X

Geertjan's Blog

  • July 24, 2007

Communicating with the User (and Yourself)

Geertjan Wielenga
Product Manager
If you use System.out and the JOptionPane to communicate with your users, or with yourself during debugging or testing, you might be interested in knowing about similar (better!) facilities that the NetBeans APIs make available:

  • Class StatusDisplayer. Provided by the UI Utilities API, this class lets you write to the IDE's status bar. I've found this a very simple way of sending messages to myself, because it just requires you to type a single simple (yet powerful) line:

    StatusDisplayer.getDefault().setStatusText("hello world");

    And that's all, nothing more than that, apart from declaring the dependency on UI Utilities API. And now you'll get your message displayed in the IDE's status bar:

  • Class OutputWriter. Provided by the I/O APIs, this class is a subclass of PrintWriter, letting you write to the Output window. You can create a new tab there and then write to it:

    OutputWriter writer;
    InputOutput io = IOProvider.getDefault().getIO("Hello Output", false);
    writer = io.getOut();
    writer.println("hello world");

    The above would give you a "Hello Output" tab with the content of the println statement:

    (Maybe it's just me, but I think that NetBeans IDE 6, thus far, on JDK 6 under Ubuntu Linux doesn't always look very nice by default. The screenshot above is a case in point. Probably I need to experiment with look and feels or something.)

    However, what is interesting about this approach is that you can add a NetBeans API OutputListener to the println, which will result in hyperlinks being created in the Output window. (I've blogged about the OutputListener quite a lot in this blog, so just do a search if you want more info.) So, here's something slightly more complex than the example above:

    OutputWriter writer;
    InputOutput io = IOProvider.getDefault().getIO("Hello World Output", false);
    writer = io.getOut();
    writer.println("Click me: ");
    writer.println(dObj.getPrimaryFile().getPath(), new HelloOutputListener(dObj));

    So, the only difference between the above snippet and the previous snippet is that I now have the path to my file, retrieved from the data object, and an OutputListener. That's a whole separate class, which in this case receives the data object and, when the link in the Output window is clicked, the related file opens:

    class HelloOutputListener implements OutputListener {
    DataObject dObj;
    public HelloOutputListener(DataObject dObj) {
    this.dObj = dObj;
    }//Specify what should happen when the hyperlink is selected:
    public void outputLineSelected(OutputEvent arg0) {
    }//Specify what should happen when the hyperlink is clicked:
    public void outputLineAction(OutputEvent arg0) {
    OpenCookie open = (OpenCookie) dObj.getCookie(OpenCookie.class);
    open.open();
    }//Specify what should happen when the hyperlink is cleared from the Output window:
    public void outputLineCleared(OutputEvent arg0) {
    }
    }

    And here is the result in the Output window which, when clicked, opens the specified file:

  • Class StatusBar. Provided by the Editor Library API (which at some future point may end up getting deprecated in favor of Editor Library 2 API), this class gives you access to an editor's status bar. The editor's status bar consists of cells, some of which are in use by default (which you can also manipulate), plus you can add your own cells to it. A very handy tutorial has been written about this class, by one of our users, and I am hoping it will be released soon. There's a lot you can do with an editor's status bar, pretty much each part of it is customizable. Here's what I've figured out—how to display, in the editor's status bar, the fully qualified file name of the open document:

    Again, be aware that this class is from an API that is on shaky footing, although I hope that whenever it is deprecated it will be replaced by some kind of alternative, but here's the code that makes the above possible:

    //Get the data object:
    DataObject dObj = (DataObject) activatedNodes[0].getCookie(DataObject.class);
    //Get the file:
    File f = FileUtil.toFile(dObj.getPrimaryFile());
    //Get the editor:
    JTextComponent editor = EditorRegistry.lastFocusedComponent();
    //Get editor ui for editor:
    EditorUI editorUI = Utilities.getEditorUI(editor);
    //Get status bar for editor ui:
    StatusBar statusBar = editorUI.getStatusBar();
    //Add label to status bar,
    //giving two strings, the first is the default length of the cell,
    //and the second is the maximum length, which here is the same as the content:

    JLabel cell = statusBar.addCell("My Cell", new java.lang.String[]{f.toString()});
    cell.setText(f.toString());

    There's lots you can do with that status bar. For example, let's add a JTextField instead of a JLabel. This means we must get the panel from the status bar and then we can simply add whatever we like (within reason, given the space that we have available) to that panel:

    JTextField cell = new JTextField();
    cell.setText(f.toString());
    JPanel panel = statusBar.getPanel();
    panel.add(cell);

    And now we have a JTextField instead of a JLabel:

  • Class DialogDisplayer. Any discussion about messaging to users (and yourself) would be incomplete without mentioning the DialogDisplayer class. Normally, implementations of this class are found in close proximity to implementations of the NotifyDescriptor class. For example:

    NotifyDescriptor d = new NotifyDescriptor.Message("Hello...", NotifyDescriptor.INFORMATION_MESSAGE);
    DialogDisplayer.getDefault().notify(d);

    So, what's the difference between DialogDisplayer and JOptionPane? I just phoned up Jarda Tulach to ask him. The answer is: "Better integration with the NetBeans Platform." For example, some actions are automatically disabled when the DialogDisplayer is shown, unlike with JOptionPane which doesn't know anything about the NetBeans Platform, and in addition the open Help window functions better in this case.

So, these are some handy approaches to communicating with the user (and yourself). [In the latter case, i.e., when communicating with yourself, you probably should be using the Debugger or the Profiler, by the way.] Of the above, I use the StatusDisplayer most frequently, because it is just one line and easy to remember. Also, using DialogDisplayer or JOptionPane means you have to click the OK button (potentially two zillion times, which is no fun). In the latter case, using the Output window (or System.out, of course) is a good choice. (By the way, I've found that I can only use System.out on Ubuntu Linux when I start the IDE from a terminal window. Then System.out prints to the terminal window.) The Output window is, of course, especially interesting when you're creating some kind of debugging tool for the user, with error messages appearing in the Output window, which can then be clicked to jump to the place where the error occurred in the editor. Using the editor's status bar is quite fun but, again, remember that the future of that approach is unclear.

Are there other ways of communicating with the user (and yourself)? Maybe you could print to a file outside the IDE, for example. But that's not an approach specific to the NetBeans APIs. You could also print messages to a TopComponent, which wouldn't be hard at all. Just create a TopComponent, add a JTextArea, and then whenever you need to communicate something, just use setText() on the JTextArea. Couldn't be much simpler. Basically you'd then have your own Output window.

In other news. Still investigating the different testing tools, will blog about these soon. But, nothing that I'll write about is not already available right now on the very good http://testtools.netbeans.org/ page.

Join the discussion

Comments ( 2 )
  • Yason Tuesday, July 22, 2008

    Hi. I have a question about StatusDisplayer.getDefault().setStatusText("hello world"); What can I do if StatusBar of main window not visible.


  • Javier Ortiz Tuesday, May 18, 2010

    In my case I'm using the OutputListener but I think I'm filling memory since I'm adding one each 3 seconds (as I receive output from a device). Some of those are duplicate messages. Basically unless something changes they'll be the same (up to 20 duplicaes per minute).

    Any idea on how to reduce the memory consumption?


Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.