Wizards usually appear in the IDE either in the Object Gallery (for those wizards that construct source code for objects) or under the Wizards menu on the menu bar. Other possible locations (such as on the toolbar and on pop-up context menus) require different registration methods. To display the Object Gallery, choose File|New.
To write a new wizard, you must perform at least these steps:
WizardAction
with the WizardManager
.
BasicWizard
class.
BasicWizardPage
panel objects used to gather user input.
Like other OpenTools, wizards are introduced into JBuilder at application startup during the OpenTools discovery process. Each wizard provides a static initOpenTools()
method that registers its static WizardAction
method with the WizardManager
. A
WizardAction
provides information that helps integrate that
wizard with JBuilder and acts as a factory when an instance of that
wizard is needed.
The entire JBuilder IDE is based on the Swing component architecture. All wizard UI components must be Swing-based.
WizardAction
.
The most common way to integrate a wizard is to register it with the
WizardManager
in the initOpenTool()
method. Here is an example:
public static void initOpenTool(byte majorVersion, byte minorVersion) { if (majorVersion == PrimeTime.CURRENT_MAJOR_VERSION) { WizardManager.registerWizardAction(myWizard); } }
A WizardAction
is essentially a cookie describing the wizard. Use one of its several constructors, which provide ever increasing overrides of default behavior. This example uses the constructor that takes six parameters: shortText
, mnemonic
, longText
, smallIcon
, largeIcon
, and galleryWizard
:
public static final WizardAction myWizard = new WizardAction ( "My Wizard...", 'm', "My wizard description", BrowserIcons.ICON_BLANK, BrowserIcons.ICON_BLANK, false) { protected Wizard createWizard() { return new MyWizard(); } };
Because the final galleryWizard
parameter is false, this wizard doesn't appear in the Object Gallery, but on the Wizards menu. If the wizard appears in the Object Gallery, the specified large icon is used and the longText
parameter value is the description that appears as the wizards tool tip. If the wizard appears on the Wizards menu, the small icon is used and the description appears on the status bar when the mouse is over the wizard menu item.
BasicWizard
class controls the flow of the wizard by defining which pages will appear and in
what order. When you extend this class, you must override the invokeWizard()
method to call setWizardTitle()
, and then call addWizardPage()
for each BasicWizardPage
you defined. Here's an example:
MyWizardPage1 page1 = new MyWizardPage1(); public WizardPage invokeWizard(WizardHost host) { setWizardTitle("My Wizard"); addWizardPage(page1); return super.invokeWizard(host); }
The BasicWizard
class also supplies the finish()
method that is called when the user clicks the OK or Finish button in the wizard, indicating the wizard should perform its assigned tasks. In this example, you would put the primary logic of the wizard within the doIt()
method:
protected void finish() throws VetoException { doIt(); }
BasicWizardPage
class provides the UI for a single panel (sometimes referred to as a step) of the wizard.
There aren't any methods that you must override in the class. Usually the content consists of Swing components that form the panel user interface. You can design the page with JBuilder's UI designer.
If the default large icon on the left of the page is in the way, you can switch to a smaller icon by calling the setPageStyle(STYLE_COMPEX)
method. Depending on which page style you choose and if you want to supply a different icon than the default, you can also call setSmallIcon()
or setLargeIcon()
.
You might find the activated()
method of the BasicWizardPage useful. It provides a way of initializing fields based on the input from prior pages.
If you want the wizard to validate the user input before the user advances to the next page or clicks the OK or Finish button, you can override the checkPage()
method, supply the validation logic, and then throw a VetoException
if any validation errors exist. Here's an example:
public void checkPage() throws VetoException { if (checkForError()) { JOptionPane.showMessageDialog(wizardHost.getDialogParent(), "We have a problem.", "Error", JOptionPane.ERROR_MESSAGE, null); throw new VetoException(); } }
WizardAction
can optionally enable itself depending on the current state of the environment. You can do this by overriding the update()
method and using the supplied Browser
reference to determine if conditions are suited for the wizard. For instance, you could have update()
check if there is a project open, test the file type of the active node if there is one, and/or check whether a needed class can be found on the current classpath.
public static final WizardAction myWizard = new WizardAction ( "My Wizard...", 'm', "My wizard description", BrowserIcons.ICON_BLANK, BrowserIcons.ICON_BLANK, false) { public void update(Object source) { Browser browser = Browser.findBrowser(source); Node node = browser.getActiveNode(); setEnabled((node != null) && (node instanceof JavaFileNode)); } protected Wizard createWizard() { return new MyWizard(); } };
You can have a BasicWizard
dynamically change the flow of the wizard pages by overriding the checkPage()
method. Usually checkPage()
is called on the
currently visible BasicWizardPage
to validate its user input. Instead, for example, you can include code that, based on the input on the first page, dynamically alters the flow for the rest of the wizard. Here's an example:
protected void checkPage(WizardPage page) throws VetoException { super.checkPage(page); if (page == step1) { removeWizardPage(step2); removeWizardPage(step3); removeWizardPage(step4); if (step1.getChoice()) { addWizardPage(step2); addWizardPage(step4); } else { addWizardPage(step3); } } }
Occasionally you might want to have the wizard validate input entered into a text field and dynamically alter the state of the OK/Finish or Next button accordingly. An easy way to do that is have your implemented BasicWizardPage
class extend javax.swing.event.DocumentListener
and add itself as a listener for
that field:
jTextField1.getDocument().addDocumentListener(this);
Then the implementation of the interface would be similiar to this:
public void insertUpdate(DocumentEvent e) {update();} public void removeUpdate(DocumentEvent e) {update();} public void changedUpdate(DocumentEvent e) {update();} private void update() { String text = jTextField1.getText().trim(); if (wizardHost ! = null){ boolean valid = (text.length() > 0); wizardHost.setNextEnabled(valid); wizardHost.setFinishEnabled(valid); } }
In a multiple step wizard, the user might use the Back button to revisit a prior step. It's also possible that you might want to enable the Finish button before the last step, thereby allowing the user to accept all the defaults for steps not visited. It then makes sense to architect such a wizard with a class that initializes all the default settings. A reference to that class would be passed when creating each wizard page, and each would initialize by accessing that class when activated and update it when deactivated. The actual logic invoked by Finish would probably reside there as well.
BasicWizardPage
implementation is the
user interface it presents. To ease future localization, you should make the
the layout as flexible as possible to accomodate labels and text that
might greatly decrease or increase in width once translated. When the wizard frame is stretched larger, the layout for each page should be designed to stretch with it so the user can adjust for any components that might have been clipped despite your best efforts. When there is extra vertical space, components should move upwards rather than centering.
You should assign keyboard accelerators to all input fields. Avoid using the same accelerator keys as are used by the Back, Next, Finish, and Help buttons if you are implementing a multi-page wizard.
Check the order in which focus moves between components when the user uses the Tab key to ensure movement is in the order the user of that page would expect.
Using the Tools|IDE Options dialog box, switch between alternative Look & Feel settings and verify that the new wizard still has an acceptable layout and colorization in each.
Compare your wizard to the other JBuilder wizards and attempt to present a similar user interface. For instance, JBuilder wizards capitialize only the first word in a label and end the text with a colon. When you put a group box around controls, use the following to make the box appear similar to the native wizards:
JPanel myGroup = new JPanel(); myGroup.setBorder(BorderFactory.createTitledBorder("My group:"));