This document describes the NetBeans Editor API, attempting to give a summary of what you need to know about it for different purposes.
The Open APIs only cover certain aspects of working with the editor. For example, you can open documents, modify them, add annotations, register editor kits you built from scratch, and so on. Some more specific capabilities that are frequently requested are not guaranteed by the Open APIs but may be possible using NetBeans modules which may have differing standards of API design, documentation, and compatibility from release to release. Here are some resources that may also be of interest:
The NetBeans editor module is the typical editor implementation. It is possible to use base classes in this module as a starting point for defining your own editor kits: syntax coloring, code completion, etc. for new languages and file formats. As of this writing, the details of how to add new editor kits from a NetBeans module are virtually undocumented, and compatibility is likely to be broken in future NetBeans releases, so do so at your own risk.
The lexer module is planned to make creation of new editor kits easier.
The Editor API may be used at a first cut for two purposes: accessing editor-related functionality from within NetBeans for use by other modules; and implementing a custom editor that can be inserted into NetBeans as a module, replacing or supplementing the NetBeans-supplied one.
JFrame
) from template - in either case, there is a
new .java
file in the user's
repository containing Java source, together with a .form
file.
.form
file associated with a
.java
file;
when it finds them, it:
.form
) file in the Form Editor.
.java
file in an editor.
.java
file was already open in some editor
window or pane, then this window/pane is reused and just given
focus (i.e. a new
{@link javax.swing.JEditorPane JEditorPane }
is created, but using the same
{@link javax.swing.text.EditorKit EditorKit }
and
{@link javax.swing.text.StyledDocument StyledDocument }).
If not, a new JEditorPane
is
{@link javax.swing.JEditorPane#getEditorKitForContentType(java.lang.String) asked }
to create an editor for the Java source content type (typically
text/x-java
). Now, the Form Editor's data loader is an
extension of the Java Source data loader; the Java loader specially treats the request to load its content
into an editor: it inserts most of the text straight into the
StyledDocument
just as you would expect, but also checks
for special comments looking something like //GEN-BEGIN:
in the source code, which are the markers used to indicate guarded
areas in saved source code. When it finds one, rather than inserting
the marker into the document, it uses
{@link org.openide.text.NbDocument#insertGuarded(javax.swing.text.StyledDocument,int,java.lang.String) NbDocument.insertGuarded(...) }
to insert the enclosed text area, making it read-only to the user.
JEditorPane
now has a StyledDocument
suited to Java source code loaded into it. This document may be
implemented by the standard NetBeans Editor, or it may be a custom editor. The user may edit non-guarded
parts of the document; attempted actions (like typing) that would
affect guarded areas are prevented by the editor in use.
Note that the Form Editor directly or indirectly calls {@link javax.swing.text.Document#insertString(int,java.lang.String,javax.swing.text.AttributeSet) Document.insertString(...) } (or {@link org.openide.text.NbDocument#insertGuarded(javax.swing.text.StyledDocument,int,java.lang.String) NbDocument.insertGuarded(...) }) and {@link javax.swing.text.Document#remove(int,int) Document.remove(...) } to do this work, so it is itself unaffected by guard blocks.
//GEN-BEGIN:
before the text is written to file.
File f = new File("/home/me/sources/my/pkg/MyFile.java"); FileObject fo = FileUtil.fromFile(f)[0]; DataObject d = DataObject.find(fo); EditorCookie ec = (EditorCookie)d.getCookie(EditorCookie.class); ec.{@link org.openide.cookies.EditorCookie#open() open }(); StyledDocument doc = ec.{@link org.openide.cookies.EditorCookie#openDocument() openDocument }();
if (ec.{@link org.openide.cookies.EditorCookie#isModified() isModified }()) ...Then see what the current contents of line 26 are (zero-based):
int start={@link org.openide.text.NbDocument#findLineOffset(javax.swing.text.StyledDocument,int) NbDocument.findLineOffset }(doc, 25); int end=NbDocument.findLineOffset(doc, 26); String contents=doc.getText(start, end-start);And display this line in the Editor window:
ec.{@link org.openide.cookies.LineCookie#getLineSet() getLineSet }().{@link org.openide.text.Line.Set#getCurrent(int) getCurrent }(25).{@link org.openide.text.Line#show(int) show }({@link org.openide.text.Line#SHOW_TRY_SHOW Line.SHOW_TRY_SHOW });Now insert a new line here after it:
final BadLocationException[] exc = new BadLocationException[] {null}; NbDocument.{@link org.openide.text.NbDocument#runAtomicAsUser(javax.swing.text.StyledDocument,java.lang.Runnable) runAtomicAsUser }(doc, new Runnable() { public void run() { try { doc.insertString(NbDocument.findLineOffset(doc, 26), "// New text.\n", SimpleAttributeSet.EMPTY); } catch (BadLocationException e) { exc[0] = e; } } }); if (exc[0] != null) throw exc[0];All done! Prompt to save the file, and close the editor window:
ec.{@link org.openide.cookies.EditorCookie#close() close }();
Typically you will want to remember the positions of the guard blocks you added using a position, so that user edits in the vicinity of the guard block will be taken into consideration. You may create such a position using {@link org.openide.text.NbDocument#createPosition(javax.swing.text.Document,int,javax.swing.text.Position.Bias) NbDocument.createPosition(...) }, and retrieve its current offset when needed using {@link javax.swing.text.Position#getOffset() Position.getOffset() }.
To do the same sort of thing while preventing yourself from accidentally touching a guard block, i.e. if your module was not the creator of the guard block (or you are not even sure if there any in the document), please use {@link org.openide.text.NbDocument#runAtomicAsUser(javax.swing.text.StyledDocument,java.lang.Runnable) NbDocument.runAtomicAsUser(...) } instead.
In order to provide richer interactions between the editor and other modules, the APIs provide for a system of annotations which are used for things like debugger breakpoints, erroneous lines, and so on. From an API perspective, anyone can create annotations and attach them to a document; the editor implementation should attempt to display these annotations as it sees fit, taking hints from the annotation provider.
Using annotations on a document need not be difficult. Everything you need to do is to define the visual appearance of your annotation and to provide a class which will represent it programmatically. For example, if you are writing a module which interacts with an open document containing some sort of program code, and the user has made a syntax error in the code which you were able to detect and want to point out, you may want to mark the line or part of the line as "erroneous". To achieve this you must:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE type PUBLIC "-//NetBeans//DTD annotation type 1.0//EN" "http://www.netbeans.org/dtds/annotation-type-1_0.dtd"> <type name="org-nb-modules-foo-some-annotation" description_key="LBL_some-annotation" localizing_bundle="org.nb.modules.foo.Bundle" visible="true" glyph="nbresloc:/org/nb/modules/foo/some-annotation-glyph.gif" highlight="#FFFF00" type="line"/>
As you can see the definition of the annotation type consists of the highlight color; the glyph icon which could be shown in the left margin of the editing area - the glyph gutter; the localized name; and so on. For more details about each field in the DTD see below.
public final class SomeAnnotation extends Annotation { private final String error; public SomeAnnotation(String error) { this.error = error; } public String {@link org.openide.text.Annotation#getAnnotationType() getAnnotationType }() { return "org-nb-modules-foo-some-annotation"; } public String {@link org.openide.text.Annotation#getShortDescription() getShortDescription }() { // Localize this with NbBundle: return "The error was: " + error; } }Note that you can return the tooltip text asynchronously, on demand, or dynamically: you may return
null
for no tooltip, and
you may later return a new or changed tooltip - just use:
firePropertyChange(PROP_SHORT_DESCRIPTION, null, null);to inform the editor of the change. The tooltip should be refreshed on-screen.
// Given error output like: "pkg/Name.java:123: You made a mistake" FileObject fileWithError = fs.findResource("pkg/Name.java"); DataObject objWithError = DataObject.find(fileWithError); {@link org.openide.cookies.LineCookie LineCookie } cookie = (LineCookie)objWithError.getCookie(LineCookie.class); {@link org.openide.text.Line.Set Line.Set } lineSet = cookie.getLineSet(); final {@link org.openide.text.Line Line } line = lineSet.{@link org.openide.text.Line.Set#getOriginal(int) getOriginal }(123); final Annotation ann = new SomeAnnotation("You made a mistake"); ann.{@link org.openide.text.Annotation#attach(org.openide.text.Annotatable) attach }(line);
In this example the user may correct the erroneous line, or at least do something to it - in this case we assume that the annotation is no longer needed, and should be removed. It is a simple call to do this; of course you need to know when it is appropriate, so for example:
line.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent ev) { String type = ev.getPropertyName(); if (type == null || type == Annotatable.PROP_TEXT) { // User edited the line, assume error should be cleared. ann.detach(); line.removePropertyChangeListener(this); } } });
It is possible that there might be more than one annotation on the same line. In this case it depends on the capabilities of the editor how the annotations will be displayed. The simplest scenario is that only one annotation is visible at time and the user can cycle through them. If this is the case it may be helpful to force the editor to move your annotation in front of any others:
ann.{@link org.openide.text.Annotation#moveToFront() moveToFront }();
NetBeans presents GUI settings to the user in which it is possible to customize the attributes of annotation types - for example, foreground and background color. Remember however that some editor implementations may find it difficult or impossible to accurately display all of these settings (for example display of arbitrary glyphs may be infeasible).
<type>
element are:
name
Annotation.getAnnotationType()
.description_key
localizing_bundle
description_key
.visible
glyph
nbresloc
protocol to access module resources and
automatically localize them. A 16x16 GIF is common.highlight
foreground
type
line
(full-line annotation) and linepart
(may be applied to a column range within a line).actions
<folder name="YourAnnotationTypeActions"> <file name="org-foo-BarAction.instance"/> </folder>
severity
error
,
warning
, ok
and none
. Annotations with severity
other then none
will be shown in the Error Stripe using default color
or color specified in custom_sidebar_color
. Default value is none
.
custom_sidebar_color
severity
is
not feasible. Note that this setting has no effect if severity
is set to none
.
browseable
true
and false
, default is false
.
priority
The annotation <type>
may also contain the
<combinations>
element - a definition of annotation
combinations. If two or more annotations from the same module (or
otherwise mutually known) are on the same line it might make sense to
combine them into one annotation visually. For example, if a debugger
module attached a breakpoint to a line and the current program counter
reached the same line, it is more pleasant to show a combination of
these two annotations rather than two separate annotations. This can
be accomplished using a combination definition. A combined annotation
type is otherwise normal (it may have a glyph icon and colors), but it
also contains the definition of annotation types which are subsumed by
this type. For example the combination of program counter with
breakpoint could be defined as follows:
<combinations tiptext_key="PC_BREAKPOINT_COMBINATION_TOOLTIP_TEXT"> <combine annotationtype="NameOfTheBreakpointAnnotationType"/> <combine annotationtype="NameOfTheCurrentPCAnnotationType"/> </combinations>
It is the responsibility of the editor to display combinations where the criteria for combinations as defined in available annotation types are met.
Attributes of the <combinations>
element
are:
tiptext_key
description_key
on <type>
. Since
multiple annotation types are being combined, each of which might have
its own tooltip, the combined annotation has just a static tooltip
which might explain that several annotations are combined in it.order
min_optionals
optional
of the
<combine/>
element. The combination is defined as a
list of annotation types to be combined. To make creation of the
combination more flexible, some annotation types can be optional
(optional
attribute on <combine/>
).
min_optionals
is then used to specify the minimum number
of optional annotation types which must be found to make the
combination valid. For example, you may have have a conditional
breakpoint and disabled breakpoint and wish to combine either of them
with the current program counter. You can define it as follows:
<combinations tiptext_key="COMBINATION_TOOLTIP_TEXT" min_optionals="1"> <combine annotationtype="debugger-disabledbreakpoint" optional="true"/> <combine annotationtype="debugger-conditionalbreakpoint" optional="true"/> <combine annotationtype="debugger-currentpc"/> </combinations>This way you specify that both breakpoints are optional, but that at least one must be found to make the combination succeed.
<combinations>
element can contain only
<combine/>
elements (one for each annotation type)
and they can have these attributes:
annotationtype
name
on <type>
.absorb_all
optional
min
If you would like to add something akin to guard blocks to a custom
file type handled by your module, you will need to insert the
appropriate hooks. The best thing to do is to subclass
EditorSupport
,
and use this subclass to implement cookies such as
{@link org.openide.cookies.EditorCookie EditorCookie }.
Now just provide appropriate implementations of
{@link org.openide.text.CloneableEditorSupport#loadFromStreamToKit(javax.swing.text.StyledDocument,java.io.InputStream,javax.swing.text.EditorKit) CloneableEditorSupport.loadFromStreamToKit(...) }
(to interpret special markings in the saved file and convert them into document attributes); and
{@link org.openide.text.CloneableEditorSupport#saveFromKitToStream(javax.swing.text.StyledDocument,javax.swing.text.EditorKit,java.io.OutputStream) CloneableEditorSupport.saveFromKitToStream(...) }
(to translate these attributes back into ASCII markings).
Beyond being a generic EditorKit, there are two significant pieces of functionality used by NetBeans which the editor should support if at all possible:
Actually, you could get away with a plain
{@link javax.swing.text.Document Document }
if you really wanted, but you would be unable to support guard blocks
and would have a harder time supporting annotations and line numbering; while this would probably
suffice for an editor that was not to be used on Java source code, in
general it is very much recommended that StyledDocument
be used instead.
Document
from the registered editor kit; however
this will not support guards (or NetBeans attributes either), and so if the
user edits what should have been a guarded block, they may cause
errors in the code, and their edits may be overwritten by NetBeans. If
you implement StyledDocument
, it is assumed you are
handling guard blocks, but again it is up to you to actually do so
correctly - NetBeans cannot determine this.
There is very little to the way NetBeans indicates
guarding. Essentially, it is just a character attribute, {@link org.openide.text.NbDocument#GUARDED NbDocument.GUARDED },
which will be set to Boolean.TRUE
for guarded characters,
and Boolean.FALSE
(or unset) for others; it is typically
placed on the document by means of the
{@link org.openide.text.NbDocument#markGuarded(javax.swing.text.StyledDocument,int,int) NbDocument.markGuarded(...) }
and
{@link org.openide.text.NbDocument#unmarkGuarded(javax.swing.text.StyledDocument,int,int) NbDocument.unmarkGuarded(...) }
methods, which just call
{@link javax.swing.text.StyledDocument#setCharacterAttributes(int,int,javax.swing.text.AttributeSet,boolean) StyledDocument.setCharacterAttributes(...) }.
Very likely you will want to override this method, calling its
superclass method but first checking to see if the attribute setting
mentions GUARDED
, whether setting it to TRUE
or FALSE
, and if so keeping track in another data
structure of which ranges of characters are currently guarded or not;
or you might already have a good way of keeping track of attributes in
general and would rather just query this attribute when it is
needed. If overriding setCharacterAttributes
for this
purpose, also remember to check the attributes on
{@link javax.swing.text.Document#insertString(int,java.lang.String,javax.swing.text.AttributeSet) insertString },
etc.
Note that currently guarded areas always consist of entire lines or sets of lines, so if you make this assumption your editor will be acceptable - but please handle sub-line granularity of guard blocks if you can do so easily, in case this situation changes. Along the same lines, you should make sure that users may only insert a newline immediately before a guarded block, and not any other character, for otherwise a half-guarded line would be created.
It is important to understand that the guarded attribute applies
only to user modifications, i.e. those undertaken in the
context of a user-initiated Swing action, or by
{@link org.openide.text.NbDocument#runAtomicAsUser(javax.swing.text.StyledDocument,java.lang.Runnable) NbDocument.runAtomicAsUser(...) }.
Internal NetBeans module code may freely insert or remove text (typically using
{@link org.openide.text.NbDocument#insertGuarded(javax.swing.text.StyledDocument,int,java.lang.String) NbDocument.insertGuarded(...) }
and Document.remove(...)
). For example, this would be
done by the Form Editor component while adding a new event
handler. The editor implementation should permit such programmatic
inserts and removes, while corresponding user actions must be
forbidden.
It is preferable, though optional, for the editor to visually mark guarded areas, say with a special background color. It is your responsibility to choose a color or another appearance for this purpose, however. Possibly NetBeans will specify a {@link javax.swing.text.StyleConstants.ColorConstants#Background StyleConstants.ColorConstants.Background } attribute describing which color will actually be used, and make this color settable in a control panel, but assume that you need to colorize based on this attribute yourself.
Even better, whenever possible an action should be disabled when it can easily be determined in advance that it would be in violation if performed at that time. As an example, if the user moves the caret into the middle of a guarded block, it would be much preferable for the editor to disable "Paste" (say, on a right-click context menu) for the time being, rather than permitting a Paste action to be invoked but then displaying an error dialog or beeping!
StyledDocument
, such as Swing's
{@link javax.swing.text.DefaultStyledDocument DefaultStyledDocument },
which already has a full implementation of important things but no
recognition of guard blocks.
The idea is to first get a list of all
{@link javax.swing.Action actions }
which might involve buffer modification - or, to be conservative, just
all actions. For each of these, create a new action which calls the
original
{@link java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) actionPerformed },
but "dynamically binds" some special flag stored with the document to
true - dynamic binding here means that it should be restored (turned
back off) in a finally
block, and also that its value
should be specific to the executing thread (and its children,
perhaps). This flag indicates that operations are currently being
performed from a user action.
Override the insertString(...)
and remove
methods. They should check the flag just mentioned. If it is turned
off, just call the superclass method normally. If it is turned on,
check whether the requested insertion or removal is affecting a
guarded area (insertion within, or removal within or overlapping). If
not, again call the superclass method. If there is an attempted
violation, you might do one or more of the following:
actionPerformed
does not have any
throws), it must do something - currently, it seems the Swing actions
simply beep.
Now bind all invocation objects (such as keymaps) to the new "wrapper" actions. Note that just returning them from the editor kit may not suffice.
If you go to the trouble of identifying specifically which actions could cause a violation, and under what circumstances they would or would not do so, then you could make specific wrappers that could disable actions, or safely invoke them only when possible, calling the original action if all is well - this is just one way to do the recommended implementation.
SimpleAttributeSet sas=new SimpleAttributeSet();
sas.addAttribute(NbDocument.GUARDED, Boolean.TRUE);
doc.setCharacterAttributes(0, 100, sas, false);
Now verify that modification attempts in this block are disabled.
You should make sure that lines in the editor are separated by exactly one text character - a newline. The APIs may rely on this to count character positions during parsing, so that this functionality may be efficient.
Furthermore you should ensure that the
{@link javax.swing.text.Document#getDefaultRootElement() default root element}
of the document has child elements corresponding to all text lines;
and if using StyledDocument
, that
{@link javax.swing.text.StyledDocument#getParagraphElement(int) "paragraph" elements}
again correspond to text lines. Proper use of elements corresponding
to lines will enable the APIs to accurately translate between buffer
offsets and line numbers. It is fine to have additional element
structures not corresponding to lines so long as the two conditions
above are met.
NbDocument.Annotatable
interface.NbDocument.Annotatable
public void addAnnotation(Position startPos, int length, Annotation annotation);The method is called with a
Position
in the document; the
length of the annotation (if the annotation is of the whole-line type,
the length will be -1
); and the annotation instance. Now
it is the responsibility of editor to collect all annotations for the
document and to display them properly. It may be helpful to take
advantage of StyledDocument
for changing the style of the
line (or part of the line). As for the glyph icon, it is fully up to
the editor how to represent it (if at all). The NetBeans standard
editor displays it in the left gutter.
The second method removed an annotation previously added:
public void removeAnnotation(Annotation annotation);This method will be called when the annotation is programmatically detached, or the document is about to be closed.
The editor should combine annotation types where requested if this behavior is supported.
If you want your editor to support printing of its contents,
you might just provide no special support. In this case,
printing should be able to work with plain text. If you have a styled
document, it is possible that NetBeans will be able to run through the
entire contents of the document; for each paragraph, retrieve its
Style
, and for each character, its attribute set; and
then attempt to convert these (Swing) formatting specifications into
(AWT) attributed character descriptions, for purposes of printing.
However, this process could be rather slow; is not terribly exact;
and may not be implemented. To better support printing, it is
desirable for the StyledDocument
to implement
{@link org.openide.text.NbDocument.Printable org.openide.text.NbDocument.Printable },
which will allow it to specify exactly how it wants to be printed. The
method
{@link org.openide.text.NbDocument.Printable#createPrintIterators() NbDocument.Printable.createPrintIterators(...) }
should return a list of
{@link java.text.AttributedCharacterIterator java.text.AttributedCharacterIterator }s,
providing attributes taken from
{@link java.awt.font.TextAttribute java.awt.font.TextAttribute }.
Also, if your document supports either {@link java.awt.print.Printable java.awt.print.Printable } or {@link java.awt.print.Pageable java.awt.print.Pageable }, then these interfaces will be used to perform the printing directly. You should only need to do this if attributed characters do not satisfactorily capture everything that you are interested in printing - for example, if your editor is working on HTML and it is desired to print embedded images.
StyledDocument
implementation to
be able to lock the document against write access, as this will make
certain operations (performed by NetBeans, not in response to user
events) more reliable and safer. In order to do this, please
implement
{@link org.openide.text.NbDocument.WriteLockable org.openide.text.NbDocument.WriteLockable }
and its
{@link org.openide.text.NbDocument.WriteLockable#runAtomic(java.lang.Runnable) runAtomic(...) }
method. The Runnable
passed in this way should be
executed with all other write actions (and reads) disabled,
i.e. blocked.
If you are extending
{@link javax.swing.text.AbstractDocument javax.swing.text.AbstractDocument },
please note that just enclosing the Runnable
block in
calls to
{@link javax.swing.text.AbstractDocument#writeLock() writeLock() }
and
{@link javax.swing.text.AbstractDocument#writeUnlock() writeUnlock() }
will not do the trick - these calls do take out exclusive locks,
however they specifically do not nest. This means that you cannot
lock and then enter a runnable this way, because any modifications
attempted within the runnable will throw an illegal state
exception. Also, the locking methods are final and cannot be
advised to nest. So you must find some other way to implement
this, e.g. your own locks.
You must also implement
{@link org.openide.text.NbDocument.WriteLockable#runAtomicAsUser(java.lang.Runnable) NbDocument.WriteLockable.runAtomicAsUser(...) },
which is very similar but is invoked on behalf of user actions
unrelated to whatever component created the guard blocks - e.g., this
would be used to rename a method from the Explorer, in which case the
rename ought to check that the renamed method is not guarded due to
being used by the Form Editor. Thus, it should attempt to make
modifications requested by the Runnable
, locking out
other modifications, but ensure that guard blocks are not violated.
Any attempted violation should be prevented (or rolled back at the end
of the runnable), and after the end of the runnable an appropriate
exception should be thrown.
Note that while both methods in this interface ought to provide a
transactional interface if at all possible - i.e., either succeed at
executing the entire Runnable
, or fail and leave the
document untouched - the transactional aspect is much more important to
implement properly for runAtomicAsUser
. This is because
it is quite possible for an innocent user action (e.g. attempting to
rename a JavaBean property) to interfere with a guard block, and the
document should not be left half-modified after such a
mistake. On the other hand, a failure in the block of
runAtomic
is more likely to be an bug in some module,
and not the user's fault; so perfect recovery is of course less
important, as the bug should be fixed anyway.
There is currently no default convenience implementation of
runAtomicAsUser
, as any implementation may well be
closely tied to how guard blocks are implemented in that particular
editor - e.g. in Emacs there will be a quite idiosyncratic
implementation.
If this is not done, NetBeans creates its own replacement.
NetBeans sometimes asks the editor to scroll the display so that a given line will be displayed. You should not need to do anything special to support this; {@link javax.swing.text.Caret#setDot(int) Caret.setDot() } will be called and ought to perform the scrolling appropriately.
Ctrl-R
; etc.
While it might be possible to add some of these things manually into the NetBeans action containers (like the "Main Window" entry in the Environment, visible in the Explorer), this is discouraged as that makes it more difficult for NetBeans to manage actions added by various modules.
Instead, you should use the Actions API to add {@link org.openide.util.actions.CallbackSystemAction CallbackSystemAction }s (e.g.) to your module.
You do not need to do much to support this editor toolbar: in the {@link javax.swing.text.EditorKit#getActions() getActions() } method of your editor kit, if there are any actions which are also {@link org.openide.util.actions.SystemAction SystemAction }s implementing {@link org.openide.util.actions.Presenter.Toolbar Presenter.Toolbar }, then this interface will be used to get a {@link java.awt.Component java.awt.Component } which will be displayed in the editor toolbar. You are free to make this component be whatever you like, e.g. a button to run some routine, or a checkbox to toggle an option; just add the appropriate event listeners to it.
A few basic editing commands you do not need to explicitly
present to NetBeans; this will be done for you automatically. NetBeans
tests whether an editor kit provides (from its
EditorKit.getActions()
method) any actions with certain
names. Default instances of these are also available in the
DefaultEditorKit
class, but your actions will be
recognized based on a call to
{@link javax.swing.Action#getValue(java.lang.String) getValue }({@link javax.swing.Action#NAME Action.NAME })
:
Name | Static field in {@link javax.swing.text.DefaultEditorKit DefaultEditorKit } |
---|---|
copy-to-clipboard | copyAction |
cut-to-clipboard | cutAction |
paste-from-clipboard | pasteAction |
There are a few more which unfortunately do not exist in the
DefaultEditorKit
, and therefore have no standard
names. The following may be used:
Name | Description |
---|---|
find | Pop up dialog to find a string in the text. |
replace | Pop up dialog to find & replace a string in the text. |
goto | Pop up dialog to go to some place in the text, e.g. a specific line number. |
delete | Delete the selection, without affecting clipboard. |
undo | Undo action. See package {@link javax.swing.undo javax.swing.undo } for help. Currently not specially supported, except to call your action. |
redo | Redo action. Ditto. |
The corresponding items in the NetBeans "Edit" menu, and other standard invocations such as from the system toolbar, will automatically invoke these actions on your document, if you supply them. You should use {@link javax.swing.Action#isEnabled() Action.isEnabled() } to indicate whether your action is ready, and thus whether or not the corresponding UI elements should be grayed out.
NbPreferences
or similar.
It is possible that there will be special support in the Modules API for registering editors. Although doing it manually in the module's {@link org.openide.modules.ModuleInstall#installed() installed() } method is not difficult, properly the module should also restore the original editor kit for each content type upon an uninstall, and ideally there would be a user-visible control panel permitting selection of the editor for each content type from among those claiming to support it.
This is not likely to be done unless it turns out to be popular to have multiple editors installed, and conflicts become a problem.
In the meantime, please refer to the Modules API for instructions on creating a module to contain your editor.