
org.openide.text.NbDocument Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.openide.text;
import java.awt.Color;
import java.awt.Component;
import java.io.IOException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JEditorPane;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.text.*;
import javax.swing.undo.UndoableEdit;
import org.netbeans.api.actions.Openable;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.awt.UndoRedo;
import org.openide.cookies.EditorCookie;
import org.openide.cookies.LineCookie;
import org.openide.util.Lookup;
import org.openide.util.Mutex;
import org.openide.util.NbBundle;
import org.openide.util.UserQuestionException;
/** Dummy class holding utility methods for working with NetBeans document conventions.
*
* @author Jaroslav Tulach
*/
public final class NbDocument extends Object {
/** Attribute that signals that a given character is guarded (cannot
* be modified). Implements {@link javax.swing.text.AttributeSet.CharacterAttribute} to signal that
* this attribute applies to characters, not paragraphs.
*/
public static final Object GUARDED = new AttributeSet.CharacterAttribute() {
};
/** Attribute set that adds to a part of document guarded flag
*/
private static final SimpleAttributeSet ATTR_ADD = new SimpleAttributeSet();
/** Attribute set to remove the guarded flag.
*/
private static final SimpleAttributeSet ATTR_REMOVE = new SimpleAttributeSet();
static {
ATTR_ADD.addAttribute(GUARDED, Boolean.TRUE);
ATTR_REMOVE.addAttribute(GUARDED, Boolean.FALSE);
}
/** Name of style attached to documents to mark a paragraph (line)
* as a (debugger) breakpoint.
* @deprecated since 6.29. Use {@link Annotation} instead.
*/
@Deprecated
public static final String BREAKPOINT_STYLE_NAME = "NbBreakpointStyle"; // NOI18N
/** Name of style attached to documents to mark a paragraph (line)
* as erroneous.
* @deprecated since 6.29. Use {@link Annotation} instead.
*/
@Deprecated
public static final String ERROR_STYLE_NAME = "NbErrorStyle"; // NOI18N
/** Name of style attached to documents to mark a paragraph (line)
* as current (in a debugger).
* @deprecated since 6.29. Use {@link Annotation} instead.
*/
@Deprecated
public static final String CURRENT_STYLE_NAME = "NbCurrentStyle"; // NOI18N
/** Name of style attached to documents to unmark a paragraph (line)
* as anything special.
* @deprecated since 6.29. Use {@link Annotation} instead.
*/
@Deprecated
public static final String NORMAL_STYLE_NAME = "NbNormalStyle"; // NOI18N
private NbDocument() {
}
/** Find the root element of all lines.
* All conforming NetBeans documents
* should return a valid element.
*
* @param doc styled document (expecting NetBeans document)
* @return the root element
* @exception NullPointerException If the doc
parameter
* is null
.
*/
public static Element findLineRootElement(StyledDocument doc) {
checkDocParameter(doc);
Element e = doc.getParagraphElement(0).getParentElement();
if (e == null) {
// try default root (should work for text/plain)
e = doc.getDefaultRootElement();
}
return e;
}
/** For given document and an offset, find the line number.
* @param doc the document
* @param offset offset in the document
* @return the line number for that offset
* @exception NullPointerException If the doc
parameter
* is null
.
*/
public static int findLineNumber(StyledDocument doc, int offset) {
/* pre-33165
Element paragraphsParent = findLineRootElement (doc);
return paragraphsParent.getElementIndex (offset);
*/
return new DocumentRenderer(DocumentRenderer.FIND_LINE_NUMBER, doc, offset).renderToInt();
}
/** Finds column number given an offset.
* @param doc the document
* @param offset offset in the document
* @return column within the line of that offset (counting starts from zero)
* @exception NullPointerException If the doc
parameter
* is null
.
*/
public static int findLineColumn(StyledDocument doc, int offset) {
/*
Element paragraphsParent = findLineRootElement (doc);
int indx = paragraphsParent.getElementIndex (offset);
return offset - paragraphsParent.getElement (indx).getStartOffset ();
*/
return new DocumentRenderer(DocumentRenderer.FIND_LINE_COLUMN, doc, offset).renderToInt();
}
/** Finds offset of the beginning of a line.
* @param doc the document
* @param lineNumber number of the line to find the start of (zero-based)
* @return offset
* @exception NullPointerException If the doc
parameter
* is null
.
* @exception IndexOutOfBoundsException when incorrect
* lineNumber
value is inserted
*/
public static int findLineOffset(StyledDocument doc, int lineNumber) {
/*
Element paragraphsParent = findLineRootElement (doc);
Element line = paragraphsParent.getElement (lineNumber);
if(line == null) {
throw new IndexOutOfBoundsException(
"Index=" + lineNumber + " is out of bounds."); // NOI18N
}
return line.getStartOffset ();
*/
return new DocumentRenderer(DocumentRenderer.FIND_LINE_OFFSET, doc, lineNumber).renderToInt();
}
/** Creates position with a bias. If the bias is {@link javax.swing.text.Position.Bias#Backward Backward}
* then if an insert occures at the position, the text is inserted
* after the position. If the bias is {@link javax.swing.text.Position.Bias#Forward Forward}, then the text is
* inserted before the position.
*
* The method checks if the document implements {@link PositionBiasable},
* and if so, {@link PositionBiasable#createPosition createPosition} is called.
* Otherwise an attempt is made to provide a Position
with the correct behavior.
*
* @param doc document to create position in
* @param offset the current offset for the position
* @param bias the bias to use for the position
* @exception NullPointerException If the doc
parameter
* is null
.
* @exception BadLocationException if the offset is invalid
*/
public static Position createPosition(Document doc, int offset, Position.Bias bias)
throws BadLocationException {
checkDocParameter(doc);
if (doc instanceof PositionBiasable) {
return ((PositionBiasable) doc).createPosition(offset, bias);
} else {
if (bias == Position.Bias.Forward) {
// default behaviour
return doc.createPosition(offset);
} else {
// use our special position
return BackwardPosition.create(doc, offset);
}
}
}
/** Mark part of a document as guarded (immutable to the user).
* @param doc styled document
* @param offset offset to start at
* @param len length of text to mark as guarded
* @exception NullPointerException If the doc
parameter
* is null
.
*/
public static void markGuarded(StyledDocument doc, int offset, int len) {
checkDocParameter(doc);
doc.setCharacterAttributes(offset, len, ATTR_ADD, false);
}
/** Remove guarded mark on a block of a document.
* @param doc styled document
* @param offset offset to start at
* @param len length of text to mark as unguarded
* @exception NullPointerException If the doc
parameter
* is null
.
*/
public static void unmarkGuarded(StyledDocument doc, int offset, int len) {
checkDocParameter(doc);
doc.setCharacterAttributes(offset, len, ATTR_REMOVE, false);
}
/** Inserts a text into given offset and marks it guarded.
* @param doc document to insert to
* @param offset offset of insertion
* @param txt string text to insert
* @exception NullPointerException If the doc
parameter
* is null
.
*/
public static void insertGuarded(StyledDocument doc, int offset, String txt)
throws BadLocationException {
checkDocParameter(doc);
doc.insertString(offset, txt, ATTR_ADD);
}
/** Attach a breakpoint to a line in the document.
* If the document has a defined style named {@link #BREAKPOINT_STYLE_NAME}, it is used.
* Otherwise, a new style is defined.
*
* @param doc the document
* @param offset identifies the line to set breakpoint to
* @exception NullPointerException If the doc
parameter
* is null
.
*
* @deprecated since 1.20. Use addAnnotation() instead
*/
@Deprecated
public static void markBreakpoint(StyledDocument doc, int offset) {
checkDocParameter(doc);
Style bp = doc.getStyle(BREAKPOINT_STYLE_NAME);
if (bp == null) {
// create the style
bp = doc.addStyle(BREAKPOINT_STYLE_NAME, null);
if (bp == null) {
return;
}
bp.addAttribute(StyleConstants.ColorConstants.Background, Color.red);
bp.addAttribute(StyleConstants.ColorConstants.Foreground, Color.white);
}
doc.setLogicalStyle(offset, bp);
}
/** Mark a line as erroneous (e.g. by the compiler).
* If the document has a defined style named {@link #ERROR_STYLE_NAME}, it is used.
* Otherwise, a new style is defined.
*
* @param doc the document
* @param offset identifies the line to mark
* @exception NullPointerException If the doc
parameter
* is null
.
*
* @deprecated since 1.20. Use addAnnotation() instead
*/
@Deprecated
public static void markError(StyledDocument doc, int offset) {
checkDocParameter(doc);
Style bp = doc.getStyle(ERROR_STYLE_NAME);
if (bp == null) {
// create the style
bp = doc.addStyle(ERROR_STYLE_NAME, null);
if (bp == null) {
return;
}
bp.addAttribute(StyleConstants.ColorConstants.Background, Color.green);
bp.addAttribute(StyleConstants.ColorConstants.Foreground, Color.black);
}
doc.setLogicalStyle(offset, bp);
}
/** Marks a line as current (e.g. for the debugger).
* If the document has a defined style named {@link #CURRENT_STYLE_NAME}, it is used.
* Otherwise, a new style is defined.
*
* @param doc the document
* @param offset identifies the line to mark
* @exception NullPointerException If the doc
parameter
* is null
.
*
* @deprecated since 1.20. Use addAnnotation() instead
*/
@Deprecated
public static void markCurrent(StyledDocument doc, int offset) {
checkDocParameter(doc);
Style bp = doc.getStyle(CURRENT_STYLE_NAME);
if (bp == null) {
// create the style
bp = doc.addStyle(CURRENT_STYLE_NAME, null);
if (bp == null) {
return;
}
bp.addAttribute(StyleConstants.ColorConstants.Background, Color.blue);
bp.addAttribute(StyleConstants.ColorConstants.Foreground, Color.white);
}
doc.setLogicalStyle(offset, bp);
}
/**
* Mark a line as normal (no special attributes).
* This uses the dummy style named {@link #NORMAL_STYLE_NAME}.
* This method should be used to undo the effect of {@link #markBreakpoint}, {@link #markError} and {@link #markCurrent}.
* @param doc the document
* @param offset identified the line to unmark
* @exception NullPointerException If the doc
parameter
* is null
.
*
* @deprecated since 1.20. Use addAnnotation() instead
*/
@Deprecated
public static void markNormal(StyledDocument doc, int offset) {
checkDocParameter(doc);
Style st = doc.getStyle(NORMAL_STYLE_NAME);
if (st == null) {
st = doc.addStyle(NORMAL_STYLE_NAME, null);
}
if (st != null) {
doc.setLogicalStyle(offset, st);
}
}
/**
* Gets recently selected editor pane opened by editor cookie
* Can be called from AWT event thread only.
*
* @param ec EditorCookie used to find out recently selected editor pane
* @return pane or null
*
* @since 6.24
*
*/
public static JEditorPane findRecentEditorPane (EditorCookie ec) {
// expected in AWT only
assert SwingUtilities.isEventDispatchThread()
: "NbDocument.findInitializedPaneForActiveTC must be called from AWT thread only"; // NOI18N
if (ec instanceof CloneableEditorSupport) {
// find if initialized or return null immediately
JEditorPane pane = ((CloneableEditorSupport) ec).getRecentPane();
return pane;
} else {
JEditorPane [] panes = ec.getOpenedPanes();
if (panes != null) {
return panes[0];
}
return null;
}
}
/** Locks the document to have exclusive access to it.
* Documents implementing {@link WriteLockable} can specify exactly how to do this.
*
* @param doc document to lock
* @param run the action to run
* @exception NullPointerException If the doc
parameter
* is null
.
*/
public static void runAtomic(StyledDocument doc, Runnable run) {
checkDocParameter(doc);
if (doc instanceof WriteLockable) {
// use the method
((WriteLockable) doc).runAtomic(run);
} else {
// transfer the runnable to event dispatch thread
synchronized (doc) {
run.run();
}
}
}
/** Executes given runnable in "user mode" does not allowing any modifications
* to parts of text marked as guarded. The actions should be run as "atomic" so
* either happen all at once or none at all (if a guarded block should be modified).
*
* @param doc document to modify
* @param run runnable to run in user mode that will have exclusive access to modify the document
* @exception NullPointerException If the doc
parameter
* is null
.
* @exception BadLocationException if a modification of guarded text occured
* and that is why no changes to the document has been done.
*/
public static void runAtomicAsUser(StyledDocument doc, Runnable run)
throws BadLocationException {
checkDocParameter(doc);
if (doc instanceof WriteLockable) {
// use the method
((WriteLockable) doc).runAtomicAsUser(run);
} else {
// transfer the runnable to event dispatch thread
synchronized (doc) {
run.run();
}
}
}
/** Helper method for checking document parameter validity.
* @exception NullPointerException If the doc
parameter
* is null
. */
private static void checkDocParameter(Document doc) {
if (doc == null) {
throw new NullPointerException("Invalid doc parameter. Document may not be null!"); // NOI18N
}
}
/** Find a way to print a given document.
* If the document implements the correct interface(s) then the document is returned,
* else a default {@link java.awt.print.Printable} is used as a wrapper. In this last case it is useful
* to implement {@link NbDocument.Printable} to describe how to print in terms of
* attributed characters, rather than specifying the complete page layout from scratch.
*
* @param doc the document to find printing support for
* @return an object that is instance of eith {@link java.awt.print.Printable} or {@link java.awt.print.Pageable}
*/
public static Object findPageable(StyledDocument doc) {
if (doc instanceof java.awt.print.Pageable) {
return doc;
} else if (doc instanceof java.awt.print.Printable) {
return doc;
} else {
return new DefaultPrintable(doc);
}
}
/**
* Add annotation to the document. For annotation of whole line
* the length parameter can be ignored (specify value -1).
*
* Note: since 6.35 the requests (delegated to document) are no longer replanned to EDT.
* @param doc the document which will be annotated
* @param startPos position which represent begining
* of the annotated text
* @param length length of the annotated text. If -1 is specified
* the whole line will be annotated
* @param annotation annotation which is attached to this text
* @since 1.20
*/
public static void addAnnotation(
final StyledDocument doc, final Position startPos, final int length, final Annotation annotation
) {
if (!(doc instanceof Annotatable)) {
return;
}
((Annotatable) doc).addAnnotation(startPos, length, annotation);
}
/**
* Removal of added annotation.
*
* Note: since 6.35 the requests (delegated to document) are no longer replanned to EDT.
* @param doc the annotated document
* @param annotation annotation which is going to be removed
* @since 1.20
*/
public static void removeAnnotation(final StyledDocument doc, final Annotation annotation) {
if (!(doc instanceof Annotatable)) {
return;
}
((Annotatable) doc).removeAnnotation(annotation);
}
/**
* Get an edit of given type that would be undone if an undo operation would be invoked
* at this time for an editor cookie.
*
* The edit to be undone may be composed from instances of various undoable edit types
* (see UndoableEditWrapper).
*
* @param type of undoable edit to be retrieved.
* @param ec editor cookie providing an undo/redo manager.
* @param type class of undoable edit to be retrieved.
* @return undoable edit of given type or null if there is no edit to be undone
* or an instance of the given type is not contained in the edit to be undone.
* @since 6.49
*/
public static T getEditToBeUndoneOfType(EditorCookie ec, Class type) {
return getEditToBeUndoneRedoneOfType(ec, type, false);
}
/**
* Get an edit of given type that would be redone if a redo operation would be invoked
* at this time for an editor cookie.
*
* The edit to be undone may be composed from instances of various undoable edit types
* (see UndoableEditWrapper).
*
* @param type of undoable edit to be retrieved.
* @param ec editor cookie providing an undo/redo manager.
* @param type class of undoable edit to be retrieved.
* @return undoable edit of given type or null if there is no edit to be redone
* or an instance of the given type is not contained in the edit to be redone.
* @since 6.49
*/
public static T getEditToBeRedoneOfType(EditorCookie ec, Class type) {
return getEditToBeUndoneRedoneOfType(ec, type, true);
}
private static T getEditToBeUndoneRedoneOfType(EditorCookie ec, Class type, boolean redone) {
UndoRedo ur;
if (ec instanceof CloneableEditorSupport &&
((ur = ((CloneableEditorSupport)ec).getUndoRedo()) instanceof UndoRedoManager))
{
UndoRedoManager urManager = (UndoRedoManager) ur;
UndoableEdit edit = urManager.editToBeUndoneRedone(redone);
if (type.isInstance(edit)) {
return type.cast(edit);
} else if (edit instanceof List) {
@SuppressWarnings("unchecked")
List listEdit = (List) edit;
for (int i = listEdit.size() -1; i >= 0; i--) { // Go from most wrapped back
edit = listEdit.get(i);
if (type.isInstance(edit)) {
@SuppressWarnings("unchecked") T inst = (T) edit;
return inst;
}
}
}
}
return null;
}
/**
* Get the document associated with a file.
*
* Method will throw {@link org.openide.util.UserQuestionException}
* exception if file size is too big. This exception is caught and its
* method {@link org.openide.util.UserQuestionException#confirmed} is used
* for confirmation.
*
*
* @param provider for example a {@code org.openide.loaders.DataObject}
* @return a document or null
* @since org.openide.text 6.46
*/
@NbBundle.Messages("TXT_Question=Question")
public static StyledDocument getDocument(Lookup.Provider provider) {
try {
EditorCookie ec = provider.getLookup().lookup(EditorCookie.class);
if (ec != null) {
StyledDocument doc = null;
try {
doc = ec.openDocument();
} catch (UserQuestionException uqe) {
final Object value = DialogDisplayer.getDefault().notify(
new NotifyDescriptor.Confirmation(uqe.getLocalizedMessage(),
Bundle.TXT_Question(),
NotifyDescriptor.YES_NO_OPTION));
if (value != NotifyDescriptor.YES_OPTION) {
return null;
}
uqe.confirmed();
doc = ec.openDocument();
}
return doc;
}
} catch (IOException ioe) {
Logger.getLogger(NbDocument.class.getName()).log(Level.WARNING, null, ioe);
}
return null;
}
/**
* Open the document associated with a file in the Editor window in a
* position specified by the offset while controlling open and visibility behavior.
* This method can be called from any thread but blocks until the document is opened.
*
* @param provider for example a {@code org.openide.loaders.DataObject}
* @param offset the position the document should be opened (starting at 0)
* @param openType control open behavior, {@link org.openide.text.Line.ShowOpenType#OPEN}
* would typically be used
* @param visibilityType control visibility behavior, {@link org.openide.text.Line.ShowVisibilityType#FOCUS}
* would typically be used
* @return true if the Document is opened - false otherwise
* @see #getDocument
* UserQuestionException handling
* @since org.openide.text 6.46
*/
public static boolean openDocument(Lookup.Provider provider, int offset, Line.ShowOpenType openType, Line.ShowVisibilityType visibilityType) {
assert provider != null;
LineCookie lc = provider.getLookup().lookup(LineCookie.class);
if ((lc != null) && (offset != -1)) {
StyledDocument doc = getDocument(provider);
if (doc != null) {
int line = NbDocument.findLineNumber(doc, offset);
int column = NbDocument.findLineColumn(doc, offset);
Line l = null;
try {
l = lc.getLineSet().getCurrent(line);
} catch (IndexOutOfBoundsException e) { // try to open at least the file (line no. is too high?)
l = lc.getLineSet().getCurrent(0);
}
if (l != null) {
doShow(l, column, openType, visibilityType);
return true;
}
}
}
Openable oc = provider.getLookup().lookup(Openable.class);
if (oc != null) {
doOpen(oc);
return true;
}
return false;
}
/**
* Open the document associated with a file in the Editor window in a
* position specified by the line and column while controlling open and visibility behavior.
* This method can be called from any thread but blocks until the document is opened.
*
* @param provider for example a {@code org.openide.loaders.DataObject}
* @param line the line the document should be opened (starting at 0)
* @param column the column which should be selected (starting at 0), value
* -1 does not change previously selected column
* @param openType control open behavior, {@link org.openide.text.Line.ShowOpenType#OPEN}
* would typically be used
* @param visibilityType control visibility behavior, {@link org.openide.text.Line.ShowVisibilityType#FOCUS}
* would typically be used
* @return true if the Document is opened - false otherwise
* @see #getDocument
* UserQuestionException handling
* @since org.openide.text 6.46
*/
public static boolean openDocument(Lookup.Provider provider, int line, int column, Line.ShowOpenType openType, Line.ShowVisibilityType visibilityType) {
assert provider != null;
LineCookie lc = provider.getLookup().lookup(LineCookie.class);
if ((lc != null) && (line >= 0) && (column >= -1)) {
StyledDocument doc = getDocument(provider);
if (doc != null) {
Line l = null;
try {
l = lc.getLineSet().getCurrent(line);
} catch (IndexOutOfBoundsException e) { // try to open at least the file (line no. is too high?)
l = lc.getLineSet().getCurrent(0);
}
if (l != null) {
doShow(l, column, openType, visibilityType);
return true;
}
}
}
final Openable oc = provider.getLookup().lookup(Openable.class);
if (oc != null) {
doOpen(oc);
return true;
}
return false;
}
private static void doShow(final Line l, final int column, final Line.ShowOpenType openType, final Line.ShowVisibilityType visibilityType) {
Mutex.EVENT.writeAccess(new Runnable() {
@Override
public void run() {
l.show(openType, visibilityType, column);
}
});
}
private static void doOpen(final Openable oc) {
Mutex.EVENT.writeAccess(new Runnable() {
@Override
public void run() {
oc.open();
}
});
}
/** Specialized version of document that knows how to lock the document
* for complex modifications.
*/
public interface WriteLockable extends Document {
/** Executes given runnable in lock mode of the document.
* In this mode, all redrawing is stopped and the caller has exclusive
* access to all modifications to the document.
*
* By definition there should be only one locker at a time. Sample implementation, if you are extending {@link AbstractDocument}:
*
*
* writeLock();
* try {
* r.run();
* } finally {
* writeUnlock();
* }
*
*
* @param r runnable to run while locked
*
* @see NbDocument#runAtomic
*/
public void runAtomic(Runnable r);
/** Executes given runnable in "user mode" does not allowing any modifications
* to parts of text marked as guarded. The actions should be run as "atomic" so
* either happen all at once or none at all (if a guarded block should be modified).
*
* @param r runnable to run in user mode that will have exclusive access to modify the document
* @exception BadLocationException if a modification of guarded text occured
* and that is why no changes to the document has been done.
*/
public void runAtomicAsUser(Runnable r) throws BadLocationException;
}
/** Document which may support styled text printing.
* Any document that wishes to support special formatting while printing
* can implement this interface and provide a AttributedCharacterIterator
* specifying colors, fonts, etc.
*/
public interface Printable extends Document {
/** Get an attributed character iterator for the document, so that it may be printed.
*
For a convenient way to do this, you may use {@link AttributedCharacters#iterator a simple implementation}
* of an
* attributed character list.
* @return list of AttributedCharacterIterator
s to be used for printing
*
* @see NbDocument#findPageable */
public java.text.AttributedCharacterIterator[] createPrintIterators();
}
/** Enhanced version of document that provides better support for
* holding and working with biased positions. It adds one new method
* {@link #createPosition} that creates
* a position that moves either to the left or to the right when an insertion
* is performed at it.
*
* If a document implements this interface, the new method is
* used in {@link NbDocument#createPosition}.
* If not, special support for the position is created.
*/
public interface PositionBiasable extends Document {
/** Creates position with a bias. If the bias is {@link javax.swing.text.Position.Bias#Backward Backward}
* then if an insert occures at the position, the text is inserted
* after the position. If the bias is {@link javax.swing.text.Position.Bias#Forward Forward}, then the text is
* inserted before the position.
*
* @param offset the offset for the position
* @param bias the bias to use for the position
* @exception BadLocationException if the offset is invalid
*
* @see NbDocument#createPosition
*/
public Position createPosition(int offset, Position.Bias bias)
throws BadLocationException;
}
/** Enabled documents to add special UI components to their Editor pane.
* If this interface is implemented by the Editor document, it can be used
* to add other components (such as toolbars) to the pane.
*/
public interface CustomEditor extends Document {
/** Create a whole editor component over the given JEditorPane
.
* The implementation should generally add some kind of scrolling
* support to the given JEditorPane
(typically with scrollbars),
* possibly some other components
* according to the desired layout,
* and return the resulting container.
* @param j editor pane over which the resulting component
* will be built
* @return component encapsulating the pane and all other
* custom UI components
*/
public Component createEditor(JEditorPane j);
}
/**
* Enabled documents to add special UI toolbar components to their Editor pane.
*/
public interface CustomToolbar extends Document {
/**
* Implementation shall return a toolbar for the document. Preferably non-floatable.
*/
public JToolBar createToolbar(JEditorPane j);
}
/** Enhanced version of document which is capable of
* attaching/detaching of annotations. It is guaranteed that
* annotations are added/removed to document only in AWT thread.
* @since 1.20
*/
public interface Annotatable extends Document {
/** Add annotation to the document. For annotation of whole line
* the length parameter can be ignored (specify value -1).
* @param startPos position which represent begining
* of the annotated text
* @param length length of the annotated text. If -1 is specified
* the whole line will be annotated
* @param annotation annotation which is attached to this text */
public void addAnnotation(Position startPos, int length, Annotation annotation);
/** Removal of added annotation.
* @param annotation annotation which is going to be removed */
public void removeAnnotation(Annotation annotation);
}
private static final class DocumentRenderer implements Runnable {
private static final int FIND_LINE_NUMBER = 0;
private static final int FIND_LINE_COLUMN = 1;
private static final int FIND_LINE_OFFSET = 2;
private StyledDocument doc;
private int opCode;
private int argInt;
private int retInt;
DocumentRenderer(int opCode, StyledDocument doc, int argInt) {
this.opCode = opCode;
this.doc = doc;
this.argInt = argInt;
}
int renderToInt() {
if (doc != null) {
doc.render(this);
}
return retInt;
}
public void run() {
switch (opCode) {
case FIND_LINE_NUMBER: {
Element paragraphsParent = findLineRootElement(doc);
retInt = paragraphsParent.getElementIndex(argInt); // argInt is offset
break;
}
case FIND_LINE_COLUMN: {
Element paragraphsParent = findLineRootElement(doc);
int indx = paragraphsParent.getElementIndex(argInt); // argInt is offset
retInt = argInt - paragraphsParent.getElement(indx).getStartOffset();
break;
}
case FIND_LINE_OFFSET: {
Element paragraphsParent = findLineRootElement(doc);
Element line = paragraphsParent.getElement(argInt); // argInt is lineNumber
if (line == null) {
throw new IndexOutOfBoundsException("Index=" + argInt + " is out of bounds."); // NOI18N
}
retInt = line.getStartOffset();
break;
}
default:
throw new IllegalStateException();
}
}
}
}