© Ioannis Kostaras, Constantin Drabo, Josh Juneau, Sven Reimers, Mario Schröder, Geertjan Wielenga 2020
I. Kostaras et al.Pro Apache NetBeanshttps://doi.org/10.1007/978-1-4842-5370-0_10

10. Learning the Extras of the Platform

Ioannis Kostaras1 , Constantin Drabo2, Josh Juneau3, Sven Reimers4, Mario Schröder5 and Geertjan Wielenga6
(1)
The Hague, South Holland, The Netherlands
(2)
Ouagadougou, Burkina Faso
(3)
Chicago, IL, USA
(4)
Salem, Germany
(5)
Berlin, Germany
(6)
Amsterdam, The Netherlands
 
In this chapter we will learn some extra features provided by the NetBeans RCP that might be useful to your applications:
  • the Dialogs API including the Wizard API

  • the Visual Library and the Palette

  • the Status Bar and Notifications

  • How to use the progress bar

  • QuickSearch and the Output window

  • How to define and persist the settings of your application in the Options window

  • How to brand and distribute your application

Let’s get started.

Dialogs API

The Dialogs API is similar to javax.swing.JOptionPane and lets you create and display dialogs and wizards in an easy way. One can display predefined dialogs, custom dialogs, and multi-step wizards.

Displaying a dialog requires two steps: creating a NotifyDescriptor to configure the dialog, and a DialogDisplayer to display it. Before you use it, add a dependency to the Dialogs API module.

Predefined Dialogs

NetBeans includes various predefined dialogs for common use cases such as displaying messages to users. Listing 10-1 invokes a dialog that displays a message along with an OK button.
NotifyDescriptor d = new NotifyDescriptor.Message("Message");
DialogDisplayer.getDefault().notify(d);
Listing 10-1

Display a message dialog using the Dialogs API

The result is shown in Figure 10-1. The commands from Listing 10-1 are equivalent to JOptionPane.showMessageDialog(...).
../images/479166_1_En_10_Chapter/479166_1_En_10_Fig1_HTML.jpg
Figure 10-1

Simple information message dialog

Listing 10-2, on the other hand, displays the confirmation dialog shown in Figure 10-2.
NotifyDescriptor d = new NotifyDescriptor.Confirmation(
      "Message", "Title");
Object retVal = DialogDisplayer.getDefault().notify(d);
if (retVal == NotifyDescriptor.YES_OPTION) {
    // do something
}
Listing 10-2

Display a confirmation dialog using the Dialogs API

These commands are equivalent to JOptionPane.showConfirmDialog(...).
../images/479166_1_En_10_Chapter/479166_1_En_10_Fig2_HTML.jpg
Figure 10-2

Confirmation dialog

You can compare retVal to the following constant values of NotifyDescriptor:
  • OK_OPTION: The OK button was clicked.

  • YES_OPTION: The Yes button was clicked.

  • NO_OPTION: The No button was clicked.

  • CANCEL_OPTION: The Cancel button was clicked.

  • CLOSED_OPTION: The dialog was closed without any button having been pressed.

Listing 10-3 displays the input dialog of Figure 10-3.
NotifyDescriptor d = new NotifyDescriptor.InputLine(
      "Input:", "Title");
Object retVal = DialogDisplayer.getDefault().notify(d);
if (retVal == NotifyDescriptor.OK_OPTION) {
    String text =
              ((NotifyDescriptor.InputLine) d).getInputText();
}
Listing 10-3

Display an input dialog using the Dialogs API

These commands are equivalent to JOptionPane.showInputDialog(...).
../images/479166_1_En_10_Chapter/479166_1_En_10_Fig3_HTML.jpg
Figure 10-3

Input dialog

One can also use the NotifyDescriptor class to define the properties of a standard dialog as shown in Listing 10-4.
NotifyDescriptor d = new NotifyDescriptor(
      "Message", // message
      "Title", // title
      NotifyDescriptor. DEFAULT_OPTION, // option type
      NotifyDescriptor.ERROR_MESSAGE, // message type
      null, // custom buttons (as Object[])
      null); // default value
DialogDisplayer.getDefault().notify(d);
Listing 10-4

Display an error dialog using the Dialogs API

The result is shown in Figure 10-4. These commands are equivalent to JOptionPane.showOptionDialog(...).
../images/479166_1_En_10_Chapter/479166_1_En_10_Fig4_HTML.jpg
Figure 10-4

Error message dialog

Option type (3rd parameter) can take one of the following values:
  • NotifyDescriptor.DEFAULT_OPTION

  • NotifyDescriptor.OK_CANCEL_OPTION

  • NotifyDescriptor.YES_NO_OPTION

  • NotifyDescriptor.YES_NO_CANCEL_OPTION

Message type (4th parameter) can take one of the following values, which you may also pass as the 2nd parameter to NotifyDescriptor.Message() described in Listing 10-1:
  • NotifyDescriptor.PLAIN_MESSAGE

  • NotifyDescriptor.INFORMATION_MESSAGE

  • NotifyDescriptor.QUESTION_MESSAGE

  • NotifyDescriptor.WARNING_MESSAGE

  • NotifyDescriptor.ERROR_MESSAGE

The 5th parameter can be used to pass in an Object array to add custom buttons to the dialog. Arrays of type String, Component, or Icon can be used. Listing 10-5 provides an example.
String[] buttons = new String[3];
buttons[0] = "Play";
buttons[1] = "Pause";
buttons[2] = "Stop";
NotifyDescriptor d = new NotifyDescriptor(
      "Message", // message
      "Title", // title
      NotifyDescriptor.DEFAULT_OPTION, // option type
      NotifyDescriptor.PLAIN_MESSAGE, // message type
      buttons, // custom buttons (as Object[])
      "Play"); // default value
DialogDisplayer.getDefault().notify(d);
Listing 10-5

Display a dialog with custom buttons using the Dialogs API

The resulting dialog is shown in Figure 10-5.
../images/479166_1_En_10_Chapter/479166_1_En_10_Fig5_HTML.jpg
Figure 10-5

Dialog with custom buttons

As an exercise, replace JOptionPane instances with the Dialogs API in the TodoRCP application of the previous chapter.

Custom Dialogs

One can also create custom dialogs via the DialogDescriptor class that extends NotifyDescriptor. An example is shown in Listing 10-6, which displays a custom Login dialog (designed in LoginPanel JPanel class).
DialogDescriptor d = new DialogDescriptor(
      new LoginPanel(), // Component
      "Login",          // title
      true,             // modality
      null);            // ActionListener
DialogDisplayer.getDefault().createDialog(d).setVisible(true);
Listing 10-6

Display a custom dialog using the Dialogs API

In short, you can pass any kind of java.awt.Component as the first parameter. As an example, let’s extend our TodoRCP application by providing a Login dialog asking for credentials when the application starts, at which point the application should be blocked until the user has been authenticated.

Securing Access

We don’t want anybody to be able to access our tasks. We need to secure them by asking for credentials to access our TodoRCP application. Here are the steps to follow in order to display a Login dialog on application start-up:
  1. 1.

    Create a new module, as explained in Chapter 7, making sure to add it to the TodoRCP module suite. Name the module Login and give it the Code Base Name todo.login.

     
  2. 2.

    Open the Login module, if not already open; right-click on the todo.login package; select New ➤ Other from the pop-up menu to display the New File dialog; select the category Swing GUI Forms; and select the JPanel Form file type. Click Next.

     
  3. 3.

    In Step 2, enter LoginPanel in the Class Name field and click Finish.

     
  4. 4.
    Create the login form shown in Figure 10-6 using:
    • A label for Username: lblUsername

    • A label for Password: lblPassword

    • A label for the error message displayed on the bottom (shown as a red rectangle in Figure 10-6 but will be invisible on your form): lblInfo

    • A text field for Username: txtUsername

    • A password field for Password: txtPassword

     
../images/479166_1_En_10_Chapter/479166_1_En_10_Fig6_HTML.jpg
Figure 10-6

LoginPanel

  1. 5.

    Switch to Source view in the editor window, and add the following methods in Listing 10-7 at the end of the LoginPanel class

     
public String getUserName() {
    return txtUsername.getText();
}
public char[] getPassword() {
    return txtPassword.getPassword();
}
public void setInfo(String info) {
    lblInfo.setText(info);
}
Listing 10-7

Access methods of the LoginPanel

  1. 6.

    Create a new module installer by right-clicking on the todo.login package and selecting New ➤ Other, then Module Development category followed by Installer / Activator as the file type. Then complete the code as shown in Listing 10-8. Notice that apart from createDialog(), that DialogDisplayer also offers both notify() and notifyLater(). The latter allows displaying the Dialog immediately after the initialization phase, which is directly after the splash screen.

     
public class Installer extends ModuleInstall {
    private LoginPanel loginPanel = new LoginPanel();
    private DialogDescriptor d = null;
    @Override
    public void restored() {
        d = new DialogDescriptor(loginPanel, "Login", true, this);
        d.setClosingOptions(new Object[]{}); // not closeable
        DialogDisplayer.getDefault().notifyLater(d);
    }
Listing 10-8

The Module Installer class allows the login dialog to be displayed on application start-up

DialogDescriptor expects an ActionListener as the constructor’s fourth parameter. Listing 10-9 shows an example of what such an ActionListener would look like.
public class Installer extends ModuleInstall implements ActionListener {
//...
@Override
    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == DialogDescriptor.CANCEL_OPTION) {
            LifecycleManager.getDefault().exit();
        } else if (!validate(loginPanel.getUserName(), loginPanel.getPassword())) {
            loginPanel.setInfo("Wrong Username or Password");
        } else {
            d.setClosingOptions(null);// can close
        }
    }
}
Listing 10-9

Module Installer actionPerformed() method implementation

The validate(String username, char[] password) method checks in the database whether the provided credentials are valid. You may find its implementation in the source code of the book.

If you play with the login dialog a bit, you will notice that you can close the login window overriding authentication by clicking on the X close button of the window. We can fix that by adding the code shown in Listing 10-10 to the constructor of the LoginPanel.
// if someone simply closes the dialog using the X close button
d.addPropertyChangeListener(new PropertyChangeListener() {
   @Override
   public void propertyChange(PropertyChangeEvent e) {
      if (e.getPropertyName().equals(DialogDescriptor.PROP_VALUE)
          && e.getNewValue() == DialogDescriptor.CLOSED_OPTION) {
             LifecycleManager.getDefault().exit();
      }
   }
});
Listing 10-10

Disallow closing the dialog from the X close button

Another way is to create a Java class that implements Runnable and annotate it with @OnStart (which also requires a dependency to the Module System API). Listing 10-11 is left as an exercise to the user.
@OnStart
public class LoginInstaller implements Runnable, ActionListener {
   private final LoginPanel loginPanel = new LoginPanel();
   private DialogDescriptor d = null;
   @Override
   public void run() {
        // same as restored() method (Listing 10-8)
   }
   // code from Listings 10-9, 10-10 goes here
}
Listing 10-11

A module installer using the @OnStart annotation

Wizards

You have already encountered how to use the NetBeans wizards (or multi-pane dialogs) by creating a new File or Project from the File menu of the IDE. The good news is that you can create your own wizards for your projects by using the Dialogs API. Additionally, NetBeans provides a wizard for creating wizards. To use it, right-click on your module, choose New ➤ Other, then choose Module Development category followed by Wizard file type, and click Next.

In the second step of the wizard (see Figure 10-7), you have a number of choices. The registration types New File with Swing UI, New File with HTML/Java UI and New File with HTML/JS UI will register a file type in the New File or New Project dialog box of the application under a category of your choice. The Custom registration type won’t, needing to be called programmatically from somewhere in your code, typically from an Action.
../images/479166_1_En_10_Chapter/479166_1_En_10_Fig7_HTML.jpg
Figure 10-7

New Wizard wizard

For custom wizards, you can choose if the wizard should pass through a sequence of separate steps in a linear fashion that are fixed and don’t change (Static), or dynamically based on the choices made by the user (i.e., the user will be able to skip some steps). The first option creates code that lets the user pass through the sequence of panels forward and backward without branching off or skipping panels. The second creates code that, by default, gives you full control over the order of the panels and allows a dynamic ordering of the panels to be created.

Static Wizards

Let’s create a custom static wizard first with three panels. Clicking Next in the Wizard Type dialog brings you to the Name and Location dialog. Set the Class Name Prefix to AddTask in Step 2 and click Finish. You end up with the following files:
  • AddTaskWizardAction: the action to invoke the wizard. You need to comment-in the annotations on top in order to register the action to a menu or a toolbar. Listing 10-12 shows these annotations.

@ActionID(category="...",   id="todo.view.wizard.AddTaskWizardAction")
@ActionRegistration(displayName="Open AddTask Wizard")
@ActionReference(path="Menu/Tools", position=...)
Listing 10-12

Annontations of the AddTaskWizardAction that have been commented-in in order to register the action to a menu or toolbar

  • At the end of the actionPerformed() method, you need to gather all data and create/update your model. For an example, see Listing 10-13.

if (DialogDisplayer.getDefault().notify(wiz) ==
     WizardDescriptor.FINISH_OPTION) {
   TaskManagerInterface taskManager =
      Lookup.getDefault().lookup(TaskManagerInterface.class);
   if (taskManager != null) {
     Task task = new Task(
      wizardDescriptor.getProperty(AddTaskConstants.DESCRIPTION),
      ...);
     taskManager.addTask(task);
   }
}
Listing 10-13

actionPerformed() method implementation

  • AddTaskWizardPanel1, AddTaskWizardPanel2, AddTaskWizardPanel3: WizardDescriptor.Panel classes are responsible for controlling each step. They check the validity of entered values (via method isValid()), support context sensitive help (by returning a help context via the getHelp() method), and initialize the visual panels by reading settings (readSettings() method) as well as storing settings (storeSettings() to be read the next time the user visits this panel or by the other panels). Usually you define an interface with some constant property names to be used by these methods as keys to save the properties. If a wizard panel’s valid status depends on user input, the wizard panel must implement addChangeListener() and removeChangeListener() and fire state change events to its ChangeListeners.

  • AddTaskVisualPanel1, AddTaskVisualPanel2, AddTaskVisualPanel3: JPanels for the visual representation of each step.

You may also use the Simple Validation API (ide/modules/ext/ValidationAPI.jar) for your validations. You need to wrap it to a module (e.g., in the Libraries module of the TodoRCP application) and make org.netbeans.validation.api public. You may also add ValidationPanel to the palette to use it to your panels (with the help of the Palette Manager). The API provides a number of useful validators: REQUIRE_NON_EMPTY_STRING, NO_WHITESPACE, REQUIRE_VALID_INTEGER, REQUIRE_NON_NEGATIVE_NUMBER, numberRange(minValue, maxValue), EMAIL_ADDRESS, FILE_MUST_EXIST, URL_MUST_BE_VALID, REQUIRE_JAVA_IDENTIFIER, REQUIRE_VALID_FILENAME. You may create your own, too, if you are not satisfied with them. Once you have dropped a ValidationPanel to your JPanels, you can validate the input there (and not in the WizardDescriptor.Panel) by calling group = validationPanel1.getValidationGroup() and then group.add()adding one or more of the validators above.

Dynamic Wizards

In the case of a dynamic wizard, an AddTaskWizardIterator (of type WizardDescriptor.Iterator) is created instead of an AddTaskWizardAction (Listing 10-14).
public final class AddTaskWizardIterator implements WizardDescriptor.Iterator<WizardDescriptor> {
    // Example of invoking this wizard:
    // @ActionID(category="...", id="...")
    // @ActionRegistration(displayName="...")
    // @ActionReference(path="Menu/...")
    // public static ActionListener run() {
    //   return new ActionListener() {
    //     @Override public void actionPerformed(ActionEvent e) {
Listing 10-14

A dynamic wizard

The navigation is done with the help of the following methods (Listing 10-15) that update the index accordingly: current(), hasNext(), hasPrevious(), nextPanel(), previousPanel(). The New File with Swing UI option registers the wizard to a file type, as explained earlier.
@TemplateRegistration(
   folder = "Other",
   displayName = "#AddTaskWizardIterator_displayName",
   iconBase = "todo/view/wizard/file/addtsk_tsk.gif",
   description = "addTask.html")
@Messages("AddTaskWizardIterator_displayName=Create New Task")
public final class AddTaskWizardIterator implements WizardDescriptor.InstantiatingIterator<WizardDescriptor> {
Listing 10-15

Registration of the dynamic wizard to a file type

During execution, an entry to execute the above wizard can be found in the File ➤ New dialog wizard inside category Other. To register the dynamic wizard that you created earlier to a category of the File ➤ Project dialog wizard, enter, for example, folder = "Projects/Other".

Instead of InstantiatingIterator , the AddTaskWizardIterator may also extend AsynchronousInstantiatingIterator (the instantiate() method is invoked outside the EDT), BackgroundInstantiatingIterator (the wizard closes and the instantiate() method is invoked in a background thread), or ProgressInstantiatingIterator (the instantiate() method is invoked outside the EDT and it is called with a ProgressHandle to display its progress in a progress bar, which is described later in this chapter).

WizardPanel, instead of WizardDescriptor.Panel, may also extend WizardDescriptor.ValidatingPanel (for additional validation when the Next or Finish button is clicked), WizardDescriptor.AsynchronousValidatingPanel (for asynchronous validation), WizardDescriptor.ExtendedAsynchronousValidatingPanel (prefer this to AsynchronousValidatingPanel), or WizardDescriptor.FinishablePanel (when Finish button needs to be dynamically enabled; this allows the user to click the Finish button before the final step, because for example s/he accepts the default values of the following steps).

Other Properties of Wizards

When using the custom wizard, you can use some properties of WizardDescriptor:
  • PROP_AUTO_WIZARD_STYLE: set to true to enable using the other properties.

  • PROP_CONTENT_BACK_COLOR: set background color of left pane.

  • PROP_CONTENT_DATA: sets the array of content items.

  • PROP_CONTENT_DISPLAYED: set to true for displaying the left pane.

  • PROP_CONTENT_FOREGROUND_COLOR: set foreground color of left pane.

  • PROP_CONTENT_NUMBERED: set to true for displaying numbers in the left pane.

  • PROP_CONTENT_SELECTED_INDEX: represents index of left pane that will be highlighted.

  • PROP_ERROR_MESSAGE: error message displayed at bottom of the wizard. The wizard’s Next button is disabled when there are errors.

  • PROP_IMAGE: set the image to be displayed in the left pane.

  • PROP_IMAGE_ALIGNMENT: set the image’s alignment.

Finally, regarding the other two options displayed in Figure 10-7, consult the documentation at http://wiki.netbeans.org/HtmlUIForTemplates.

As an exercise, build a custom static wizard for the AddTask action. The wizard will contain three steps, and the first panel will ask for the description and the priority of a task (description is mandatory); the second panel the due date, the alert, and the days before; and the third panel the observations and whether the task is completed or not. Try also to create the same wizard for the TodoFXRCP application. Hint! Use JFXPanels in place of JPanels.

As another exercise, use the New File with HTML/Java UI wizard to create a wizard to add a new task for the TodoHTMLJava application.

Miscellaneous

Here we describe some APIs of the NetBeans Platform that you may find useful in your projects.

Status Bar

In Listing 9-2 of the previous chapter, we saw how to display messages in the status bar by registering to the StatusLineElementProvider service provider interface. If you want to display simple text, use the command of Listing 10-16.
StatusDisplayer.getDefault().setStatusText("Status message").
Listing 10-16

Display a message in the status bar

Notifications

The NetBeans Platform provides a mechanism to display balloon-shaped pop-ups in the far right of the status bar. Use NotificationDisplayer for that purpose, which requires a dependency to the UI Utilities module. Listing 10-17 provides the various possibilities to customize the balloon.
NotificationDisplayer.getDefault().notify(String title, Icon icon, String detailsText, ActionListener detailsAction);
NotificationDisplayer.getDefault().notify(String title, Icon icon, String detailsText, ActionListener detailsAction, Priority priority);
NotificationDisplayer.getDefault().notify(String title, Icon icon, JComponent balloonDetails, JComponent popupDetails, Priority priority);
NotificationDisplayer.getDefault().notify();
Listing 10-17

Display a ballon-shaped pop-up in the status bar

You can also include an icon in the balloon. An example adding an alert ../images/479166_1_En_10_Chapter/479166_1_En_10_Figa_HTML.jpg icon is shown in Listing 10-18.
NotificationDisplayer.getDefault().notify(
   "Alert1",
   ImageUtilities.loadImageIcon("todo/view/illegal.png", false),
   "Alert Details1",
   null);
Listing 10-18

Display a ballon-shaped pop-up with an icon in the status bar

As an exercise, modify TodoRCP to display the alerts at start-up as notifications instead of JOptionPane messages (see Figure 10-8).
../images/479166_1_En_10_Chapter/479166_1_En_10_Fig8_HTML.jpg
Figure 10-8

Notifications

If ActionListener is not null, then a hyperlink is displayed that, when clicked, executes the action. Priority can be: HIGH, NORMAL, LOW, or SILENT.

ImageUtilities

org.openide.util.ImageUtilities is a utility class providing static methods for manipulation and conversion of images and icons. Results are cached. Useful methods include loadImage(), loadImageIcon(), icon2Image(), image2Icon, mergeImages(), etc. We saw an example in Listing 10-18.

Quick Search

Typically, in the top right of the toolbar a search field is displayed (see Figure 10-9). A QuickSearch box searches for keywords you type in its text box.
../images/479166_1_En_10_Chapter/479166_1_En_10_Fig9_HTML.jpg
Figure 10-9

Quick Search in the toolbar

If the search field is not shown automatically, then you need to include the XML tags shown in Listing 10-19 in layer.xml:
<folder name="Toolbars">
   <folder name="QuickSearch">
      <file name="org-netbeans-modules-quicksearch-QuickSearchAction.shadow">
      <attr name="originalFile"
stringvalue="Actions/Edit/org-netbeans-modules-quicksearch-QuickSearchAction.instance"/>
      </file>
   </folder>
</folder>
Listing 10-19

XML tags in layer.xml to display the quick search field in the toolbar

You can choose to display the quick search in the menu bar instead of in the toolbar. When doing that, it is good practice to include a spacer, which ensures that the search field will be right aligned in the menu bar. You can do these things by adding the code from Listing 10-20 to your layer.xml file.
<folder name="Menu">
   <file name="Spacer.instance">
      <attr name="instanceCreate"
methodvalue="javax.swing.Box.createHorizontalGlue"/>
      <attr name="position" intvalue="9005"/>
   </file>
   <file name="org-netbeans-modules-quicksearch-QuickSearchAction.shadow">
      <attr name="originalFile"
stringvalue="Actions/Edit/org-netbeans-modules-quicksearch-QuickSearchAction.instance"/>
      <attr name="position" intvalue="9010"/>
   </file>
</folder>
Listing 10-20

Display the quick search in the menu bar instead of the toolbar

To display the quick search on the menu on Mac OSX, you will need to exclude the Apple Application Menu module from the platform cluster (right-click on the TodoRCP module suite and select Properties, then the Libraries category as shown in Figure 10-10) in order not to use the Apple menu bar instead.
../images/479166_1_En_10_Chapter/479166_1_En_10_Fig10_HTML.jpg
Figure 10-10

Remove Apple Application Menu module from platform cluster

If you need to add the quicksearch filter tool to your own panel/toolbar, then follow the tricks found here: http://layerxml.wordpress.com/2011/02/16/place-the-quicksearch-component-anywhere-you-like/, and also at https://blogs.oracle.com/geertjan/place-the-quicksearch-component-anywhere-you-like-part-2.

You can search for actions, but how can you search for tasks? The answer is by following these steps:
  1. 1.

    Right-click on the View module and select New ➤ Other.

     
  2. 2.

    In the New File dialog, choose Module Development ➤ Quick Search Provider (see Figure 10-11) and click Next.

     
  3. 3.

    In the Quick Search Provider panel, set the provider class name, etc. (see Figure 10-12).

     
  4. 4.

    Click on Finish.

     
../images/479166_1_En_10_Chapter/479166_1_En_10_Fig11_HTML.jpg
Figure 10-11

New Quick Search Provider wizard (step 1)

../images/479166_1_En_10_Chapter/479166_1_En_10_Fig12_HTML.jpg
Figure 10-12

New Quick Search Provider wizard (step 2)

The wizard will create the stub shown in Listing 10-21.
package todo.view;
import org.netbeans.spi.quicksearch.SearchProvider;
import org.netbeans.spi.quicksearch.SearchRequest;
import org.netbeans.spi.quicksearch.SearchResponse;
public class TaskSearchProvider implements SearchProvider {
    @Override
    public void evaluate(SearchRequest request, SearchResponse response) {
        //sample code
        //for (SearchedItem item : getAllItemsToSearchIn()) {
        //    if (isConditionSatisfied(item, request)) {
        //        if (!response.addResult(item.getRunnable(), item.getDisplayName(),
        //        item.getShortcut(), item.getDisplayHint())) {
        //        break;
        //    }
        //    }
        //}
    }
}
Listing 10-21

TaskSearchProvider generated by the Quick Search Provider wizard

The SearchRequest class provides the description of the quick search request. The methods getText() and getShortcut() give you access to the text and shortcut typed into the search field. Typically, the text entered in the search field is matched against an object in the Lookup. When a match succeeds, the object is added to SearchResponse, which is the response object for collecting the results of the SearchRequest and displaying them in the search field drop-down list.

As an example, Listing 10-22 provides an implementation of SearchProvider that finds all Nodes in TasksTopComponent, then searches through their Lookups for Task objects.
@Override
public void evaluate(final SearchRequest request, final SearchResponse response) {
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            TopComponent tc = WindowManager.getDefault().findTopComponent("TasksTopComponent");
            final ExplorerManager em = ((ExplorerManager.Provider) tc).getExplorerManager();
            Node root = em.getRootContext();
            for (final Node node : root.getChildren().getNodes()) {
                Task task = node.getLookup().lookup(Task.class);
               if (accept(task, request)) {
                   response.addResult(new Runnable() {
                        @Override
                       public void run() {
                            try {
                                em.setSelectedNodes(new Node[]{node});
                            } catch (PropertyVetoException ex) {
                                Exceptions.printStackTrace(ex);
                            }
                        }
                    }, task.getDescription());
                }
            }
        }
    });
}
private boolean accept(Task task, SearchRequest request) {
    return task != null && task.getDescription().contains(request.getText());
}
Listing 10-22

Example implementation of SearchProvider

The wizard also creates the code shown in Listing 10-23, placing that code into layer.xml.
<folder name="QuickSearch">
    <folder name="Tasks">
        <attr name="command" stringvalue="t"/>
        <attr name="position" intvalue="0"/>
        <file name="todo-view-TaskSearchProvider.instance">
            <attr name="displayName" bundlevalue="todo.view.Bundle#QuickSearch/Tasks/todo-view-TaskSearchProvider.instance"/>
        </file>
    </folder>
</folder>
Listing 10-23

QuickSearch folder generated in layer.xml from the Quick Search Provider wizard

QuickSearch is not to be confused with QuickFilter. Check ShowCompletedTasksAction implementation in the source code bundle to see how to filter the OutlineView (see also https://jnkjava.wordpress.com/2014/02/26/recipe-9-how-to-filter-an-outlineview/). You may also add your own custom quick filter in order to filter the OutlineView by following the instructions described in this blog (https://jnkjava.wordpress.com/2015/02/01/recipe-14-how-to-add-a-quick-filter/).

Output Window

The Output window is a display area, usually at the bottom of the NetBeans IDE window, for showing messages to the user (see Figure 10-13). Messages from multiple sources can be displayed in different tabs simultaneously.

To use the Output window in your application, add a dependency to I/O API and SPI module. An example use case is shown in Listing 10-24.
InputOutput io = IOProvider.getDefault().getIO("Hello World!", true);
io.show();   // display Output window if it is not open
try (OutputWriter writer = io.getOut()) {
    writer.println("This is a message.");
}
Listing 10-24

Output window code example

Passing true as the second parameter to IOProvider.getDefault().getIO() ensures that a new tab is created in the Output window for each call. Pass false to write the output to the same tab. InputOutput.show() ensures that the Output window opens, if it is closed.
../images/479166_1_En_10_Chapter/479166_1_En_10_Fig13_HTML.jpg
Figure 10-13

Output window

Settings

NetBeans IDE provides the Options window (menu Tools ➤ Options or NetBeans ➤ Preferences on MacOS) that allows the user to customize it (see Figure 10-14).
../images/479166_1_En_10_Chapter/479166_1_En_10_Fig14_HTML.jpg
Figure 10-14

Options window

As you can see in Figure 10-14, there are Primary panels (for example General, Miscellaneous) and Secondary panels (for example Formatting, Hints, etc.). The Primary Panels can be used to configure the most important groupings of settings in an application. Secondary Panels can be used for less important settings needed for configuring the application. You can also export and import your settings with the two respective buttons in the bottom left of the Options window.

The Apache NetBeans Platform Options Dialog API and SPI allow modules to customize the Options window. To use this API, right-click on your module, and select New ➤ Other. Select the Module Development category and the Options Panel file type. In the next step, choose among Primary and Secondary panel (see Figure 10-15). The keywords enable the user to quickly open the Options window from the Quick Search field as we learned previously.
../images/479166_1_En_10_Chapter/479166_1_En_10_Fig15_HTML.jpg
Figure 10-15

New Options panel wizard

The wizard will create a package-info.java file with the contents shown in Listing 10-25 for a primary panel.
@OptionsPanelController.ContainerRegistration(
   id = "TaskManager",
   categoryName = "#OptionsCategory_Name_TaskManager",
   iconBase = "todo/view/task_manager.png",
   keywords = "#OptionsCategory_Keywords_TaskManager",
   keywordsCategory = "TaskManager")
@NbBundle.Messages(
   value = {"OptionsCategory_Name_TaskManager=TaskManager", "OptionsCategory_Keywords_TaskManager=task manager"})
package todo.view;
import org.netbeans.spi.options.OptionsPanelController;
import org.openide.util.NbBundle;
Listing 10-25

package-info.java contents

For a secondary panel, the wizard will create a Controller class annotated as shown in Listing 10-26 as well as a JPanel for you.
@OptionsPanelController.SubRegistration(
        location = "TaskManager",
        displayName = "#AdvancedOption_DisplayName_Tasks",
        keywords = "#AdvancedOption_Keywords_Tasks",
        keywordsCategory = "TaskManager/Tasks"
)
@org.openide.util.NbBundle.Messages(
{"AdvancedOption_DisplayName_Tasks=Tasks", "AdvancedOption_Keywords_Tasks=task"})
public final class TasksOptionsPanelController extends OptionsPanelController {
...
Listing 10-26

The TasksOptionsPanelController for the secondary panel

Whatever you set in this panel is persisted (stored) into the NetBeans Platform user directory with the help of org.openide.util.NbPreferences class to be available the next time you open the application.

You can easily locate the NetBeans Platform user directory by displaying the About dialog box from Help ➤ About or NetBeans ➤ About NetBeans (in MacOS) and verifying the User Directory entry (see Figure 10-16).

../images/479166_1_En_10_Chapter/479166_1_En_10_Fig16_HTML.jpg
Figure 10-16

About NetBeans dialog box

Progress

More often than not, when dealing with large amounts of data, the calculation and display of them may take some noticeable time to the user. For example, retrieving data from a filesystem or database can take several seconds on a good day. As a good User Experience rule, the user should be given some visual feedback of the progress that a long task takes.

For example, expanding a Node that creates child elements synchronously in the Event Dispatch Thread (EDT) will freeze the UI. During this time, no UI updates are performed and user gestures are ignored, making the UI unusable for the duration of the process. That is the reason why creating the child elements is done in a different thread. Meanwhile, you should inform the user that child elements are being created and are about to be displayed. During this time, normally a wait cursor and a status message are shown.

In Chapter 8 we saw how ChildFactory class offers support for creating child elements asynchronously (by passing true as the second parameter of the Children.create() method).

By default, the NetBeans Platform status bar has an integrated progress bar. When you are working with long-running tasks, you can plug into the progress bar to let the user visualize progress, either of a single task or multiple tasks.

To plug into the progress bar, you need to add a dependency to the Progress API. The ProgressHandleFactory creates ProgressHandle objects, which display progress and, optionally, enables the user to cancel the progress display. Progress can be displayed with a definite or an indefinite runtime.

For example, the code snippet of Listing 10-27 could be used inside the createKeys() method of TaskChildFactory to display the progress of loading and displaying a large number of tasks in the OutineView of the TodoRCP application.
ProgressHandle handle = ProgressHandleFactory.creatHandle("Tasks progress");
handle.start(100);    // 100 percentiles, i.e. 0-100
...
handle.progress(25);
...
handle.progress(50);
...
handle.progress(75);
...
handle.finish();
Listing 10-27

Example to display the progress of loading a large number of tasks

To handle a long task that spans many modules, use the ProgressContributor as described in the API (http://bits.netbeans.org/dev/javadoc/org-netbeans-api-progress/index.html).

Visual Library

The Visual Library is used to display selectable, movable, and changeable graphic components (called widgets) on a Scene. The Visual Library is based on Java2D and uses a lightweight window system so that it can be used with Swing, JavaFX, etc.

The Visual Library is normally used in combination with the Component Palette, which allows for dragging and dropping items onto components (like Scenes). To open your appetite, you may start your adventure by exploring the Visual Library examples (https://platform.netbeans.org/graph/examples.html) to see the capabilities of the library as well as take a look at some tutorials and real applications that actually use it (https://platform.netbeans.org/graph/).

Before you can use the Visual Library in your projects, you need to add the Visual Library API from the platform cluster, which is not added by default. You do this by right-clicking on your Module suite and selecting Properties as described and illustrated earlier in Figure 10-10.

The main classes of the Visual Library are described in the subsections to follow.

Widgets

Widgets are lightweight graphical components that can be used to draw graphs. As shown in Figure 10-17, Widget follows the Composite design pattern (widgets contain widgets), and the various widgets are organized into a tree hierarchy.
../images/479166_1_En_10_Chapter/479166_1_En_10_Fig17_HTML.jpg
Figure 10-17

Visual Library class diagram

A variety of Widget implementations are provided by the Visual Library (see Figure 10-18), each incorporating predefined behavior consisting of customizable actions, borders, and layouts. Together, Widgets are arranged and displayed in a Scene, of which there are various implementations, too (see Figure 10-17).

Widgets have efficient validation and repaint mechanisms. A widget (as shown in Figure 10-17) has also a state, and that state can take on one of several values: selected, highlighted (also called secondary selection), object-hovered, widget-hovered, widget-aimed.
../images/479166_1_En_10_Chapter/479166_1_En_10_Fig18_HTML.jpg
Figure 10-18

Widget implementations

Finally, Widget has a Lookup, which can be used to add objects to it. Listing 10-28 provides an example of using a Lookup in a Widget.
class MyWidget extends LabelWidget {
    private Lookup lookup;
    private InstanceContent ic;
    public MyWidget(Scene scene) {
        super(scene);
        ic = new InstanceContent();
    }
    @Override
    public Lookup getLookup() {
        if (lookup == null) {
            lookup = new AbstractLookup(ic);
        }
        return lookup;
    }
Listing 10-28

A Widget with a Lookup to store model objects

This Lookup is usually proxied by the TopComponent that contains the visual components, which is in turn proxied by Utilities.actionsGlobalContext(), as we have learned in the previous chapters.

Scene

The Scene (see Figures 10-17 and 10-18) is a kind of Widget, too. It is a top-level Widget (the root element of the hierarchical tree structure) that contains other Widgets. It is usually displayed in a Swing component like, for example, a JScrollPane. There are various types of Scenes as one can see in Figure 10-17. For example, ObjectScene maps a Widget to a data object. GraphScene, GraphPinScene and VMDGraphScene (VMD stands for Visual Mobile Designer) are used to simplify creating graphs.

To create a Scene and add Widgets to it, you typically follow the steps shown in Listing 10-29.
public class TaskViewerTopComponent extends TopComponent {
    public TaskViewerTopComponent() {
        setLayout(new BorderLayout());
        Scene scene = new Scene();
        LayerWidget mainLayer = new LayerWidget(scene);
        LabelWidget labelWidget =
            new LabelWidget(scene, "ATask");
        mainLayer.addChild(labelWidget);
        scene.addChild(mainLayer);
        add(new JScrollPane(scene.createView()), BorderLayout.CENTER);
    }
}
Listing 10-29

Steps to create a Scene

With createView() or createSatelliteView() methods, you create a JComponent that can be embedded in any Swing container. Similarly, with getSceneAnimator() method, you can create a SceneAnimator (which contains a few built-in animations). LayerWidget acts like a JGlassPane and it is typically used as a container for other widgets.

ConnectionWidget

The ConnectionWidget represents a connection between two locations. The locations are resolved by Anchors (see Figure 10-19). The path of the connection is specified by control points, which are resolved by Routers (see Figure 10-20).

A number of anchor types are supported (created by AnchorFactory): center, circular, directional, fixed, or rectangular. Routers (created by RouterFactory) can be orthogonal, direct, or free (DirectRouter is used by default).
../images/479166_1_En_10_Chapter/479166_1_En_10_Fig19_HTML.jpg
Figure 10-19

ConnectionWidget

../images/479166_1_En_10_Chapter/479166_1_En_10_Fig20_HTML.jpg
Figure 10-20

ConnectionWidget example with control points

Borders

Each Widget has a border, which by default is an empty border, provided by the EmptyBorder class. Borders implement the Border interface. Examples of borders are LineBorder, BevelBorder, DashedBorder, ImageBorder, ResizeBorder. Switching to a different border is done via Widget.setBorder() as shown in Listing 10-30. Borders are created via BorderFactory.
widget.setBorder(BorderFactory.createLineBorder(2,Color.BLUE));
Listing 10-30

Define a Border for a Widget

Layouts

A widget can be a container for additional widgets and also has a layout defined and managed by a LayoutManager . The following layout variants are provided:

AbsoluteLayout, FlowLayout, CardLayout, OverlayLayout. Listing 10-31 shows how to assign a layout to a widget using the LayoutFactory.
layerWidget.setLayout(LayoutFactory.createVerticalFlowLayout());
Listing 10-31

Define a layout for a Widget

WidgetAction

The behavior of a Widget is described by its Actions, which are defined by the WidgetAction interface (see Figure 10-21). This interface defines methods called by corresponding events, such as the click of a mouse on the Widget to which the Action is assigned. The Visual Library provides predefined Actions, which can also be customized, for standard functionality, such as moving and editing of Widgets. Examples of Actions include MoveAction, HoverAction, ZoomAction, PanAction.

Actions do not need to be defined and instantiated. Instead, they are assigned to a Widget by the ActionFactory class. To create actions for widgets, use ActionFactory and add them to the widgets with method addAction() (see Listing 10-32). Some ActionFactory methods require a provider (such as the EditAction). Other actions have default providers (such as the MoveAction). If you wish to deviate from the default behavior, though, use another provider.

Available providers: AcceptProvider, ConnectProvider, ContiguousSelectProvider, CycleFocusProvider, EditProvider, HoverProvider, InplaceEditorProvider, MoveControlPointProvider, MoveProvider, PopupMenuProvider, ReconnectProvider, RectangularSelectProvider, ResizeProvider, SelectProvider, TwoStateHoverProvider.

Actions are chained. WidgetAction.Chain class represents a chain of widget actions; it receives user events and forwards them to the related Actions. When you call getActions() on a Widget, you actually get the Chain. Of course, order of actions is important.
labelWidget.getActions().addAction(
    ActionFactory.createMoveAction());
Listing 10-32

Add an action to a Widget

Actions can also be grouped together. This can be done by calling Scene.setActiveTool() method and then using getActions("activeTool") instead of getActions().
../images/479166_1_En_10_Chapter/479166_1_En_10_Fig21_HTML.jpg
Figure 10-21

Widget actions

Palette

You usually use the Visual Library in combination with the Palette to drag Widgets from the Palette to a Scene. The Palette is a TopComponent containing items that can be dragged and dropped onto a component of your choice within any other TopComponent.

The TopComponent publishes a PaletteController in its Lookup, together with Actions that hook into the right-click pop-up menus on categories and items in the Palette. The Palette opens together with the TopComponent, if a PaletteController is found in the Lookup of the TopComponent. The PaletteFactory.createPalette() method returns an implementation of the PaletteController and associates it with the TopComponent by putting it into its Lookup (see Listing 10-33).
associateLookup(Lookups.singleton(
      PaletteFactory.createPalette(...)));
Listing 10-33

Associate the Palette with the TopComponent’s Lookup

By default, when an item is dragged from a Palette, it cannot be dropped. You must explicitly define a component as a drop target; otherwise it cannot be a recipient of a Palette’s drop event. If you wish to combine the Palette with the Visual Library, then the drop target is a Scene.

The code example of Listing 10-34 shows how whenever a drop occurs on the Scene, a new LabelWidget is created. One can configure the Palette by right-clicking on it and selecting the Palette Manager action from the pop-up menu.
scene.getActions().addAction(
    ActionFactory.createAcceptAction(
        new AcceptProvider() {
            @Override
            public ConnectorState isAcceptable(
                Widget widget, Point point, Transferable t) {
                return ConnectorState.ACCEPT;
            }
            @Override
            public void accept(Widget widget, Point point, Transferable t) {
                Node node = NodeTransfer.node(t, NodeTransfer.DND_COPY_OR_MOVE);
                LabelWidget labelWidget =
                   new LabelWidget(scene, node.getDisplayName());
                labelWidget.getActions().addAction(
                                        ActionFactory.createMoveAction());
                labelWidget.setPreferredLocation(point);
                layerWidget.addChild(labelWidget);
            }
        }
    )
);
Listing 10-34

Create a new LabelWidget when drag&drop on a Scene

As an exercise, add a new module (Visual, todo.visual) to TodoRCP that allows the user to drop the Tasks stored in the application from the Palette on a Scene. The user should also be able to connect tasks in order to show dependencies among them.

Branding, Distribution, and Internationalization

What remains to be done is to replace the splash screen and the About box, and you’re almost ready to distribute your application. The following are a few of the things you can do to brand and distribute your application:
  1. 1.

    Right-click your project and select Branding. Here you can select a splash screen and the icon to display when the application is minimized as well as the title to show in the application’s title bar. You can also control the windows behavior of your application (for example, you may disable floating) and finally internationalize the application, that is, change the UI in your preferred language (Internationalization Resource Bundles tab) by translating the resource bundles to that language.

     
  2. 2.

    Right-click your project and select Properties. In the Application category, you can change the application’s name. In the Libraries category, you can choose which clusters and modules to include to improve performance.

     
  3. 3.

    To create a distributable .zip or installer of your application, right-click your project, select Properties, and then the Installer category. Check the platform(s) that you wish to create installer(s) for (such as Windows, Mac OSX, Solaris, and Linux), and click OK. You may also provide a license. Right-click the project again, and select Package as and select one of the available options, such as Installers or Zip distribution.

     

If everything ran smoothly, you created a new dist folder inside your project’s folder, which contains the deployed application. Unzip it, if needed, navigate inside (for example, dist/todorcp/bin) and execute the executable file (depending on your platform, which could be, for example, todorcp.exe or something similar).

You can also allow online updates of your applications’ modules via the Update center, the same way that you download plugins, in order for example to provide bug fixes or new features. To provide this capability for your application, follow these steps:
  1. 1.

    Enable the Auto Update Services and Auto Update UI modules in the platform cluster (see Figure 10-10). After clean and build, verify that you have a Tools ➤ Plugins menu item that opens the Plugin Manager.

     
  2. 2.

    Right-click on the project, and select Package as ➤ NBMs. An .nbm (or NetBeans Module) file is created for each module in the build/updates/ directory. The Update Center descriptor (updates.xml) is also created. You need to publish these files to a web server.

     
  3. 3.

    In the Plugin Manager window, click on Settings tab, then click Add and enter the URL to the Update Center descriptor (updates.xml).

     
  4. 4.
     

Summary

Table 10-1 shows a grouping of modules that provide functionality from the Apache NetBeans Platform. The table is a summary of the provided functionality by that platform. You may use most of the jar files mentioned in the table outside the NetBeans IDE, that is, using another IDE or a text editor to develop an application based on Apache NetBeans RCP.

In the next chapter you will apply what you have learned so far in order to create a plugin for NetBeans.
Table 10-1

Apache NetBeans Platform Modules Overview

Modules grouping

Provided functionality

Core

boot.jar

core.jar

org-openide-filesystems.jar

org-openide-modules.jar

org-openide-util-lookup.jar

org-openide-util.jar

Provide the runtime container.

org-netbeans-modules-masterfs.jar

Provides the central registry file system functionality (a.k.a. layer).

UI

org-netbeans-core.jar

org-netbeans-core-execution.jar

org-netbeans-core-ui.jar

org-netbeans-core-windows.jar

Provide the basic UI components.

org-netbeans-windows.jar

Provides the API for processing the window system.

org-openide-actions.jar

Provides a number of configurable system actions (for example “Cut,” “Copy,” “Paste”).

org-openide-nodes.jar

org-openide-explorer.jar

org-netbeans-swing-outline.jar

Provide the APIs for Nodes and Explorer views, that is, the UI part of the platform.

org-netbeans-core-multiview.jar

Provides the MultiView window.

org-netbeans-swing-plaf.jar

org-netbeans-swing-tabcontrol

org-jdesktop-layout.jar

Provides the look and feel and the display of tabs and a wrapper for the Swing Layout Extensions library.

Extras

org-openide-awt.jar

Provides many helper classes for displaying UI elements (for example notifications, see Figure 10-8).

org-openide-dialogs.jar

Provides an API for displaying standard and customized dialogs and wizards (see beginning of this chapter).

org-netbeans-api-visual.jar

Provides the widget and graph library API for modeling and displaying visual representations of data.

org-netbeans-spi-quicksearch.jar

Provides the API for integrating items into the Quick Search field (see Figure 10-9).

org-netbeans-modules-options-api.jar

Provides the Options window API (see Figure 10-14).

org-netbeans-modules-settings.jar

Provides an API for saving module-specific settings in a user-defined format.

org-netbeans-api-progress.jar

org-openide-execution.jar

org-netbeans-modules-progress-ui.jar

Provides support for asynchronous long-running tasks and integration for long-running tasks with the NetBeans Platform’s progress bar.

org-netbeans-core-output2.jar

org-openide-io.jar

Provides the Output window (see Figure 10-13).

org-netbeans-modules-autoupdate-services.jar

org-netbeans-modules-autoupdate-ui.jar

Provides the update center and plugins functionality.

org-netbeans-modules-favorites.jar

Provides the Favorites window functionality (menu Window ➤ Favorites).

org-openide-loaders.jar

Provides an API to work with file types.

org-netbeans-modules-mimelookup.jar

org-netbeans-modules-editor-mimelookup.jar

Provides an API for discovery and creation of settings specific to file types.

org-netbeans-modules-javahelp.jar

Provides the JavaHelp runtime library that enables JavaHelp sets from different modules to be merged into a single help set.

org-netbeans-modules-queries.jar

Provides an API for getting information about files and an SPI for creating your own queries.

org-netbeans-modules-sendopts.jar

Provides an API and SPI for registering your own handlers for accessing the command line.

org-openide-text.jar

Provides an extension to the javax.swing.text API.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.192.3