Netbeans Platform Options Dialog

September 5th, 2007 | by Tonny Kohar |

If you build an application on top of Netbeans Platform and wondering how to create the custom options or preferences panel (Menu Tool - Options), then this tutorial is for you.

This tutorial based on Sketsa SVG Graphics Editor that create custom Options Dialog for storing canvas related thing in Netbeans Platform based application.

Note: this example require Netbeans 6 due to the use of NbPreferences

Create Singleton Options Class
The first step is to create Singleton Option Class with NbPreferences as the backing store. It is basically just getters and setters for the things you want to store eg: grid color, selection color, grid size, etc. Please note do not forget to call firePropertyChange evertime the value is changed, as it is required in later step (the controller know that the value is change and could update the GUI)

/**
 * Abstract Preferences Option. 
 * if nodeName is null, it will use DEFAULT_NODE_NAME
 * It is using NbPreferences as the backing store.
 */
public abstract class AbstractOptions implements Options {
    protected static String DEFAULT_NODE_NAME = "prefs";
    protected String nodeName = null;
 
    private EventListenerList listenerList;
 
    public AbstractOptions() {
        listenerList = new EventListenerList();
    }
 
    /** {@inheritDoc} */
    public void addPropertyChangeListener(PropertyChangeListener listener) {
        listenerList.add(PropertyChangeListener.class, listener);
    }
 
    /** {@inheritDoc} */
    public void removePropertyChangeListener(PropertyChangeListener listener) {
        listenerList.remove(PropertyChangeListener.class, listener);
    }
 
    protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();
        // Process the listeners last to first, notifying
        // those that are interested in this event
        PropertyChangeEvent event = new PropertyChangeEvent(this, propertyName, oldValue, newValue);
        for (int i = listeners.length - 2; i >= 0; i -= 2) {
            if (listeners[i] == PropertyChangeListener.class) {
                ((PropertyChangeListener) listeners[i+1]).propertyChange(event);
            }
        }
    }
 
    /** Return the backing store Preferences
     * @return Preferences
     */
    protected final Preferences getPreferences() {
        String name = DEFAULT_NODE_NAME;
        if (nodeName != null) {
            name = nodeName;
        }
 
        Preferences prefs = NbPreferences.forModule(this.getClass()).node("options").node(name);
 
        return prefs;
    }
}
public class CanvasOptions extends AbstractOptions {
 
    private static CanvasOptions instance; // The single instance
    static {
        instance = new CanvasOptions();
    }
 
    public static final String SELECTION_COLOR_PROPERTY = "selection_color";
    public static final String CANVAS_ANTI_ALIAS_PROPERTY = "canvas_anti_alias";
    public static final String GRID_COLOR_PROPERTY = "grid_color";
    public static final String GRID_SIZE_PROPERTY = "grid_size";
    public static final String GRID_STYLE_PROPERTY = "grid_style";
 
    private Color selectionColor;
    private boolean canvasAntiAlias;
    private Color gridColor;
    private int gridSize;
    private int gridStyle;
 
    /**
     * Returns the single instance, creating one if it's the
     * first time this method is called.
     *
     * @return The single instance.
     */
    public static CanvasOptions getInstance() {
        return instance;
    }
 
    /** Creates a new instance of Preferences */
    protected CanvasOptions() {
        nodeName = "canvas";
 
        Preferences prefs = getPreferences();
 
        String value;
 
        value = prefs.get(SELECTION_COLOR_PROPERTY, null);
        selectionColor = OptionsUtilities.stringToColor(value, Color.BLUE);
 
        canvasAntiAlias = prefs.getBoolean(CANVAS_ANTI_ALIAS_PROPERTY, true);
 
        value = prefs.get(GRID_COLOR_PROPERTY, null);
        gridColor = OptionsUtilities.stringToColor(value, Color.GRAY);
        int gridColorAlpha = 128;
        if (gridColor.getAlpha() != 128) {
            gridColor = new Color(gridColor.getRed(), gridColor.getGreen(), gridColor.getBlue(), gridColorAlpha); // set the alpha
        }
 
        gridSize = prefs.getInt(GRID_SIZE_PROPERTY, 50);
 
        gridStyle = prefs.getInt(GRID_STYLE_PROPERTY, GRID_STYLE_LINE);
    }
 
    public void setSelectionColor(Color selectionColor) {
        Color old = this.selectionColor;
        this.selectionColor = selectionColor;
        firePropertyChange(SELECTION_COLOR_PROPERTY, old, selectionColor);
        getPreferences().put(SELECTION_COLOR_PROPERTY, OptionsUtilities.colorToString(this.selectionColor));
    }
 
    public Color getSelectionColor() {
        return selectionColor;
    }
 
    public void setCanvasAntiAliased(boolean antiAlias) {
        boolean old = this.canvasAntiAlias;
        this.canvasAntiAlias = antiAlias;
        firePropertyChange(CANVAS_ANTI_ALIAS_PROPERTY, old, this.canvasAntiAlias);
        getPreferences().putBoolean(CANVAS_ANTI_ALIAS_PROPERTY, this.canvasAntiAlias);
    }
 
    public boolean isCanvasAntiAliased() {
        return canvasAntiAlias;
    }
 
    /* .. skip the rest as it is basically other getter and setter as reguired, 
     * do not forget to call firePropertyChange everytime the value is change 
     */
}

Create the Option GUI as usual either using matisse or handcoded
In this step create the Option GUI which contains combobox, textbox, radioBox, etc as required. You could create this GUI using Matisse GUI builder or handcoded. Just do not forget to add 2 methods as below

public class CanvasOptionsPane extends JPanel {
...
    public void refreshOptions() {
        // acquire Canvas Prefs
        CanvasOptions canvasOpts = CanvasOptions.getInstance();
        // update the GUI with value from the Canvas Options eg:
        // if (canvasOpts.isCanvasAntiAliased()) {
        //    canvasAntiAliasTrueRadio.setSelected(true);
        // } else {
        //    canvasAntiAliasFalseRadio.setSelected(true);
        // }
    }
 
    public void applyChanges() {
        // acquire Canvas Prefs
        CanvasOptions canvasOpts = CanvasOptions.getInstance();
        // Update the Canvas Option with value from the GUI eg:
        // canvasOpts.setCanvasAntiAliased(canvasAntiAliasTrueRadio.isSelected());
    }
}

Create OptionsPanelController
In this step, we will create the options panel controller, this controller act as a bridge between our singleton Option object and the Option GUI (as previously described). Note the function getComponent(Lookup masterLookup) should return the Option GUI that we created earlier, so Netbeans Platform will know which GUI to display when the category is selected.

public class CanvasOptionsPanelController extends OptionsPanelController {
 
    private boolean changed;
    private CanvasOptionsPane canvasPane;
 
    public GeneralOptionsPanelController() {
        canvasPane = new CanvasOptionsPane();
        changed = false;
 
        Options opts = CanvasOptions.getInstance();
        opts.addPropertyChangeListener(new PropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent evt) {
                changed = true;
            }
        });
    }
 
    /** {@inheritDoc} */
    public void update() {
        optionsPane.refreshOptions();
    }
 
    /** {@inheritDoc} */
    public void applyChanges() {
        optionsPane.applyChanges();
        changed = false;
    }
 
    /** {@inheritDoc} */
    public void cancel() {
        // do nothing
    }
 
    /** {@inheritDoc} */
    public boolean isValid() {
        return true;
    }
 
    /** {@inheritDoc} */
    public boolean isChanged() {
        return changed;
    }
 
    /** {@inheritDoc} */
    public JComponent getComponent(Lookup masterLookup) {
        return optionsPane;
    }
}

Create OptionsCategory
Finally, we need to create the Options Category, which tell Netbeans Platform regarding the icon and title name. Note, the function create() should return the CanvasOptionsPanelController that we created earlier.

public class GeneralOptionsCategory extends OptionsCategory {
 
    private String categoryName;
    private Icon icon;
 
    /** Creates a new instance of GeneralOptionsCategory */
    public GeneralOptionsCategory() {
        categoryName = NbBundle.getMessage(GeneralOptionsCategory.class, "CTL_GeneralOptionsCategory");
 
        String iconPath = NbBundle.getMessage(GeneralOptionsCategory.class, "ICON_GeneralOptionsCategory");
        try {
            icon = new ImageIcon(Utilities.loadImage(iconPath, true));
        } catch (Exception ex) {
            // do nothing
        }
    }
 
    /** {@inheritDoc} */
    public String getCategoryName() {
        return categoryName;
    }
 
    /** {@inheritDoc} */
    public String getTitle() {
        return getCategoryName();
    }
 
    /** {@inheritDoc} */
    public Icon getIcon() {
        return icon;
    }
 
    /** {@inheritDoc} */
    public OptionsPanelController create() {
        CanvasOptionsPanelController controller = new CanvasOptionsPanelController();
        return controller;
    }
}

Register in the layer.xml
As usual, register the Canvas options category in your layer.xml file.

<filesystem>
    ...
    <folder name="OptionsDialog">
        <attr name="SystemFileSystem.localizingBundle" stringvalue="kiyut.sketsa.Bundle"/>
        <file name="CanvasOptionsCategory.instance">
            <attr name="instanceClass" stringvalue="kiyut.sketsa.options.CanvasOptionsCategory"/>
            <attr name="position" intvalue="100"/>
        </file>
    </folder>
    ...
</filesystem>

You must be logged in to post a comment.