当前页面:
在线文档首页 >
NetBeans API Javadoc 5.5.1
How to use certain NetBeans APIs - NetBeans API Javadoc 5.5.1
How to use certain NetBeans APIs
This page contains extracted usecases for some of the NetBeans modules
that
offer an API.
The debuggercore/api module (Debugger Core API) allows to install different debugger implementation to one IDE.
It allows to share some common UI components.
UseCase I. - Install
and use CPP debugger plug-in to NetBeans + Java Debugger.
CPP debugger plug-in installs support
for debugging of some new language to the NetBeans IDE, and some new
debugging engine. This implementation of debugger should share UI
components (actions, Debugger Views, ...) with default NB Java
Debugger. It should share basic debugger model too - notion of current
context, current session, thread, call stack line, ...
CPP debugger plug-in installs:
- New set of breakpoint types - CPPLineBreakpointType,
CPPMethodBreakpointType...
- This set of breakpoint types will have special cathegory in Add
Breakpoint Dialog called "CPP". Each breakpoint type will install a new
JPanel to Add Breakpoint Dialog.
- ToggleBreakpointAction on CPP files will create / remove a
instance of CPPLineBreakpointType.
- Install some watches evaluator for CPP language.
- Some new View to Debugger Window
- Use Termilnal Emulator in Output Window as command line interface
to CPP debugger plug-in.
- Install / uninstall a columns to / from standard Debugger Window
Views.
- Redefine Nodes used for representation of CPP threads, watches,
variables, callstacks, sessions and breakpoints
- Add / remove some properties
- Add / remove some actions
- change icons
- change display names
- Register CPP Actions for:
- Step Into, Over, Out, Continue, Pause, Start, Kill, Restart,
Finish
- Some new CPP specific actions.
UseCase II. -
Install and use JSP debugger plug-in to NetBeans + Java Debugger.
JSP debugger plug-in installs support
for debugging of some new language to the NetBeans Java Debugger. It
does not contain a new debugger engine, but it delegates to standard NB
Java debugger. So it does not depends on Debugger Core API only, but it
depends on JavaDebugger API too.
JSP debugger plug-in installs:
- New set of breakpoint types - JSPLineBreakpointType, ...
- This set of breakpoint types will have special cathegory in Add
Breakpoint Dialog called "JSP". Each breakpoint type will install a new
JPanel to Add Breakpoint Dialog.
- ToggleBreakpointAction on JSP files will create / remove a
instance of JSPLineBreakpointType.
- JSPLineBreakpointType delegates all functionality to
JPDAClassBreakpoint and JPDALineBreakpoint
- Some watches evaluator for JSP language expression. This
evaluator delegates evaluation of Java expressions to standard
JavaExpressionEvaluator.
- Redefine Nodes used for representation of JSP callstacks and
breakpoints
- Add / remove some properties
- Add / remove some actions
- change icons
- change display names
- Register JSP Actions for:
- Step Into, Over, Out
- Implementation of this actions delegates to standard Java Step
actions - it redefines Java stepping functionality.
- JSP debugger plug in adds support for new programming language
(JSP) to already running Java Session.
UseCase III. -
Install and use J2EE debugger plug-in to NetBeans + Java Debugger.
J2EE debugger plug-in installs some
enhancements to the standard Java Debugger. It
does not contain a new debugger engine or language support. So it does
not depends on Debugger Core API only, but it
depends on JavaDebugger API too.
J2EE debugger plug-in installs:
- New set of breakpoint types
- Filter for Threads and Callstack Views. This filter should allow
to:
- Add / remove / modify nodes in this views.
- Redefine Stepping (Smart Stepping) behaviour of default Java
Debugger.
- Some new View to Debugger Window
UseCase IV. -
Install and use DBX debugger plug-in to NetBeans.
DBX debugger plug-in installs support
for debugging of some new language (CPP) to the NetBeans IDE, and some
new
debugging engine. But it contains debugger engine for Java debugging
too. DBX debugger engine has its own session management (or will have
in the next versions). One debugger engine can manage more than one
sessions. One engine supports debugging in more than one language.
UseCase V. -
Implement Debugger Core UI module on top of Debugger Core API / SPI.
Debugger Core UI needs:
- List all breakpoint types and all breakpoint cathegories.
- Visually customize all breakpoints - some panel.
- Add / remove breakpoints.
- Add / remove watches.
- Represent breakpoints, threads, thread groups, watches, sessions,
call stack frames, locales, and fields as Nodes in NB Explorer View.
- List all threads, thread groups, locales, watches, breakpoints,
callstack frames, and fields.
- Listen on changes of hierarchy of threads, thread groups,
locales, watches, breakpoints, callstack frames, and fields.
- Some current context definition. Current contet should define
current session, language, thread, and call stack line.
The debuggerjpda/api (Debugger JPDA API) defines API for NetBeans Java Debugger.
UseCase I. - Install
and use CPP debugger plug-in to NetBeans + Java Debugger.
CPP debugger plug-in installs support
for debugging of some new language to the NetBeans IDE, and some new
debugging engine. This implementation of debugger should share UI
components (actions, Debugger Views, ...) with default NB Java
Debugger. It should share basic debugger model too - notion of current
context, current session, thread, call stack line, ...
CPP debugger plug-in installs:
- New set of breakpoint types - CPPLineBreakpointType,
CPPMethodBreakpointType...
- This set of breakpoint types will have special cathegory in Add
Breakpoint Dialog called "CPP". Each breakpoint type will install a new
JPanel to Add Breakpoint Dialog.
- ToggleBreakpointAction on CPP files will create / remove a
instance of CPPLineBreakpointType.
- Install some watches evaluator for CPP language.
- Some new View to Debugger Window
- Use Termilnal Emulator in Output Window as command line interface
to CPP debugger plug-in.
- Install / uninstall a columns to / from standard Debugger Window
Views.
- Redefine Nodes used for representation of CPP threads, watches,
variables, callstacks, sessions and breakpoints
- Add / remove some properties
- Add / remove some actions
- change icons
- change display names
- Register CPP Actions for:
- Step Into, Over, Out, Continue, Pause, Start, Kill, Restart,
Finish
- Some new CPP specific actions.
UseCase II. -
Install and use JSP debugger plug-in to NetBeans + Java Debugger.
JSP debugger plug-in installs support
for debugging of some new language to the NetBeans Java Debugger. It
does not contain a new debugger engine, but it delegates to standard NB
Java debugger. So it does not depends on Debugger Core API only, but it
depends on JavaDebugger API too.
JSP debugger plug-in installs:
- New set of breakpoint types - JSPLineBreakpointType, ...
- This set of breakpoint types will have special cathegory in Add
Breakpoint Dialog called "JSP". Each breakpoint type will install a new
JPanel to Add Breakpoint Dialog.
- ToggleBreakpointAction on JSP files will create / remove a
instance of JSPLineBreakpointType.
- JSPLineBreakpointType delegates all functionality to
JPDAClassBreakpoint and JPDALineBreakpoint
- Some watches evaluator for JSP language expression. This
evaluator delegates evaluation of Java expressions to standard
JavaExpressionEvaluator.
- Redefine Nodes used for representation of JSP callstacks and
breakpoints
- Add / remove some properties
- Add / remove some actions
- change icons
- change display names
- Register JSP Actions for:
- Step Into, Over, Out
- Implementation of this actions delegates to standard Java Step
actions - it redefines Java stepping functionality.
- JSP debugger plug in adds support for new programming language
(JSP) to already running Java Session.
UseCase III. -
Install and use J2EE debugger plug-in to NetBeans + Java Debugger.
J2EE debugger plug-in installs some
enhancements to the standard Java Debugger. It
does not contain a new debugger engine or language support. So it does
not depends on Debugger Core API only, but it
depends on JavaDebugger API too.
J2EE debugger plug-in installs:
- New set of breakpoint types
- Filter for Threads and Callstack Views. This filter should allow
to:
- Add / remove / modify nodes in this views.
- Redefine Stepping (Smart Stepping) behaviour of default Java
Debugger.
- Some new View to Debugger Window
UseCase IV. -
Install and use DBX debugger plug-in to NetBeans.
DBX debugger plug-in installs support
for debugging of some new language (CPP) to the NetBeans IDE, and some
new
debugging engine. But it contains debugger engine for Java debugging
too. DBX debugger engine has its own session management (or will have
in the next versions). One debugger engine can manage more than one
sessions. One engine supports debugging in more than one language.
Models basic aspects of the metadata surrounding Java source files, such as
the classpath. More information in the Javadoc.
The API is widely used by all sorts of IDE modules which need to work with
Java sources. They can obtain the classpath or boot classpath for a file (if
there is one), find out where its source root is, find sources corresponding
to bytecode class files, find all sources or classpaths corresponding to open
projects, find Javadoc, etc. The SPI is intended mainly for Java platform and
library providers, and project type providers, to declare all of this
information.
See answer to the use-cases question.
The MDR should be used for data-integration accross different modules in the IDE.
An example usecase can be found at
http://mdr.netbeans.org/example.html.
Basic usecases can also be found in the overview of individual API packages in javadoc.
The progress API is good for tracking progress of long lasting tasks in the IDE.
Basic usage
There are 3 types of progress indication:
- 1. indefinite when it's not known how much time will be needed to complete
- 2. definite with time estimate for completion (UI shows time that remains)
- 3. definite without time estimate where the UI shows percentage completed.
The default location of the progress indication is the status bar which aggregates all
tasks running in the IDE that show progress. However it's possible to exclude the task from the default location
and show the progress in one's custom dialog component. In such a case the same task should not appear in the status line component as well.
It's possible to request cancelling the task from status line progress aggregator if the task allows cancelling.
Progress tasks that get started as a result of explicit user action takes precedence in the status line
docked component over tasks that are triggered by the system. (say filesystem refresh for example)
The most common usecase of the API looks like this:
ProgressHandle handle = ProgressHandleFactory.creatHandle("My custom task");
...
// we have 100 workunits
// at this point the task appears in status bar.
handle.start(100);
...
handle.progress(10);
...
handle.progress("half way through", 50);
...
handle.progress(99);
// at this point the task is finished and removed from status bar
// it's not realy necessary to count all the way to the limit, finish can be called earlier.
// however it has to be called at the end of the processing.
handle.finish();
Advanced Usage
In case your usage of the API
- spans across multiple independent modules,
- requires adjusting of number of workunits or
- triggers additional action based on the current progress
then you should consider using the aggregating version of APIs which is similar to the
simple APIs but has distinctive differences and additions that allow for more complex scenarios.
It allows to compose the progress bar from 1+ independent sources, all sharing proportional piece
of the progress bar. Additionally you can monitor the task's overall progress from one central place and possibly
add more contributing sources of the progress during processing.
// let's have a factory for client code that performs some part of the job to be done..
Lookup.Result res = Lookup.getDefault().lookup(new LookupTemplate(MyWorkerFactory.class));
Iterator it = res.allInstances().iterator();
ProgressContributor[] contribs = new ProgressContributor[res.allInstances().size()];
int i = 0;
while (it.hasNext()) {
MyWorkerFactory prov = (MyWorkerFactory)it.next();
contribs[i] = AggregateProgressFactory.createProgressContributor("Module X contribution");
MyWorker worker = prov.createWorker(contribs[i]);
//... snip ... do something with the worker..
i = i + 1;
}
AggregateProgressHandle handle = AggregateProgressFactory.createHandle("My Task", contribs, null, null);
// non-cancellable and with out output link.
// calling start() at the time when the actual long running task starts processing
handle.start("here we go");
// ...snip...
// now the individual MyWorker instances log their progress.
// possibly in other threads too..
// ... snip...
//
if (myConditionThatSpawnsAnotherContributor()) {
ProgressContributor cont = AggregateProgressFactory.createProgressContributor("Additional exceptional contribution");
handle.addContributor(cont);
// ... snip ...
}
// the task is finished when all the ProgressContributors finish..
Code Templates allow to paste various code snippets by using parametrized text.
The parameters of the same name will share the same default value and if that value
gets changed by user's typing the new value gets replicated into all the parameter's
occurrences.
Code Templates replace the original abbreviations functionality.
Code template's example
for (Iterator ${iterator} = ${collection instanceof="java.util.Collection"}.iterator(); ${iterator}.hasNext();) {
${cursor}${iterator}.next();"
}
Each parameter can have additional hints of what values
can be assigned to it.
The hint has a form
${param hint=value}
or just
${param hint}
which translates to
${param hint="true"}
If necessary the value of the hint can be enclosed
in quotes to allow to write whitespace or { or }
into the value. The quote can be written by using \".
Reserved parameter names
${cursor} defines position where the caret will be located
after the editing of the code template default values will finish.
Reserved hint names
${param editable=false} can be used to make the parameter to be skipped
from user's editing. This may be useful e.g. with using java-specific type
hint (described below).
Java:
${ind index} defines that the default value of the parameter
should be an unused variable in the given context named i.
If i is already used then j is attempted
or then k etc. until z.
Then i0, i1 etc. are attempted.
${param type="java.util.Collection"} defines
java type that the parameter must be instance of.
Besides class names there can be array e.g. String[]
or generics java.util.List<String>
${param array} defines parameter of array type (including
arrays of primitive data types).
${param type="java.util.Iterator"} defines
that the parameter has the given java type. The template processing infrastructure
will use short name Iterator and import java.util.Iterator.
Code Template Parameters
One of the main benefits of the code templates is their parametrization
which allows to substitute the default values for the parameters
before the final insertion and it also allows the user to modify
these default values explicitly after the code template gets inserted
into the document.
The parameters are marked in the code template's text by
${...}
.
Parameters of the same name benefit from automatic replication. Once the template
gets pasted into the document all the parameter's occurrences
get replaced by parameter's default value.
The first parameter's occurrence gets selected.
The user can now replace the default value. If the user does so the new value
gets replicated to all the other occurrences of this parameter automatically.
Mime-type specific operation
Each code template needs to find the default values for its parameters
before it gets inserted into the text.
Sometimes it's enough to just specify the default value in the template's text
but usually the default value gets determined from the context of insertion.
There is an intent to create a mime-type specific code template processor
that would be registered per mime-type. There could be even more than one
such processors processing the template in a specific order.
Parameter hints
Besides parameter's name the template processors may need additional
hints of how to find a default value for the parameter.
For example java code template's parameter may be an
index
parameter which means that the infrastructure should fill in
a fresh index variable e.g.
i
.
Or the parameter can only be of a certain java type such
as in the case of iterating through a collection
the type must be subtype of
java.util.Collection
.
These requirements could be specified as additional hints
to the parameters e.g.
${i index}
or
${c instanceof=java.util.Collection}
.
The hints allow string literals to support arbitrary
explicit default values specifications
e.g.
${x default="Hello world"}
.
The '{' and '}' have no special meaning inside the string literal.
The '"' char is allowed to be used by escaping
${x default="\"quoted string\""}
.
Temporary Code Templates
The Code Completion functionality allows to build temporary
Code Templates functionality if it could build a temporary template
for completing of the method parameters. The parameters could
be completed one by one by tabbing and the Code Templates framework
would fill in proper default values just like it does for regular templates.
Insert Text Building and Updating
The parametrized text of the code template first gets parsed
and the parameters get their default values which by default are
the names of the parameters.
The code template processor are then called to update this default
value.
The new java infrastructure being developed would benefit
from the possibility to obtain the full string containing
the skeleton of the code template (without parameters)
with the present default values. It can take that string
and locally parse it to find out types of local variables
used in the particular template and fill in dependent
variable types.
Parameter Editability
Certain part of the code template may change text but it should
not be edited by the user. For example when iterating over collection
given as a parameter the collection may be generics-ed
by additional type. The iterator's variable type then also
needs to generics-ed with the same type.
The iterator's type parameter should not be editable because
this operation may be done automatically
by the java code template processor.
There should be a hint
editable
having
true
/
false
.
Code Completion provides users with a list of suggested completions for partially typed texts in the editor and various dialog input fields.
The Code Completion module was created to replace
the original legacy editor code completion which
lacked several key requirements:
Support for multiple independent code completion content providers.
Implied requirement for ordering and prioritization of the completion items.
Direct support for asynchronous completion result computation.
Missing separation to the API and SPI and implementation parts.
API
Show or hide completion window
The API is small and it only allows to explicitly show or hide the completion window.
It's being used by code templates that need to explicitly show the code completion
window when tabbing to a particular parameter.
There may be certain actions that want to ensure that the code completion is hidden
at the time when they are invoked. For example the actions pasting the content
of the completion item into the document.
SPI
Provide completion content by independent providers
Completion infrastructure needs to obtain the results that are then displayed
in the completion window.
There are three types of displayed results related to the current caret offset:
-
Code completion items
-
Documentation (e.g. the javadoc documentation)
-
Tooltip (e.g. for method parameters)
For the purpose of obtaining these completion results
CompletionProvider
exists.
There may be an arbitrary number of independent completion providers for
a single completion popup window.
The completion providers are registered through the xml layer into
Editors/<mime-type>/CompletionProviders. Once the document
with the particular mime-type gets loaded the corresponding completion providers
will get instantiated and used.
Threading:
The code completion's infrastructure invokes the requests
for the completion results in the AWT thread.
Therefore all the methods of the completion providers are invoked
in AWT thread but they may reschedule their processing into other threads.
Provide completion results computed asynchronously
The completion provider creates a task that computes the resulting
data that will then be displayed by the code completion infrastructure.
The task creation and computation are called synchronously
from the AWT event dispatch thread.
However there can be potentially long-running tasks (e.g. working with MDR)
that are not desirable to be run in AWT thread.
Therefore the completion infrastructure provides a listener
to which the completion task notifies the results.
The support class
AsyncCompletionTask allows to post the task computation
into RequestProcessor
.
Provide list of completion items fulfilling various requirements
The completion task computes a collection of completion items
which are then collected by the completion infrastructure and displayed.
Displaying. Each completion item must be able to display itself in a JList
.
Sorting. The completion items may come from different completion providers
and they must be sorted before displaying. The sort order
should not only be alphabetical but it should also allow a prioritization
of the items according to their importance in the given context.
Actions. The interaction of the user with the completion item
is done by interacting with item's input map and action map.
Documentation. The item may want to display additional
detailed information in a documentation popup window.
The Error Stripe shows an overview of important information of an edited source code.
It shows this information for the whole source code (regardless of its size).
Augment Annotations to be shown in the Error Stripe
Use the
OpenIDE Text API.
Provide Up-to-date Status for the Error Stripe
A module in the IDE has information whether data shown in the Error Stripe
is up-to-date or not. The Error Stripe may change the appearance according to this knowledge.
Implement the UpToDateStatusProvider
that provides up-to-date status. Be sure that it fires PropertyChangeEvent when this status is changed.
Implement the UpToDateStatusProviderFactory
that creates an instance of your UpToDateStatusProvider for a given JTextComponent and install it as described
here.
The Code Folding is part of the editor module functionality and it's responsible for hiding of the portions
of the code that are less important for the user at the given time.
API Use Cases
Exploring of the Folds
The code folding structure (fold hierarchy) relates
to javax.swing.JTextComponent
instance in one-to-one relationship.
To find the code folding hierarchy instance for the given non-null text component
the following code snippet can be used:
JTextComponent editorComponent = ...
FoldHierarchy hierarchy = FoldHierarchy.get(editorComponent);
Explore the Folds Hierarchy
The tree-based hierarchy has one non-removable and non-collapsable root fold
that covers the whole document. It can be obtained by
FoldHierarchy foldHierarchy = ...
Fold rootFold = hierarchy.getRootFold();
The children folds of the root fold (or children folds)
can be obtained by
// the hierarchy must be locked prior exploration or manipulation
hierarchy.lock();
try {
Fold rootFold = ...
int foldCount = rootFold.getFoldCount();
for (int i = 0; i < foldCount; i++) {
Fold childFold = rootFold.getFold(i);
}
} finally {
hierarchy.unlock();
}
Index of the child in its parent can be found by
hierarchy.lock();
try {
Fold rootFold = ...
int foldIndex = rootFold.getFoldIndex(childFold);
} finally {
hierarchy.unlock();
}
Collapse Nearest Fold
In the given fold hierarchy find the nearest fold right at or after the given offset
and collapse it.
hierarchy.lock();
try {
Fold fold = FoldUtilities.findNearestFold(hierarchy, offset);
hierarchy.collapse(fold);
} finally {
hierarchy.unlock();
}
Expand All Folds
In the given fold hierarchy expand all folds that are currently collapsed.
FoldUtilities.expand(hierarchy, null);
Collapse All Folds of Certain Type
In the given fold hierarchy collapse all e.g. javadoc folds that are currently collapsed.
The example can be generalized to any fold type.
FoldUtilities.collapse(hierarchy, JAVADOC_FOLD_TYPE);
Force Fold Expansion for Caret Moving Into Collapsed Fold
In the given fold hierarchy expand the fold into which the caret
is going to be moved by Caret.setDot(offset)
.
The hierarchy must be locked and this example assumes that the underlying
document is already read-locked e.g. by Document.render()
.
FoldHierarchy hierarchy = FoldHierarchy.get(caretComponent);
hierarchy.lock();
try {
Fold collapsed = FoldUtilities.findCollapsedFold(hierarchy, offset, offset);
if (collapsed != null && collapsed.getStartOffset() < offset &&
collapsed.getEndOffset() > offset) {
hierarchy.expand(collapsed);
}
} finally {
hierarchy.unlock();
}
Start Listening on Fold Hierarchy Changes
In the given fold hierarchy start to listen on all changes
done in the hierarchy.
This is actually used e.g. in the Editor's View Hierarchy that needs
to refresh views based on the fold changes.
hierarchy.addFoldHierarchyListener(new FoldHierarchyListener() {
public void foldHierarchyChanged(FoldHierarchyEvent evt) {
// Hierarchy does not need to be locked here
//
// evt.getAffectedStartOffset() and getAffectedEndOffset()
// give text area affected by the fold changes in the event
}
});
Inspect Collapsed Folds in Affected Area
Listen on the hierarchy changes
and refresh the views in the text area affected by the fold change.
Inspect the collapsed folds in the affected area
because special views need to be created for the collapsed folds.
The actual code in the View Hierarchy is somewhat different
but the one given here is more descriptive.
hierarchy.addFoldHierarchyListener(new FoldHierarchyListener() {
public void foldHierarchyChanged(FoldHierarchyEvent evt) {
for (Iterator collapsedFoldIterator
= FoldUtilities.collapsedFoldIterator(hierarchy,
evt.getAffectedStartOffset(),
evt.getAffectedEndOffset()
);
it.hasNext();
) {
Fold collapsedFold = (Fold)it.next();
// Create special view for the collapsedFold
}
}
});
SPI Use Cases
Create a New Fold Manager
Manipulation of the folds is designed to be done by fold managers.
Those classes implement FoldManager
interface in the SPI.
At initialization time they are given instance of FoldOperation
through which they can create, add or remove the fold instances.
To create and use a new FoldManager
instance
it's necessary to
- Define the class of the FoldManager.
public class MyFoldManager implements FoldManager { // or extends AbstractFoldManager
...
}
- Create FoldManagerFactory for the FoldManager.
public class MyFoldManager ...
...
public static final class Factory implements FoldManagerFactory {
public FoldManager createFoldManager() {
return new MyFoldManager();
}
}
}
- Register FoldManagerFactory into xml layer into the directory
"Editors/<mime-type>/FoldManager/"
- Enable Code Folding in editor's Settings initializer
(please see e.g.
NbJavaSettingsInitializer
)
public class MySettingsInitializer ...
public void updateSettingsMap(Class kitClass, Map settingsMap) {
...
settingsMap.put(SettingsNames.CODE_FOLDING_ENABLE, Boolean.TRUE);
}
}
Create a New Fold by Fold Manager
Create a new fold and add it to the hierarchy. The operation
is performed by the fold manager either at initialization phase
(in the initFolds()
which gets called automatically
by the infrastructure) or at any other time when the fold manager's
operation gets invoked (usually by a listener that the fold manager
attaches to be notified about changes that can cause the folds structure
to be changed - e.g. a parsing listener for java folds).
Operations that manipulate the hierarchy are done
in terms of a valid transaction over the fold hierarchy.
Transactions allow to fire the collected changes as a single
FoldHierarchyEvent
at the time when they are committed.
// In the FoldManager's context
FoldOperation operation = getOperation();
FoldHierarchyTransaction transaction = operation.openTransaction();
try {
Fold fold = operation.createFold(...);
operation.addFoldToHierarchy(fold, transaction);
} finally {
transaction.commit();
}
Remove Fold from Hierarchy by Fold Manager
Remove the existing fold from the hierarchy
// In the FoldManager's context
FoldOperation operation = getOperation();
FoldHierarchyTransaction transaction = operation.openTransaction();
try {
Fold fold = ...
operation.removeFoldFromHierarchy(fold, transaction);
} finally {
transaction.commit();
}
Each editor provides an EditorKit which controls the policy of specific MIME content type.
The policy of content type should be easily registered and found via some lookup mechanism,
that will provide convenient way of using it either for kit provider or base
editor infrastructure. In addition to this, the policy can be inherited, (i.e. in case of embeded
kits like JSP) and the content types need to be merged in this case. MIME Lookup API should
provide all mentioned requierements via easy lookup query, so content type policy
user need not to solve this searching and merging on its own side.
Per mime-type operation
Operation of the editor module must be parametrized by the type of the file
being edited. In the past the operation was parametrized by the class
of the editor kit but that did not show up as being useful enough.
It is more practical to use a string-based parametrization concretely
the mime-type. Anyone can then easily register an additional functionality
for the editor because it's just enough to know the right mime-type and the type
of the functionality class to be implemented and the xml layer folder
where the class should be registered.
For example the editor/fold module expects to find the registered
fold manager factories (
org.netbeans.spi.editor.fold.FoldManagerFactory
classes)
in the
Editors/<mime-type>/FoldManager layer folder.
The editor/completion module expects to find the registered
completion providers (
org.netbeans.spi.editor.completion.CompletionProvider
classes)
in the
Editors/<mime-type>/CompletionProviders layer folder.
Provide list of instances as lookup result
On the modules' implementation side the registered functionality
must be retrieved somehow. It's necessary to instantiate the registered objects
and react to module enablding/disabling which can affect validity of the registered objects.
As the most convenient solution appears to use
org.openide.util.Lookup
allowing to provide
the registered instances as a
Lookup.Result
allowing to listen for changes (e.g. caused by the module enabling/disabling).
This resulted into creation of
class MimeLookup extends Lookup
containing
static MimeLookup getMimeLookup(String mimeType)
.
Nested mime-types
On the lexical level the document can contain nested languages.
For example JSP document can contain pieces of java code which can further contain
javadoc comment tokens with nested javadoc language.
The nested languages should allow for special settings
such as fonts and colors of nested syntax coloring but even
things like actions that would be active in the nested document section.
This resulted into creation of
MimeLookup childLookup(String mimeType)
method in
MimeLookup
.
API Use Cases
Find class instances for the given mime-type
An API method
MimeLookup lookup = MimeLookup.getMimeLookup("text/x-java");
can be used for getting the mime specific lookup. Having this we can lookup class
or template:
Object obj = lookup.lookup(LookedUpClass.class);
or
Lookup.Result result = lookup.lookup(new Lookup.Template(LookedUpClass.class));
Getting mime-type specific child (embeded) MimeLookup
Child mime content can be embeded into parent
mime content in various embeded languages. In this case mime lookup child is
specified as subelement of parent lookup i.e.: MimeLookup("text/x-jsp") can have
a child MimeLookup("text/x-java") in a case of a jsp scriplet.
MimeLookup lookup = MimeLookup.getMimeLookup("text/x-jsp").childLookup("text/x-java");
SPI Use Cases
Declare the mime specific object via xml layer
This is the simpliest way of declaring, it is suitable for objects stored directly in mime specific
folders:
<filesystem>
<folder name="Editors">
<folder name="text">
<folder name="x-java">
<file name="org-netbeans-modules-editor-mimelookuptest-MimeSpecificObject.instance"/>
</folder>
</folder>
</folder>
</filesystem>
Lookup of this object will look like:
MimeLookup lookup = MimeLookup.getMimeLookup("text/x-java");
MimeSpecificObject mso = (MimeSpecificObject) lookup.lookup(MimeSpecificObject.class);
Declare the object via xml layer in some specific folder using Class2LayerFolder.
Some objects are connected to some specific folder in mime specific layer folders, i.e.
FoldManagerFactory.class is placed in
FoldManager
subfolder in the
Editors/<mime-type>/.
Such objects can be registered
to the specific folder using interface
Class2LayerFolder
.
Let's register FoldManagerFactory.class to
FoldManager folder.
First we need to implement the interface:
public class FoldManagerClass2LayerFolder implements Class2LayerFolder{
public FoldManagerClass2LayerFolder {
}
/* declaring the class */
public Class getClazz(){
return FoldManagerFactory.class;
}
/* assigning the declared class to folder */
public String getLayerFolderName(){
return "FoldManager";
}
/* we will not support InstanceProvider */
public org.netbeans.spi.editor.mimelookup.InstanceProvider getInstanceProvider() {
return null;
}
}
Then we need to register it to default lookup via META-INF/services registration. We need to create
a folder structure
META-INF/services
and place there a file
org.netbeans.spi.editor.mimelookup.Class2LayerFolder
with the content
FoldManagerClass2LayerFolder
having this we can register appropriate object to specific folder:
<filesystem>
<folder name="Editors">
<folder name="text">
<folder name="x-java">
<folder name="FoldManager">
<file name="org-netbeans-modules-editor-MyFoldManagerFactory.instance"/>
</folder>
</folder>
</folder>
</folder>
</filesystem>
Lookup of this object will look like:
MimeLookup lookup = MimeLookup.getMimeLookup("text/x-java");
FoldManagerFactory foldManagerFactory = (FoldManagerFactory) lookup.lookup(FoldManagerFactory.class);
or, if there should be more instances of the FoldManagerFactory:
MimeLookup lookup = MimeLookup.getMimeLookup("text/x-java");
Collection foldManagerFactories = lookup.lookup(new Lookup.Template(FoldManagerFactory.class)).allInstances();
Notice, that the FoldManagerFactory object is found in "FoldManager" folder. It is not necessary for client
of the API to know about some folder structure.
Providing implemented MimeLookupInitializer
It is the general way of adding mime specific object into the
MimeLookup
. Implementation of
MimeLookupInitializer
should be created and
registered to default lookup via
META-INF/services
registration.
For details, please look at the simplified
TestMimeLookupInitializer
in
mimelookup/test/unit
or
LayerMimeLookupInitializer
.
Using InstanceProvider for declaration of compound folder objects and inheritance
Demonstration of
InstanceProvider
and inheritance will be used together in one use case. Example of
editor context menu construction will be used. Each module can register its actions to
editor context menu via xml layer. As context menu is mime type sensitive (java editor has
different menu items than plain editor) the actions are registered into mime specific layer folders
to subfolder "Popup". When the context menu is constructing, the xml layer is scanned for the
action items located in the "Popup" subfolder of specific mime folder. In addition to
this there is inheritance mechanism used to share global actions (like Cut, Copy, Paste) over all mime
types context menus, thus not only the action items from actual mime type are considered. All
underlaying mime types are scanned also and the result is context menu with merged action items.
For example JSP scriplet context menu should be merged from action items gathered over:
- Editors/Popup - this is base level for global actions like Cut, Copy, Paste
- Editors/text/x-jsp/Popup - items from jsp mime type
- Editors/text/x-jsp/text/x-java/Popup - items specific to java scriplet in JSP document
For now, this construction mechanism is implemented in editor module in NbEditorKit class. The code
responsible for construction is quite large, and it is not very generic,
now it supports only base level + mime specific level merging. Embeded languages like example in JSP scriplet
are not supported.
MimeLookup
will solve also this embeding and the gathering of the context menu items will
looks such simply as:
MimeLookup lookup = MimeLookup.getMimeLookup("text/x-jsp").childLookup("text/x-java");
PopupActions actions = (PopupActions) lookup.lookup(PopupActions.class);
List popupActions = actions.getPopupActions();
where PopupActions is implementation of
InstanceProvider
and PopupActions.class needs to be
registered to "Popup" subfolder using
Class2LayerFolder
implementation. Let's register
this step by step.
Because action items are instances of various objects like:
- javax.swing.Action
- javax.swing.JSeparator
- org.openide.util.actions.SystemAction
- java.lang.String
the
InstanceProvider
needs to be created for this:
public class PopupActions implements InstanceProvider{
List ordered;
public PopupActions(){
}
public PopupActions(List ordered){
this.ordered = ordered;
}
public List getPopupActions(){
List retList = new ArrayList();
for (int i = 0; i<ordered.size(); i++){
DataObject dob = (DataObject) ordered.get(i);
InstanceCookie ic = (InstanceCookie)dob.getCookie(InstanceCookie.class);
if (ic!=null){
try{
if (String.class.isAssignableFrom(ic.instanceClass()) ||
Action.class.isAssignableFrom(ic.instanceClass()) ||
SystemAction.class.isAssignableFrom(ic.instanceClass()) ||
JSeparator.class.isAssignableFrom(ic.instanceClass())){
Object instance = ic.instanceCreate();
retList.add(instance);
}
}catch(IOException ioe){
ioe.printStackTrace();
}catch(ClassNotFoundException cnfe){
cnfe.printStackTrace();
}
} else{
retList.add(dob.getName());
}
}
return retList;
}
public Object createInstance(List ordered) {
return new PopupActions(ordered);
}
}
This
InstanceProvider
needs to be declared in
Class2LayerFolder
implementation:
public class PopupInitializer implements Class2LayerFolder{
public PopupInitializer() {
}
public Class getClazz(){
return PopupActions.class;
}
public String getLayerFolderName(){
return "Popup"; //NOI18N
}
public InstanceProvider getInstanceProvider() {
return new PopupActions();
}
}
Now, we just need to register PopupInitializer into default lookup via META-INF/services
registration and the initialization is done.
Now editor settings in editor module are maintained by complicated structures and their
initialization processes. It is because of historical reasons and the presence of standalone editor,
where standalone settings layer is supported via openide dependent editor module layer. Each editor
that provides its own settings needs to depend on the whole editor module.
The main purpose of this project is to create editor/settings API, that will contain settings classes,
which will be lookup-able via mimelookup. The aim is NOT to provide physical implementation of
editor settings storage. The module will be just interface between the settings storage and
the settings clients like <mime-type> editors, externaleditor, etc.
Per mime-type operation
The particular mime-type settings can be found for example using the following
mimelookup search:
FontColorSettings fcs = (FontColorSettings) MimeLookup.getMimeLookup("text/x-java").lookup(FontColorSettings.class);
AttributeSet fontColors = fcs.getFontColors(FontColorNames.SELECTION_COLORING);
This will resolve Fonts ant Colors settings of the coloring used
for selection for the mime-type "text/x-java" in AttributeSet representation.
Listening on settings change
Settings clients often need to be able to listen on the settings change and handle this
event. This can be done using mimelookup via
Lookup.Template
by registering LookupListener
on the returned Lookup.Result.
LookupResult fontsColors = MimeLookup.getMimeLookup("text/x-java").lookup(
new Lookup.Template(FontColorSettings.class));
if (fontsColors !=null) {
fontsColors.addLookupListener(new LookupListener(){
public void resultChanged(LookupEvent ev) {
Lookup.Result result = ((Lookup.Result)ev.getSource());
//... settings client response on settings change
}
});
}
FontColorSettings implementor is responsible of creation of new instance of FontColorSettings
if some setting will change in the lookup, it provides.
Many Java-based project types need to be able to configure the version and
location of Java to be used when building and running the project. This
API/SPI permits these platforms to be registered and queried, and any
customizations made in an appropriate GUI and persisted to disk.
The API can be used by any code wishing to know the list of installed
platforms and information about each one; typically this would be used by
project type providers to implement a customizer dialog. The SPI is intended
to be implemented by a few modules supply support for locating and
introspecting installed platforms, for example a JDK setup wizard.
Provides support infrastructure for projects working with the Java language.
Project type providers wishing to show Java packages in their logical views
can use this SPI. Templates which are Java-centric can use it. Projects which
wish to implement queries from the Java Support APIs can place implementations
in their lookup and these will be delegated to automatically.
Provides the basic infrastructure by which Ant-based projects can be created,
read and write configuration parameters and properties from/to disk, satisfy
common queries and interfaces, etc. See Javadoc and build system design
document.
Mostly an SPI for use by project type providers to create the project type.
Also includes a small API/SPI for other projects to find what Ant build steps
are necessary to create a certain build product, for use in inter-project
dependencies.
Ant project support faq:
How to use support for storing project properties?
Q:
I'm creating a customizer (properties dialog) for my project type. I wan't to use the support
for simple data types. What do I need to do?
You basicaly need to do two things. First create the representation of the project properties which
can be used in the GUI. Second at some time convert the objects back to the ANT properties form and
store them into the project.
-
Creating the object representation.
- Create new instance of StoreGroup for each group of properties you want to store later
e.g. project and prvate. Sometimes it might be useful to create temporary source group
which will only be used for creating the models without being used for storing. E.g.
for properties which need special handling.
- Call the factory methods e.g. createBooleanButtonModel, createStringDocument, etc. which
will create the swing models for you.
- Use the models in your Swing controls by calling setModel() or setDocument()
-
Storing the models back to the proprties of project.
- Get the EditableProperties you want to store the model in e.g. private or project
properties
- Call the store method on given SourceGroup with the EditableProperties as parameter
- Manually store models which need some special handling.
Permits libraries to be defined, customized, and stored by the user for
reuse in multiple projects. For example, a Java JAR library has a classpath
(usually one JAR), and an optional source path and Javadoc path that may be
used for development-time features.
Different technology support modules will supply definitions of different
kinds of libraries, e.g. Java JARs, that may be reused in user projects.
Modules may register library predefinitions to wrap libraries they bundle.
Project type providers can refer to available libraries in customizer dialogs.
Provides a generic infrastructure for modelling projects.
Documentation available in the Javadoc. The build system design overview
describes the basic purpose of modelling projects.
The SPI should be used by modules defining particular project types, e.g. the
J2SE project type. The API is to be used primarily by GUI infrastructure and
some queries, though other module code may on occasion need to refer to the
API.
The module supplies the APIs for the basic, generic UI infrastructure for
projects: list of opened projects, main project, basic project-sensitive
actions, template wizards, etc.
The main use case is for project type providers to supply logical views and
customizers for the project. Also for template providers to create
project-aware file templates. Can also get a list of open projects, create
different kinds of project-related actions, and select projects on disk.
General kinds of queries between modules.
Queries are one way of solving the
intermodule communication problem when it is necessary for some modules to
obtain basic information about the system (e.g. whether a particular file is
intended for version control) without needing direct dependencies on the
module providing the answer (e.g. the project type which controls the file).
Details are covered in the Javadoc.
Particular use cases are enumerated in the Javadoc for each query API. Usage
consists of simple static method calls. Potentially a wide variety of modules
could use these queries; implementations are typically registered by project
type providers, though also by Java library and platform implementations.
The debuggercore module (Debugger Core UI) contains shared UI components for all debugger implementations, and defines some SPI
for sharing of them.
UseCase I. - Install
and use CPP debugger plug-in to NetBeans + Java Debugger.
CPP debugger plug-in installs support
for debugging of some new language to the NetBeans IDE, and some new
debugging engine. This implementation of debugger should share UI
components (actions, Debugger Views, ...) with default NB Java
Debugger. It should share basic debugger model too - notion of current
context, current session, thread, call stack line, ...
CPP debugger plug-in installs:
- New set of breakpoint types - CPPLineBreakpointType,
CPPMethodBreakpointType...
- This set of breakpoint types will have special cathegory in Add
Breakpoint Dialog called "CPP". Each breakpoint type will install a new
JPanel to Add Breakpoint Dialog.
- ToggleBreakpointAction on CPP files will create / remove a
instance of CPPLineBreakpointType.
- Install some watches evaluator for CPP language.
- Some new View to Debugger Window
- Use Termilnal Emulator in Output Window as command line interface
to CPP debugger plug-in.
- Install / uninstall a columns to / from standard Debugger Window
Views.
- Redefine Nodes used for representation of CPP threads, watches,
variables, callstacks, sessions and breakpoints
- Add / remove some properties
- Add / remove some actions
- change icons
- change display names
- Register CPP Actions for:
- Step Into, Over, Out, Continue, Pause, Start, Kill, Restart,
Finish
- Some new CPP specific actions.
UseCase II. -
Install and use JSP debugger plug-in to NetBeans + Java Debugger.
JSP debugger plug-in installs support
for debugging of some new language to the NetBeans Java Debugger. It
does not contain a new debugger engine, but it delegates to standard NB
Java debugger. So it does not depends on Debugger Core API only, but it
depends on JavaDebugger API too.
JSP debugger plug-in installs:
- New set of breakpoint types - JSPLineBreakpointType, ...
- This set of breakpoint types will have special cathegory in Add
Breakpoint Dialog called "JSP". Each breakpoint type will install a new
JPanel to Add Breakpoint Dialog.
- ToggleBreakpointAction on JSP files will create / remove a
instance of JSPLineBreakpointType.
- JSPLineBreakpointType delegates all functionality to
JPDAClassBreakpoint and JPDALineBreakpoint
- Some watches evaluator for JSP language expression. This
evaluator delegates evaluation of Java expressions to standard
JavaExpressionEvaluator.
- Redefine Nodes used for representation of JSP callstacks and
breakpoints
- Add / remove some properties
- Add / remove some actions
- change icons
- change display names
- Register JSP Actions for:
- Step Into, Over, Out
- Implementation of this actions delegates to standard Java Step
actions - it redefines Java stepping functionality.
- JSP debugger plug in adds support for new programming language
(JSP) to already running Java Session.
UseCase III. -
Install and use J2EE debugger plug-in to NetBeans + Java Debugger.
J2EE debugger plug-in installs some
enhancements to the standard Java Debugger. It
does not contain a new debugger engine or language support. So it does
not depends on Debugger Core API only, but it
depends on JavaDebugger API too.
J2EE debugger plug-in installs:
- New set of breakpoint types
- Filter for Threads and Callstack Views. This filter should allow
to:
- Add / remove / modify nodes in this views.
- Redefine Stepping (Smart Stepping) behaviour of default Java
Debugger.
- Some new View to Debugger Window
UseCase IV. -
Install and use DBX debugger plug-in to NetBeans.
DBX debugger plug-in installs support
for debugging of some new language (CPP) to the NetBeans IDE, and some
new
debugging engine. But it contains debugger engine for Java debugging
too. DBX debugger engine has its own session management (or will have
in the next versions). One debugger engine can manage more than one
sessions. One engine supports debugging in more than one language.
UseCase V. -
Implement Debugger Core UI module on top of Debugger Core API / SPI.
Debugger Core UI needs:
- List all breakpoint types and all breakpoint cathegories.
- Visually customize all breakpoints - some panel.
- Add / remove breakpoints.
- Add / remove watches.
- Represent breakpoints, threads, thread groups, watches, sessions,
call stack frames, locales, and fields as Nodes in NB Explorer View.
- List all threads, thread groups, locales, watches, breakpoints,
callstack frames, and fields.
- Listen on changes of hierarchy of threads, thread groups,
locales, watches, breakpoints, callstack frames, and fields.
- Some current context definition. Current contet should define
current session, language, thread, and call stack line.
Navigator module is a base API module which provides:
A place for modules to show structure/outline of their documents
Ability for modules to show their view only when special document(node)
is active in the system
UI for switching between multiple views available for currently active document(node)
Coalescing of fast coming selected node changes to show content for
Basic Usage Steps
In order to plug in a view into Navigator UI for certain document (data) type,
module writers need to complete following steps:
-
Write NavigatorPanel
implementation
-
Register implementation class in xml layer
Writing NavigatorPanel implementation
Implementing NavigatorPanel
interface is easy, you can copy from template basic implementation
BasicNavPanelImpl.java.
Advices on important part of panel implementation:
-
Instantiation: Your implementation of NavigatorPanel
is instantied automatically by the system if you register it in a layer
(described in next step). See detailed
Instantiation rules
to understand which kind of instantiation possibilities are on the plate.
-
getComponent method: Simply create and return your UI
representation of your data in Swing's JComponent envelope. Just be sure
that you don't create new JComponent subclass per every call, as
performance will suffer then.
-
panelActivated and panelDeactivated methods wraps an
'active' life of your panel implementation. In panelActivated, grab
your data from given Lookup,
usually by looking up its asociated
DataObject
or
FileObject
to take data from. Also remember to attach listeners to lookup result,
perhaps also to data itself and trigger UI update with new data.
Code will typically look like this:
/** JavaDataObject used as example, replace with your own data source */
private static final Lookup.Template MY_DATA = new Lookup.Template(JavaDataObject.class);
public void panelActivated (Lookup context) {
// lookup context and listen to result to get notified about context changes
curResult = context.lookup(MY_DATA);
curResult.addLookupListener(/** your LookupListener impl here*/);
Collection data = curResult.allInstances();
// ... compute view from data and trigger repaint
}
Do *not* perform any long computation in panelActivated directly, see below.
In panelDeactivated, be sure to remove all listeners to context given
to you in panelActivated.
-
Long computation of content: What if rendering your
Navigator view takes long time, more than several milliseconds?
Right approach is to create and run new task using
RequestProcessor techniques,
each time when panelActivated call arrived or your listeners on
data context got called.
While computing, UI of Navigator view
should show some please wait message.
Registering NavigatorPanel impl in a layer
Declarative registration of your NavigatorPanel impl connects this
implementation with specific content type, which is type of
the document, expressed in mime-type syntax, for example 'text/x-java'
for java sources. Infrastructure will automatically load and show
your NavigatorPanel impl in UI, when currently activated Node
is backed by primary FileObject whose
FileObject.getMimeType()
equals to content type specified in your layer.
Writing layer registration itself is easy, you can again copy from template layer
Basic Navigator Registration Layer.
Additional important info:
- System looks up for navigator providers only in
Navigator/Panels folder, nowhere else.
- Content type is specified as cascade of subdirectories under
Navigator/Panels directory. Mime-type syntax is recommended and
handy for specifying content type.
It is not compulsory though, so if you need to make up a name
for your content type, just go for it. (more below)
Advanced Content Registration - Linking to Node's Lookup
There may be situations where linking between your Navigator view and
activated Node's primary FileObject is not enough or not possible at all.
This simply happens when the data you want to represent in Navigator are
not accessible through primary FileObject or DataObject. Usual example is
Multiview environment, where more views of one document exists.
The solution is to bind content of your Navigator view directly to your
TopComponent. Then, whenever your TopComponent gets activated in the system, Navigator UI
will show th content you connected to it.
Steps to do:
The project implements a new component palette that will be reused by other
projects. The new palette should provide a common look and feel for Form editor,
Mobility, J2EE and possible other modules as well.
UI draft specification is available at http://ui.netbeans.org/docs/ui/palette/index.html
Basic usage
The following steps must be taken if an editor module wants to display a palette of
items that can be dropped to editor window:
- Define palette's root folder in editor's layer and also define subfolders for
categories and file objects for palette items.
Another option is to create a hierarchy
of Nodes (root - categories - items).
- Extend PaletteActions class that provides custom Actions for palette's popup menus.
- Use PaletteFactory to create an instance of PaletteController. The editor module keeps
a reference to this object and registers a PropertyChangeListner to it to be notified
of palette's selection changes.
- Add the instance of PaletteController to the lookup of editor's TopComponent.
When an item is selected in the palette and user clicks into the editor window
then the module can ask for selected item by calling PaletteController.getSelectedItem().
This method returns a Lookup that holds object(s) representing the selected item.
After the item is inserted into the editor window the module may clear palette's selection
(ProgressController.clearSelection()) or leave the item selected to implement 'multi drop'
insertion scenario.
Filtering
It is possible to filter palette content and hide some categories and/or
items from the user by implementing PaletteFilter interface.
Calling PaletteController.setPaletteFilter() updates palette content and
repaints the palette window.
Default Settings
The initial state of the palette can be overridden by setting appropriate
attributes to palette model. The list of supported attributes is defined
in PaletteController class. If the palette model is create from Nodes then
the attributes are extracted by calling Node.getValue() method on the root
Node and category and item nodes. If the palette model is defined as folders
and files in the layer then the attributes are extracted by calling
FileObject.getAttribute().
Writing Item
The following steps must be taken when writing the item using the support provided by this module:
-
Create XML file with item definition according to
the editor-palette-item-1_0.dtd.
-
Register it in the editor's layer file (see Basic usage).
-
Provide custom item implementation of the ActiveEditorDrop interface if needed. I must be
referenced from the definition file.
The debuggercore/ViewModel
module (View Model) allows to share one TreeTableView among different modules.
Used by debugger to display various information - threads, call stack, variables, etc.
Actions provides system of support and utility classes
for 'actions' usage in NetBeans.
First see the
API description. Here is just
a list of frequently asked or interesting questions slowly expanding as
people ask them:
Actions faq:
How to define configurable Shortcut for Component based shortcut?
Q:
The usual Swing way of defining Actions for your component is to create an Action instance and put it into the Input and Action maps of your component.
However how to make this Action's shortcut configurable from the Tools/Keyboard Shortcuts dialog?
In order for the action to show up in Keyboards Shortcut dialog you need the action defined in the
layer file under "Actions" folder and have the shortcut defined there under "Keymaps/<Profile Name>" linking to your action.
<folder name="Actions" >
<folder name="Window">
<file name="org-netbeans-core-actions-PreviousViewCallbackAction.instance"/>
</folder>
</folder>
<folder name="Keymaps">
<folder name="NetBeans">
<file name="S-A-Left.shadow">
<attr name="originalFile" stringvalue="Actions/Window/org-netbeans-core-actions-PreviousViewCallbackAction.instance"/>
</file>
</folder>
</folder>
The mentioned Action has to be a subclass of org.openide.util.actions.CallbackSystemAction
. It does not necessarily has to
perform the action, it's just a placeholder for linking the shortcut. You might want to override it's getActionMapKey()
and give it a
reasonable key.
The actual action that does the work in your component (preferably a simple Swing javax.swing.Action
)
is to be put into your TopComponent
's ActionMap
. The key for the ActionMap
has to match the key defined in the global action's getActionMapKey()
method.
getActionMap().put("PreviousViewAction", new MyPreviousTabAction());
This way even actions from multiple TopComponent
s with the same gesture (eg. "switch to next tab") can share the same configurable shortcut.
Note: Don't define your action's shortcut and don't put it into any of the TopComponent
's
javax.swing.InputMap
. Otherwise the component would not pick up the changed shortcut from the
global context.
The org.openide.awt
provides API/SPI for UI related aspects of application.
XXX no answer for arch-usecases
The
DialogsAPI
allows creating a user notification, a dialog's description
and also permits it to be displayed. The wizard framework allows create a sequence
of panels which leads a user through the steps to complete any task.
This API is part of package org.openide.
There is a
Wizard Guide Book
providing the introductionary information, moreover here is a list
of frequently asked questions and their answers:
How to change the title of a wizard?
Q: Although none of my panels have names set (using setName() method) and the method name() in the WizardDescriptor.Iterator
returns an empty string, I'm getting "wizard ( )" as the title of each panel in my wizard.
When I set the name of the panel and return a string from the method name() I get: "panelName wizard (myName)".
The wizard steps are labeled correctly, it just the panel title/name that looks like it adds "wizard ()" to
any of my panels. I don't mind the "( )", but I would like to rid of the word "wizard".
A: You can change the format of your wizard's title by
WizardDescriptor.setTitleFormat(MessageFormat format)
and rid of 'wizard' word in the default wizard's title.
The Input/Output API is a small API module
which contains InputOutput and related interfaces used in
driving the Output Window. The normal implementation is org.netbeans.core.output2.
There is an SPI but additional implementations are not expected. The API is most important.
Simple usage example:
InputOutput io = IOProvider.getDefault().getIO("My Window", true);
io.select();
OutputWriter w = io.getOut();
w.println("Line of plain text.");
OutputListener listener = new OutputListener() {
public void outputLineAction(OutputEvent ev) {
StatusDisplayer.getDefault().setStatusText("Hyperlink clicked!");
}
public void outputLineSelected(OutputEvent ev) {
}
public void outputLineCleared(OutputEvent ev) {
}
};
w.println("Line of hyperlinked text.", listener, true);
In summary, the LoadersAPI
is responsible for scanning files in a directory on disk,
weeding out irrelevant files of no interest to the IDE,
and grouping the rest into logical chunks, or just determining
what type of data each represents. It does this scanning by asking each registered
data loader whether or not the given file(s) should be handled. The first
loader to recognize a file takes ownership of it, and creates a matching data object to represent it to the rest of the IDE.
A lot of usecases is described
in the javadoc. Here
is the list of some faqs:
How to add action to folder's popup menu?
The actions that the default folder loader shows in its popup menu are read from
a layer folder Loaders/folder/any/Actions
so if any module wishes
to extend, hide or reorder some of them it can just register its actions there.
As code like this does:
<folder name="Loaders" >
<folder name="folder" >
<folder name="any" >
<folder name="Actions" >
<file name="org-mymodule-MyAction.instance" >
<attr name="instanceCreate" stringvalue="org.mymodule.MyAction" />
</file>
</folder>
</folder>
</folder>
</folder>
As described in general
actions registration tutorial.
This functionality is available since version 5.0 of the loaders module. Please use
OpenIDE-Module-Module-Dependencies: org.openide.loaders > 5.0
in your
module dependencies.
In version 5.8 all the standard loaders were changed to read actions
from layer:
-
The actions that the standard XML loader shows in its popup menu are read from
a layer folder
Loaders/text/xml/Actions
-
The actions that the loader for unrecognized files shows in its popup menu are read from
a layer folder
Loaders/content/unknown/Actions
-
The actions that the loader for instance and settings files shows in its popup menu are read from
a layer folder
Loaders/application/x-nbsettings/Actions
How to allow others to enhance actions of your loader?
If you want other modules to enhance or modify actions that are visible on
DataObject
s produced by your
DataLoader
and you
are either using
DataNode
or its subclass, you can just override
protected String actionsContext()
method to return non-null
location of context in layers from where to read the actions.
The usual value should match
Loaders/mime/type/Actions
scheme,
for example java is using
Loaders/text/x-java/Actions
, but
the name can be arbitrary.
This functionality is available since version 5.0 of the loaders module. Please use
OpenIDE-Module-Module-Dependencies: org.openide.loaders > 5.0
in your
module dependencies.
Described in the overall answer.
There is a great introduction to Lookup and its usage in its
javadoc. Here is just
a list of frequently asked or interesting questions slowly expanding as
people ask them:
Lookup faq:
How to specify that a service in Lookup should be available only on Windows?
Q:
Most of the time I specify interfaces that I want to add to the Lookup class in the layer.xml file.
But, let's say I have a platform-specific interface (something on Windows only, for instance).
How can I specify (in the xml, or programmatically) that this service should only be added to the Lookup if the platform is Windows?
>
In general there are three ways to achieve this.
-
It is possible to write a specific module and enable it only on windows.
See os specific modules documentation.
Then you can put a registration of your instance into your module's
META-INF/services directory and it
will be available only on Windows.
-
Another possibility that does not require new module, but which executes
a code on startup (which may have performance implications) is to use methodvalue
attribute. Register your instance in layer using your-Object.instance
file
as described at
services
documentation and in your factory method either return the instance
your want or null
depending on result of
Utilities.isWindows() call.
-
In some cases, the interface for which you will register an implementation permits a
no-operation semantics. For example, InstalledFileLocator.locate(...)
can
return a valid File
, or null. You could always register an
InstalledFileLocator
instance yet disable it on non-Windows platforms
(always returning null).
How shall I write an extension point for my module?
Q:
I have more modules one of them providing the core functionality and
few more that wish to extend it. What is the right way to do it?
How does the Netbeans platform declare such extension point?
Start with declaring an extension interface in your
core module and put it into the module's public packages. Imagine
for example that the core module is in JAR file org-my-netbeans-coremodule.jar
and already contains in manifests line like
OpenIDE-Module: org.my.netbeans.coremodule/1
and wants
to display various tips of the day provided by other modules and thus defines:
package org.my.netbeans.coremodule;
public interface TipsOfTheDayProvider {
public String provideTipOfTheDay ();
}
And in its manifest adds line
OpenIDE-Module-Public-Packages: org.my.netbeans.coremodule.*
to specify that this package contains exported API and shall be
accessible to other modules.
When the core module is about to display the tip of the day it can ask
the system for all registered instances of the TipsOfTheDayProvider
,
randomly select one of them:
import java.util.Collection;
import java.util.Collections;
import org.openide.util.Lookup;
Lookup.Result result = Lookup.getDefault ().lookup (new Lookup.Template (TipsOfTheDayProvider.class));
Collection c = result.allInstances ();
Collections.shuffle (c);
TipsOfTheDayProvider selected = (TipsOfTheDayProvider)c.iterator ().next ();
and then display the tip. Simple, trivial, just by the usage of
Lookup interface once
creates a registry that other modules can enhance. But such enhancing
of course requires work on the other side. Each module that would like
to register its TipsOfTheDayProvider
needs to depend on the
core module - add
OpenIDE-Module-Module-Dependencies: org.my.netbeans.coremodule/1
into its manifest and write a class with its own implementation of the
provider:
package org.my.netbeans.extramodule;
class ExtraTip implements TipsOfTheDayProvider {
public String provideTipOfTheDay () {
return "Do you know that in order to write extension point you should use Lookup?";
}
}
Then, the only necessary thing is to register such class by using the
J2SE standard into plain text file
META-INF/services/org.my.netbeans.coremodule.TipsOfTheDayProvider
in the module JAR containing just one line:
org.my.netbeans.extramodule.ExtraTip
and your modules are now ready to communicate
using your own extension point.
Window System API is used to display and control application GUI: Main window,
frames, components.
General
Use cases
can be read on the
external page.
Here is a small howto for simple things that may
be found useful:
How to create a '.settings' file for a TopComponent?
Either write it by hand (not that hard if you copy other file and
tweak it to match your TC), or start the IDE, instantiate the TC
somehow (You have a "Window->Show My TC", right? ),
copy the file that gets created in $userdir/config/Windows2Local/Component
and cleanup the serialdata section - replace it with proper "<instance class='..." /> tag.
How to make a TopComponentGroup?
Q: I'm trying to make a TopComponentGroup. I've just read http://ui.netbeans.org/docs/ui/ws/ws_spec.html#3.9
I want to make a group that uses the first invocation strategy.
That is, I want the group to open/close when I activate a certain subclass of TopComponent.
Say, for example, I have a FooTopComponent, and when it's active,
I want to open a FooPropertySheetComponent, docked in a mode on the right-hand side.
I know I have to:
- declare the group in the layer file (Windows2/Groups)
- have code for opening the group
- have code for closing the group
I think I do #2 in FooTopComponent.componentActivated() and #3 in
FooTopComponent.componentDeactivated(). Is that right?
A:Yes it is correct way. You can check
simple test module.
First you must get TopComponentGroup instance using find method then call TopComponentGroup.open()/close().
Here is the code in your componentDeactivated method:
protected void componentDeactivated ()
{
// close window group containing propsheet, but only if we're
// selecting a different kind of TC in the same mode
boolean closeGroup = true;
Mode curMode = WindowManager.getDefault().findMode(this);
TopComponent selected = curMode.getSelectedTopComponent();
if (selected != null && selected instanceof FooTopComponent)
closeGroup = false;
if (closeGroup)
{
TopComponentGroup group = WindowManager.getDefault().findTopComponentGroup(TC_GROUP);
if (group != null)
{
group.close();
}
}
}