JBuilder StructureView concepts

General description

The StructureView is an IDE panel called the structure pane that acts as a container for a JComponent that is optionally provided by the currently active NodeViewer. Usually a tree hierarchy displays the file structure and reports syntax errors.

You can't replace the StructureView with the OpenTools API.


Detailed description of feature/subsystem

Registering a component for the StructureView

When you are defining a file type using the OpenTools API, the object you register with the Browser usually extends the FileNode class, or it extends TextFileNode if the file contains text.

If you extend FileNode, then the createStructureComponent() method of its NodeViewer supplies the Swing-based component for the StructureView. For example,

  public JComponent createStructureComponent() {
    return null;
  }

If you extend TextFileNode, the implementation for a new file type instead customizes the existing JBuilder editor NodeViewer. You do this indirectly by overriding the getTextStructureClass() method of TextFileNode. For example,

  public Class getTextStructureClass() {
    return MyTextStructure.class;
  }

getTextStructureClass() returns your class that extends com.borland.primetime.node.TextStructure and overrides its setTree() method. The JTree parameter of setTree() is the JComponent that is given to the StructureView.

For more information about defining your own file types, see "Implementing FileNode and TextFile Node."

Writing a component for the StructureView

Usually files that aren't text don't provide a structure view so there are no helper classes provided by the OpenTools API at this time. If you plan to use a sorted JTree to provide the structure view, however, you might find this section interesting.

For a TextFileNode, the component provided for the structure view is a JTree that is sorted and with the most commonly needed nodes automatically expanded. The key methods are setTree() and updateStructure().

The updateStructure() method is called automatically whenever the source file is modified. Usually the nodes it adds to the tree contain references to positions in the file that are used by the nodeSelected() method, which scrolls to and points at a line in the source file, and the nodeActivated() method, which moves the focus to the source file line.

The following is a skeleton of a simple implementation. The mergeChildren() method is a helper which tries not to collapse any expanded tree nodes as the tree is being updated.

public class MyTextStructure extends TextStructure {

  public MyTextStructure() {
    treeModel.setRoot(new MyStructureNode(null));
  }

  class MyStructureNode extends MergeTreeNode {
    public MyStructureNode(Object userObject) {
      super(userObject);
    }

    public void sortChildren() {
      MergeTreeNode[] array = getChildrenArray();
      if (array == null)
        return;
      Arrays.sort(array, new Comparator() {
        public int compare(Object o1, Object o2) {
          // Do comparison here between two tree nodes
          return 0;
        }
      });
      children = new Vector(Arrays.asList(array));
      sortDescendants();
    }

    public void sortDescendants() {
      if (children != null) {
        Enumeration e = children.elements();
        while (e.hasMoreElements()) {
          ((MyStructureNode)e.nextElement()).sortChildren();
        }
      }
    }
  }

  private void showProperties() {
    // Show properties here

    // Re-sort everything but the top level of the structure tree
    MyStructureNode root = (MyStructureNode)treeModel.getRoot();
    root.sortChildren();
    treeModel.nodeStructureChanged(root);
  }

  public void setTree(JTree tree) {
    super.setTree(tree);

    TreeNode root = (TreeNode)treeModel.getRoot();
    int count = root.getChildCount();
    for (int index = 0; index < count; index++) {
      TreeNode node = root.getChildAt(index);
      tree.expandPath(new TreePath(new Object[] {root, node}));
    }
  }

  public JPopupMenu getPopup() {
    JPopupMenu menu = new JPopupMenu();
    JMenuItem props = new JMenuItem("Properties...");
    props.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        showProperties();
      }
    });
    menu.add(props);
    return menu;
  }

  public void nodeSelected(DefaultMutableTreeNode node) {
    // handle selection of a tree node here
  }

  public void nodeActivated(DefaultMutableTreeNode node) {
    // handle activation of a tree node here
  }

  public Icon getStructureIcon(Object value) {
    // return Icon appropriate for Object instance
    return null;
  }

  public void updateStructure(Document doc) {

    final MyStructureNode newRoot = new MyStructureNode(null);

    try {

      // Build a new structure tree using newRoot here

      // Prepare an object that updates the model
      Runnable update =
        new Runnable() {
          public void run() {

            MyStructureNode root = (MyStructureNode)treeModel.getRoot();

            // Merge the new model into the old so that expansion paths can be
            // preserved
            root.mergeChildren(newRoot);

            // Sort everything including the top level of the structure tree
            root.sortChildren();

            // Update the display
            treeModel.nodeStructureChanged(root);
          }
        };

      // Update the model on the main swing thread...
      if (SwingUtilities.isEventDispatchThread())
        update.run();
      else
        SwingUtilities.invokeLater(update);

    }
    catch (java.io.IOException ex) {
    }
  }
}