JBuilder MessageView concepts

General description

The MessageView is an IDE panel called the message pane that appears only as it is needed. Because there are multiple subsystems within the IDE that use the message pane, a tabbed interface separates the subsystems. There is one MessageView for each Browser instance. You can't replace a MessageView using the OpenTools API.

The MessageCategory class defines a MessageView tab. When we speak of a tab in the message pane, we are referring to the tab and the page it displays as a unit. A MessageCategory routes Message objects to the correct tab as messages are added to the message pane. Usually the tab is the parent of a JComponent that displays a tree, but you can provide a custom component. In fact, the run/debug console in JBuilder is a custom component.

Each Message object displays using its defined text, background color, foreground color, font, icon, context Action, and tooltip attributes. To add a message to the MessageView, call one of the several variants of the addMessage() method, specifying the appropriate MessageCategory.

Despite the appearance of being a text control, the MessageView actually contains a JTree allowing you to optionally generate a hierarchical view by specifying a parent Message. (This also means that you should not send extremely long blocks of text with embedded carriage control or tab characters since they will not display as they would in a text control.)

The Message class accepts event notifications. When the user single-clicks a message in the message pane, the selectAction() method is called. A double-click calls the messageAction() method, which is the trigger of the event. If the user presses F1 while a message is selected in the message pane, the helpAction() method is called. Implementing these events is optional.


Detailed description of feature/subsystem

To get access to the MessageView, call the Browser getMessageView() method. The Browser also provides the isMessagePaneVisible() and setMessagePaneVisible() methods that are called when the user selects the View|Messages menu command.

Creating a MessageView tab

A MessageView tab is defined from a MessageCategory object. You can choose from several possible constructors. The simplest one requires just the initial text for the tab, such as this example:
  final static MessageCategory MYMESSAGES = new MessageCategory("MyMessages");

The MessageCategory is always passed as a parameter in either one of the many variants of the addMessage() method or in the addCustomTab() method. If the passed MessageCategory describes a tab that doesn't yet exist, then a new tab is created before the message is added to it. For example, this code adds the new tab labeled "MyMessages" to the MessageView and adds the message that displays "This is a test.":

  final static MessageCategory MYMESSAGES = new MessageCategory("MyMessages");
  Browser.getMessageView().addMessage(MYMESSAGES, new Message("This is a test."));
If you have created a custom component to display the tab content, that component supplies all the UI inside that tab. Therefore, calling the MessageView addMessage() method would result in an exception. To create a custom tab, call the MessageView addCustomTab() method, passing to it the MessageCategory and the custom component. For example,
  JComponent c = new MyCustomPanel();
  Browser.getMessageView().addCustomTab(MYMESSAGES, c);

Using a MessageView tab

Unless you're using a custom tab component, tab content consists of a JTree inside a JScrollPane. You can create a message hierarchy with code like this:
  MessageView mv = Browser.getActiveBrowser().getMessageView();
  Message msg = mv.addMessage(MYMESSAGES, new Message("This is test."));
  mv.addMessage(MYMESSAGES, msg, new Message("This is a child of test."));

If messages are added to the MessageView with addMessage() calls that don't supply a parent Message, the tree appears flat like a text area.

MessageView provides several UpdateAction and BrowserAction objects that are tied to the user interface. Some apply against a particular Message object (expand, next, prior), a particular tab (clear, copy content, remove), and against all tabs (hide, remove all).

ACTION_CopyContent attempts to work against tab content in a way that might also work with a custom component. It assumes that within the component hierarchy there exists either a JTree or a JTextArea. When it is invoked with a keystroke or a click on a context menu, it tries to identify the key component and copies the selected portion (or if none is selected, then the entire content) to the clipboard.

MessageView calls the MessageCategory methods categoryActivated(), categoryDeactivated(), and categoryClosing() at the appropriate times. You might find them useful when you are implementing a custom tab. You can also use a PropertyChangeListener class for notification if the icon, tooltip, or title of the MessageCategory changes.

Customizing messages

Each Message object represents a single message line in the MessageView. Its text is displayed using any font, background color, and/or foreground color settings it defines. (Font changes are currently disabled here and elsewhere in the IDE.)

If you are using a message hierarchy, you might want to take advantage of the setLazyFetchChildren() and fetchChildren() methods. They allow you to create a parent Message yet not supply its children until later when the user decides to expand it. Here's an example that shows you how to do it:

  public class ParentMessage extends Message {
    public ParentMessage() {
      setLazyFetchChildren(true);
    }
    public void fetchChildren(Browser browser) {
      MessageView mv = browser.getMessageView();
      mv.addMessage(MYMESSAGES, this, new Message("This is my child."));
    }
  }

If the user selects a Message by using the keyboard arrow keys to navigate through the message tree or by single-clicking the message, then either the selectAction() method or an Action specified by setSelectAction() method is called. If this is the type of interface you want, you should highlight the UI element that is associated with that Message.

If the user triggers a Message by using the Enter key or double-clicking, then either messageAction() or an Action specified by setMessageAction() is called. If this is the type of interface you want, you should move the focus to the UI element that is associated with that Message.

This example assumes that when the Message was created, it was associated with a known TextFileNode and a particular location in that file. (Refer to LineMark and EditorPane for more details about how this works.)

    TextFileNode fileNode;
    int line;
    int column;
    static final LineMark MARK = new HighlightMark();

    public void selectAction(Browser browser) {
      displayResult(browser, false);
    }

    public void messageAction(Browser browser) {
      displayResult(browser, true);
    }

    private void displayResult(Browser browser, boolean requestFocus) {
      try {
        if (requestFocus || browser.isOpenNode(fileNode)) {
          browser.setActiveNode(fileNode, requestFocus);
          TextNodeViewer viewer = (TextNodeViewer)browser.getViewerOfType(fileNode, TextNodeViewer.class);
          browser.setActiveViewer(fileNode, viewer, requestFocus);
          EditorPane editor = viewer.getEditor();
          editor.gotoPosition(line, column, false, EditorPane.CENTER_IF_NEAR_EDGE);
          if (requestFocus)
            editor.requestFocus();
          else
            editor.setTemporaryMark(line, MARK);
        }
      }
      catch (Exception ex) {
        ex.printStackTrace();
      }

    public static class HighlightMark extends LineMark {
      static Style highlightStyle;
      static {
        StyleContext context = EditorManager.getStyleContext();
        highlightStyle = context.addStyle("line_highlight", null);
        highlightStyle.addAttribute(MasterStyleContext.DISPLAY_NAME, "Line highlight");
        StyleConstants.setBackground(highlightStyle, Color.yellow);
        StyleConstants.setForeground(highlightStyle, Color.black);
      }

      public HighlightMark() {
        super(highlightStyle);
      }
    }

If the user presses F1 when the Message has the focus, its helpAction() method is called if no Action was supplied by setHelpAction().

Each Message can supply an Action or ActionGroup through the getContextAction() method so that a pop-up menu appears if the user right-clicks it.

If you want a Message to appear as something other than a text string, specify a TreeCellRenderer using the setCellRenderer() method.