JBuilder editor concepts

General description

The JBuilder editor provides users a way to view and modify text documents. It also gives other tools, such as Code Insight and the structure pane, access to the text. The user communicates with the editor through keyboard and mouse input. The editor key bindings, as defined in keymaps, translate the keyboard input into meaningful editor actions that manipulate the text in the editor.

Detailed description

The editor manager

JBuilder has a single instance of the EditorManager class. Its task is to keep track of most of the editor specific details. The EditorManager creates instances of the EditorPane class. It also fires events to those EditorPanes to notify them about global changes they should act upon, such as changes to the tab size, the font, the keymap, and so on. See the EditorManager class for the complete list of attributes, also called properties, such as fontAttribute and tabSizeAttribute. You can recognize them because they all end with the word "Attribute."

The EditorManager is also responsible for creating and managing keymaps. For more information about keymaps, see "JBuilder keymap concepts."

Because other classes also must be notified about global editor changes, the EditorManager has support for adding and removing property change listeners. You can either add a class as the listener of a single property change with the (EditorManager.addPropertyChangeListener(String, PropertyChangeListener)) method, or add a class as the listener of all property changes with the (EditorManager.addPropertyChangeListener(PropertyChangeListener)) method. The class that is added as the listener must implement the java.beans.PropertyChangeListener interface, which basically means you must implement the propertyChange() method:

  void propertyChange(PropertyChangeEvent evt);

For example, this class implements the PropertyChangeListener interface; it's called each time the EditorManager fires a tab size property change:

  public class KnowsTabSize implements PropertyChangeListener {
    int tabSize;
    public KnowsTabSize() {
      tabSize = 0;
    }

    public static void initOpenTool(byte majorVersion, byte minorVersion) {

      // Make sure the OpenTools API is compatible
      if (majorVersion != PrimeTime.CURRENT_MAJOR_VERSION)
        return;

      // Create our class and add it as a listener of the tabSize EditorManager changes
      KnowsTabSize knowItAll = new KnowsTabSize();
      EditorManager.addPropertyChangeListener(EditorManager.tabSizeAttribute,
          knowItAll);
    }

    // Implement to satisfy the PropertyChangeListener interface
    // The EditorManager will call this function anytime it fires a
    // tab size property change

    public void propertyChange(PropertyChangeEvent e) {
      // Update our own tab size
      tabSize = EditorManager.getTabSize();
    }
  }
  

The editor pane

When JBuilder opens a text-based file, such as files with .java or .html extensions, the browser creates a new node, which adds a new tab to the content pane. The file opens in a new editor window. This editor window is an EditorPane. You can get a handle to the active editor window (the one with the focus) from EditorAction with code like this:
  EditorPane editor = EditorAction.getFocusedEditor();

Open nodes in the browser that don't have the focus might also have an EditorPane associated with them. In this case you can use the getEditor() method of EditorManager:

  Node[] nodes = Browser.getActiveBrowser().getOpenNodes();
  for (int i = 0; i < nodes.length; i++) {
    EditorPane editor = EditorManager.getEditor(nodes[i]);
    if (editor != null) {
      // Do something with the editor
    }
  }
  

The document

Each EditorPane stores its text in an EditorDocument. To access that document, call EditorPane.getDocument(). Once you have an EditorDocument object, you can get to the document's actual text. The text is stored in objects of type javax.swing.text.Element. Each Element object basically represents one line and has a starting and an ending index. Each character of the document also has an index. Here is a code sample that demonstrates how to use Element and the indexes:
  // First get the document
  EditorPane editor = EditorAction.getFocusedEditor();
  Document doc = editor.getDocument();

  // Get the base of all Elements
  Element baseElement = doc.getDefaultRootElement();

  // How many lines are there in the document?
  int totalLines = baseElement.getElementCount();

  // Where is my caret now?
  int caretIndex = editor.getCaretPosition();

  // Find the line the caret is on and get the Element of that line
  int lineIndex = baseElement.getElementIndex(caretIndex);
  Element lineElement = baseElement.getElement(lineIndex);

  // Get the boundaries of that line
  int startingIndex = lineElement.getStartOffset();
  int endingIndex = lineElement.getEndOffset();

  //Get the actual text of the line
  String lineText = doc.getText(startingIndex, endingIndex - startingIndex);

The caret

Each editor pane has a caret, which will be at a certain index into the document. The caret also holds information about the starting and ending index of a highlighted selection, if there is one. The editor has methods that work with the selected text as well. The following code shows how to use the caret:
  // Get the editor and the caret
  EditorPane editor = EditorAction.getFocusedEditor();
  Caret caret = editor.getCaret();

  // Where is the caret?
  int caretPosition = editor.getCaretPosition();

  // Does the caret say there is a highlighted selection?
  int dot = caret.getDot();
  int mark = caret.getMark();

  if (dot != mark) {
    // There is an highlighted selection
  }

  // Alternate way to find out about selections
  int selectBegin = editor.getSelectionStart();
  int selectEnd = editor.getSelectionEnd();
  if (selectBegin != selectEnd) {
    // There is an highlighted selection
  }

The editor actions

The EditorActions class contains a set of actions for the editor. Learn about these actions by studying the Keymap examples in the OpenTools samples. You can find samples that show how to create an CUA key binding, an Emacs key binding, and a Brief key binding. The keymaps associate keystrokes with many of the actions defined in EditorActions.

When a new action is written, you must hook it up to a keystroke. Also, the action should register itself so it will be displayed in the keymap editor. There are several steps involved in this process, and these steps can be studied in the samples/OpenToolsAPI/LineCommentHandler example, which shows how to create a new action and register it. Here is some of that code:

  public static void initOpenTool(byte majorVersion, byte minorVersion) {
    // Register our LineCommentHandler action so it will show up
    // in the keymap editor.
    // We register it as an Editor action because this action only
    // makes sense if the focus is in the editor.
    EditorActions.addBindableEditorAction(ACTION_LineCommentHandler);
  }

  public static EditorAction ACTION_LineCommentHandler =
    // The "line_comment_handler" name is used in the
    // keymap editor to name our action.
    new LineCommentHandlerAction("line_comment_handler");

  public LineCommentHandlerAction(String nm) {
    super(nm);
    // The long description property is used in the keymap editor
    // to fill up the description memo.
    this.putValue(Action.LONG_DESCRIPTION,
    "Adds //DBG at the start of the line if it's not already there, otherwise deletes it."
    );
    // The ActionGroup property is used to put this action
    // in the specified group of actions.
    this.putValue("ActionGroup", "Miscellaneous");
  }

The text utilities

TextUtilities is another useful class. It has methods that you can use to move from a document index to a particular line or to find the beginning and end of words. If the text has hard-coded tab characters, methods exist that compute the width of the text, or that determine the index of a character in the text, taking tabs into account.