blob: 2d479b89ce8a2aa93b6a1daf9eb645797c6e6562 [file] [log] [blame]
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
= NetBeans Wizard Module Tutorial
:jbake-type: platform_tutorial
:jbake-tags: tutorials
:jbake-status: published
:syntax: true
:source-highlighter: pygments
:toc: left
:toc-title:
:icons: font
:experimental:
:description: NetBeans Wizard Module Tutorial - Apache NetBeans
:keywords: Apache NetBeans Platform, Platform Tutorials, NetBeans Wizard Module Tutorial
In this tutorial, you will learn how to use the main features provided by the Wizard classes of the link:http://bits.netbeans.org/dev/javadoc/org-openide-dialogs/org/openide/package-summary.html[NetBeans Dialogs API].
NOTE: This document uses NetBeans Platform 7.1 and NetBeans IDE 7.1. If you are using an earlier version, see link:../70/nbm-wizard.html[the previous version of this document].
In NetBeans Platform applications, many different kinds of wizards can be created. If you want to create a wizard that appears in the New Project dialog, see the link:https://netbeans.apache.org/tutorials/nbm-projectsamples.html[Project Sample Module Tutorial]. If you want to create a wizard that appears in the New File dialog, see the link:https://netbeans.apache.org/tutorials/nbm-filetemplates.html[File Template Module Tutorial]. In this tutorial, you create a general wizard that appears when you click a button in the toolbar.
== Creating the Module Project
We begin by working through the New Module Project wizard. At the end of it, we will have a basic source structure, with some default files, that every NetBeans module requires.
[start=1]
1. Choose File > New Project (Ctrl+Shift+N). Under Categories, select NetBeans Modules. Under Projects, select Module. Click Next.
[start=2]
1. In the Name and Location panel, type ``DemoWizard`` in the Project Name field. Change the Project Location to any directory on your computer. Click Next.
[start=3]
1. In the Basic Module Configuration panel, type ``org.demo.wizard`` in Code Name Base. Click Finish.
The IDE creates the ``DemoWizard`` project. The project contains all of your sources and project metadata, such as the project's Ant build script. The project opens in the IDE. You can view its logical structure in the Projects window (Ctrl-1) and its file structure in the Files window (Ctrl-2).
== Creating the Wizard Infrastructure
In this section, we use the Wizard wizard to add the stubs of a wizard to our module.
[start=1]
1. In the Projects window, right-click the DemoWizard project node, choose New | Other, and then choose Module Development | Wizard. Click Next.
[start=2]
1. In the Wizard Type panel, type 2 in the "Number of Wizard Panels" field, and leave the other values unchanged:
image::images/wizard_70_wizard-wizard.png[]
The fields in the panel above are as follows:
* *Registration Type.* Determines where the user will access the wizard. If you select "Custom", the Wizard wizard will create a new action class that you can use to open and initialize your wizard. If you select "New File", the Wizard wizard will register your wizard in the module's ``layer.xml`` file.
* *Wizard Step Sequence.* Determines whether the wizard will be linear or whether the user of the wizard will be able to skip wizard steps, depending on choices made earlier in the wizard. Linear wizards are 'Static', which is the default, while wizards with skippable steps require a custom iterator class, which is created if you select 'Dynamic'. (For details on dynamic wizards, see link:http://netbeans.dzone.com/nb-how-to-create-dynamic-wizard[How to Create a Dynamic Wizard].)
* *Number of Wizard Panels.* Determines the number of wizard panels that will be created. For each wizard step, two Java files will be createda view and a controller.
Click Next.
[start=3]
1. In the Name and Location panel, type ``Demo`` in the Class Name Prefix and select the main package from the Package drop-down list:
image::images/wizard_70_wizard-wizard-4.png[]
Click Finish.
In the Projects window, you should now see this:
image::images/wizard_70_projects-window.png[]
Read through the NetBeans wizard javadoc in the link:http://bits.netbeans.org/dev/javadoc/org-openide-dialogs/[NetBeans Dialogs API] and compare the classes generated above to the classes described in the link:http://bits.netbeans.org/dev/javadoc/org-openide-dialogs/org/openide/package-summary.html[javadoc], which will prepare you for the next sections.
== Registering the Wizard Action Class
In this section, we modify the generated Action class and register it in the central registry.
[start=1]
1. Open the ``DemoWizardAction.java`` file and notice that it consists of the following:
[source,java]
----
package org.demo.wizard;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.MessageFormat;
import java.util.ArrayList;
import javax.swing.JComponent;
import org.openide.DialogDisplayer;
import org.openide.WizardDescriptor;
// An example action demonstrating how the wizard could be called from within
// your code. You can move the code below wherever you need, or register an action:
// @ActionID(category="...", id="org.demo.wizard.DemoWizardAction")
// @ActionRegistration(displayName="Open Demo Wizard")
// @ActionReference(path="Menu/Tools", position=...)
public final class DemoWizardAction implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
List<WizardDescriptor.Panel<WizardDescriptor>> panels = new ArrayList<WizardDescriptor.Panel<WizardDescriptor>>();
panels.add(new DemoWizardPanel1());
panels.add(new DemoWizardPanel2());
String[] steps = new String[panels.size()];
for (int i = 0; i < panels.size(); i++) {
Component c = panels.get(i).getComponent();
// Default step name to component name of panel.
steps[i] = c.getName();
if (c instanceof JComponent) { // assume Swing components
JComponent jc = (JComponent) c;
jc.putClientProperty(WizardDescriptor.PROP_CONTENT_SELECTED_INDEX, i);
jc.putClientProperty(WizardDescriptor.PROP_CONTENT_DATA, steps);
jc.putClientProperty(WizardDescriptor.PROP_AUTO_WIZARD_STYLE, true);
jc.putClientProperty(WizardDescriptor.PROP_CONTENT_DISPLAYED, true);
jc.putClientProperty(WizardDescriptor.PROP_CONTENT_NUMBERED, true);
}
}
WizardDescriptor wiz = new WizardDescriptor(new WizardDescriptor.ArrayIterator<WizardDescriptor>(panels));
// {0} will be replaced by WizardDesriptor.Panel.getComponent().getName()
wiz.setTitleFormat(new MessageFormat("{0}"));
wiz.setTitle("...dialog title...");
if (DialogDisplayer.getDefault().notify(wiz) == WizardDescriptor.FINISH_OPTION) {
// do something
}
}
}
----
[start=2]
1. At the top of the Action class, notice that some Action annotations have been commented out. Remove the comments and add a category and a position, so that the annotations are as follows:
link:http://bits.netbeans.org/dev/javadoc/org-openide-awt/org/openide/awt/ActionID.html[@ActionID]
[source,java]
----
(category="Demo", id="org.demo.wizard.DemoWizardAction")
link:http://bits.netbeans.org/dev/javadoc/org-openide-awt/org/openide/awt/ActionRegistration.html[@ActionRegistration](displayName="Open Demo Wizard")
link:http://bits.netbeans.org/dev/javadoc/org-openide-awt/org/openide/awt/ActionReference.html[@ActionReference](path="Menu/Tools", position=10)
----
When the module is compiled, you will find a " ``generated-layer.xml`` " file, if you switch to the Files window and look in the ``build/classes/META-INF`` folder, as shown below:
image::images/wizard_70_generated-layer.png[]
The ``generated-layer.xml`` file provides contributions to the NetBeans central registry (also known as the 'system filesystem'), where fixed folders (such as "Actions" and "Menu") provide placeholders for the registration of the content of menubars, toolbars, and many other NetBeans Platform features. For details, see link:https://netbeans.apache.org/wiki/devfaqsystemfilesystem[http://wiki.netbeans.org/DevFaqSystemFilesystem].
[start=3]
1. Run the module. The application starts up and you should see your menu item where you specified it to be in the annotation above:
image::images/wizard_70_result-1.png[]
Click the menu item and the wizard appears:
image::images/wizard_70_result-2.png[]
Click Next and notice that in the final panel the Finish button is enabled:
image::images/wizard_70_result-3.png[]
Now that the wizard infrastructure is functioning, let's add some content.
== Designing the Wizard Content
In this section, we add content to the wizard and customize its basic features. For purposes of this example, we imagine that we are creating a wizard in a music application.
[start=1]
1. Open the ``DemoWizardAction.java`` file and notice that you can set a variety of customization properties for the wizard:
image::images/wizard_70_wizard-tweaking.png[]
Read about these properties link:http://ui.netbeans.org/docs/ui_apis/wide/index.html[here].
[start=2]
1. In ``DemoWizardAction.java`` , change ``wizardDescriptor.setTitle`` to the following:
[source,java]
----
wizardDescriptor.setTitle("Music Selection");
----
[start=3]
1. Open the ``DemoVisualPanel1.java`` file and the ``DemoVisualPanel2.java`` file and use the "Matisse" GUI Builder to add some Swing components, such as the following:
image::images/wizard_panel-1-design.png[]
For code later in this tutorial to work, you need to set the name of the ``JTextFields`` above to ``nameField`` and ``addressField`` .
image::images/wizard_panel-2-design.png[]
Above, you see ``DemoVisualPanel1.java`` file and the ``DemoVisualPanel2.java`` , with some Swing components.
[start=4]
1. Open the two panels in the Source view and change their ``getName()`` methods to "Name and Address" and "Musician Details", respectively.
[start=5]
1.
Run the module again. When you open the wizard, you should see something like this, depending on the Swing components you added and the customizations you provided:
image::images/wizard_70_result-4.png[]
The image in the left sidebar of the wizard above is set in the ``DemoWizardAction.java`` file, like this:
[source,java]
----
wizardDescriptor.putProperty(WizardDescriptor.PROP_IMAGE, ImageUtilities.loadImage("org/demo/wizard/banner.PNG", true));
----
Now that you have designed the wizard content, let's add some code for processing the data that the user will enter.
== Processing User Data
In this section, you learn how to pass user data from panel to panel and how to display the results to the user when Finish is clicked.
[start=1]
1. In the ``WizardPanel`` classes, i.e., in ``DemoWizardPanel1.java`` and in ``DemoWizardPanel2.java`` , use the ``storeSettings`` method to retrieve the data set in the visual panel.
For example, start by creating getters in the ``DemoVisualPanel1.java`` file:
[source,java]
----
public JTextField getNameField(){
return nameField;
}
public JTextField getAddressField(){
return addressField;
}
----
Then access the above from the ``DemoWizardPanel1.java`` file:
[source,java]
----
@Override
public void storeSettings(Object settings) {
((WizardDescriptor) settings).putProperty("name", ((DemoVisualPanel1)getComponent()).getNameField().getText());
((WizardDescriptor) settings).putProperty("address", ((DemoVisualPanel1)getComponent()).getAddressField().getText());
}
----
[start=2]
1. Next, use the ``DemoWizardAction.java`` file to retrieve the properties you have set and do something with them:
[source,java]
----
public void actionPerformed(ActionEvent e) {
WizardDescriptor wizardDescriptor = new WizardDescriptor(getPanels());
// {0} will be replaced by WizardDesriptor.Panel.getComponent().getName()
wizardDescriptor.setTitleFormat(new MessageFormat("{0}"));
wizardDescriptor.setTitle("Music Selection");
Dialog dialog = DialogDisplayer.getDefault().createDialog(wizardDescriptor);
dialog.setVisible(true);
dialog.toFront();
boolean cancelled = wizardDescriptor.getValue() != WizardDescriptor.FINISH_OPTION;
if (!cancelled) {
*String name = (String) wizardDescriptor.getProperty("name");
String address = (String) wizardDescriptor.getProperty("address");
DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(name + " " + address));*
}
}
----
The `` link:http://bits.netbeans.org/dev/javadoc/org-openide-dialogs/org/openide/NotifyDescriptor.html[NotifyDescriptor]`` can be used in other ways too, as indicated by the code completion box:
image::images/wizard_70_notifydescriptor.png[]
You now know how to process data entered by the user.
== Validating User Data
In this section, you learn how to validate the user input when "Next" is clicked in the wizard.
[start=1]
1. In ``DemoWizardPanel1`` , change the class signature, implementing `` link:http://bits.netbeans.org/dev/javadoc/org-openide-dialogs/org/openide/WizardDescriptor.ValidatingPanel.html[WizardDescriptor.ValidatingPanel]`` instead of ``WizardDescriptor.Panel`` :
[source,java]
----
public class DemoWizardPanel1 implements WizardDescriptor.ValidatingPanel
----
[start=2]
1. At the top of the class, change the ``JComponent`` declaration to a typed declaration:
[source,java]
----
private DemoVisualPanel1 component;
----
[start=3]
1. Implement the required abstract method that throws a `` link:http://bits.netbeans.org/dev/javadoc/org-openide-dialogs/org/openide/WizardValidationException.html[WizardValidationException]`` like this:
[source,java]
----
@Override
public void validate() throws WizardValidationException {
String name = component.getNameField().getText();
if (name.equals("")){
throw new WizardValidationException(null, "Invalid Name", null);
}
}
----
[start=4]
1. Run the module. Click "Next", without entering anything in the "Name" field, and you should see the result below. Also, note that you are not able to move to the next panel, as a result of the validation having failed:
image::images/wizard_70_validation1.png[]
[start=5]
1. Optionally, disable the "Next" button if the name field is empty. Start by declaring a boolean at the top of the class:
[source,java]
----
private boolean isValid = true;
----
Then override ``isValid()`` like this:
[source,java]
----
@Override
public boolean isValid() {
return isValid;
}
----
And, when ``validate()`` is called, which is when the "Next" button is clicked, return false:
[source,java]
----
@Override
public void validate() throws WizardValidationException {
String name = component.getNameTextField().getText();
if (name.equals("")) {
*isValid = false;*
throw new WizardValidationException(null, "Invalid Name", null);
}
}
----
Run the module. This time, the first time you click "Next", you'll find that if there is no content is in the field, the "Next" button becomes disabled:
image::images/wizard_70_validation2.png[]
Alternatively, set the boolean to false initially. That will cause the "Next" button to be disabled when the wizard is shown. Then add a ``DocumentListener`` to the text field and, when the user types something in the field, set the boolean to true and call ``isValid()`` .
You now know how to validate data entered by the user.
For more information on validating user input, see Tom Wheeler's sample at the end of this tutorial.
For a very simple and powerful Swing validation framework, see the link:http://kenai.com/projects/simplevalidation/pages/Home[Simple Validation API].
== Persisting Data Across Restarts
In this section, you learn how to store the data when the wizard closes and retrieve it when the wizard opens again.
[start=1]
1. In ``DemoWizardPanel1.java`` , override the ``readSettings`` and the ``storeSettings`` methods as follows:
[source,java]
----
*JTextField nameField = ((DemoVisualPanel1) getComponent()).getNameField();
JTextField addressField = ((DemoVisualPanel1) getComponent()).getAddressField();*
@Override
public void readSettings(Object settings) {
*nameField.setText(NbPreferences.forModule(DemoWizardPanel1.class).get("namePreference", ""));
addressField.setText(NbPreferences.forModule(DemoWizardPanel1.class).get("addressPreference", ""));*
}
@Override
public void storeSettings(Object settings) {
((WizardDescriptor) settings).putProperty("name", nameField.getText());
((WizardDescriptor) settings).putProperty("address", addressField.getText());
*NbPreferences.forModule(DemoWizardPanel1.class).put("namePreference", nameField.getText());
NbPreferences.forModule(DemoWizardPanel1.class).put("addressPreference", addressField.getText());*
}
----
[start=2]
1. Run the module again and type a name and address in the first panel of the wizard:
image::images/wizard_70_nbpref1.png[]
[start=3]
1. Close the application, open the Files window, and look in the ``wizard.properties`` file within the application's ``build`` folder. You should now see settings like this:
image::images/wizard_70_nbpref3.png[]
[start=4]
1. Run the application again and, when you next open the wizard, the settings specified above are automatically used to define the values in the fields in the wizard.
You now know how to persist wizard data across restarts.
For more information on the ``NbPreferences`` class, used above, see the link:http://bits.netbeans.org/dev/javadoc/org-openide-util/org/openide/util/NbPreferences.html[ ``NbPreferences`` javadoc], as well as the link:https://netbeans.apache.org/tutorials/nbm-options.html[NetBeans Options Window Tutorial].
== Branding the Wizard
In this section, you brand the "Next" button's string, which is provided by the wizard infrastructure, to "Advance".
The term "branding" implies customization, i.e., typically these are minor modifications within the same language, while "internationalization" or "localization" implies translation into another language. For information on localization of NetBeans modules, link:http://translatedfiles.netbeans.org/index-l10n.html[go here].
Branding entails overriding properties files in the NetBeans Platform. These properties files, normally referred to as "bundle properties" files, contain strings that are used in display texts, such as the texts on the buttons in the NetBeans Platform wizards. To override these bundle properties files, your module needs to be part of a NetBeans Platform application. Each NetBeans Platform application has a "branding" folder, which is where bundle properties override files are placed.
[start=1]
1. Make sure your NetBeans module is part of a NetBeans Platform application, rather than being a standalone module.
[start=2]
1. In the Files window, expand the application's ``"branding"`` folder and then create the folder/file structure highlighted below:
image::images/wizard_70_branding-1.png[]
[start=3]
1. Define the content of the "Bundle.properties" file as follows:
[source,java]
----
CTL_NEXT=&amp;Advance >
----
Other strings you might like to brand are as follows:
[source,java]
----
CTL_CANCEL
CTL_PREVIOUS
CTL_FINISH
CTL_ContentName
----
The key "CTL_ContentName" is set to "Steps" by default, which is used in the left panel of the wizard,if the "WizardPanel_autoWizardStyle" property has not been set to "FALSE".
[start=4]
1. Run the application and the "Next" button will be branded to "Advance":
image::images/wizard_70_branding-2.png[]
Optionally, use the ``DemoWizardAction.java`` file, as described earlier, to remove the whole left side of the wizard as follows:
[source,java]
----
wizardDescriptor.putProperty(WizardDescriptor.PROP_AUTO_WIZARD_STYLE, Boolean.FALSE);
----
The above setting results in a wizard that looks as follows:
image::images/wizard_70_branding-3.png[]
You now know how to brand the strings defined in the wizard infrastructure with your own branded versions.
== Further Reading
Several pieces of related information are available on-line:
* link:http://netbeans.dzone.com/nb-how-to-create-dynamic-wizard[How to Create a Dynamic Wizard]
*
Tom Wheeler's NetBeans Site (click the image below):
[.feature]
--
image::images/wizard_tom.png[role="left", link="http://www.tomwheeler.com/netbeans/"]
--
Even though it was written for NetBeans 5.5, the above sample has been successfully tried in NetBeans IDE 6.5.1 on Ubuntu Linux with JDK 1.6.
The sample is especially useful in showing how to validate user data.
* Geertjan's Blog:
* link:http://blogs.oracle.com/geertjan/entry/how_wizards_work[How Wizards Work: Part 1—Introduction]
* link:http://blogs.oracle.com/geertjan/entry/how_wizards_work_part_2[How Wizards Work: Part 2—Different Types ]
* link:http://blogs.oracle.com/geertjan/entry/how_wizards_work_part_3[How Wizards Work: Part 3—Your First Wizard]
* link:http://blogs.oracle.com/geertjan/entry/how_wizards_work_part_4[How Wizards Work: Part 4—Your Own Iterator ]
* link:http://blogs.oracle.com/geertjan/entry/how_wizards_work_part_5[How Wizards Work: Part 5—Reusing and Embedding Existing Panels ]
* link:http://blogs.oracle.com/geertjan/entry/creating_a_better_java_class[Creating a Better Java Class Wizard]