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: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.
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);
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(); }
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(); }
In the given fold hierarchy expand all folds that are currently collapsed.
FoldUtilities.expand(hierarchy, null);
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);
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(); }
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 } });
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 } } });
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
public class MyFoldManager implements FoldManager { // or extends AbstractFoldManager ... }
public class MyFoldManager ... ... public static final class Factory implements FoldManagerFactory { public FoldManager createFoldManager() { return new MyFoldManager(); } } }
NbJavaSettingsInitializer
)
public class MySettingsInitializer ... public void updateSettingsMap(Class kitClass, Map settingsMap) { ... settingsMap.put(SettingsNames.CODE_FOLDING_ENABLE, Boolean.TRUE); } }
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 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(); }
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 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.
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.
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.
Loaders/folder/any/Actions
so if any module wishes
to extend, hide or reorder some of them it can just register its actions there.<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.
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.
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.
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.
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.
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 "Shortcuts" linking to your action.
<folder name="Actions" > <folder name="Window"> <file name="org-netbeans-core-actions-PreviousViewCallbackAction.instance"/> </folder> </folder> <folder name="Shortcuts"> <file name="S-A-Left.shadow"> <attr name="originalFile" stringvalue="Actions/Window/org-netbeans-core-actions-PreviousViewCallbackAction.instance"/> </file> </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.
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.
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).
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 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.
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(); } } }