X

Geertjan's Blog

  • October 17, 2005

How Wizards Work (Part 5): Reusing and Embedding Existing Panels

Geertjan Wielenga
Product Manager
When you use the IDE to create a wizard, there are a few panels provided by the IDE that you can reuse, instead of creating everything from scratch yourself. For example, this panel is used over and over (all of the IDE's file wizards have it), for setting the name and location for a to-be-created file:

Instead of creating the above panel yourself, use the following steps to reuse it:

  1. Create a module project and then add wizard files (use the 'Wizard' file type in the 'NetBeans Plug-in Modules' category).

  2. Right-click the project node and use the Libraries node to add module dependencies for 'Java project Support', 'Project API', and 'Project UI API'.

  3. Everything else you'll need to do is done in the getPanels() method of the iterator class. This is the default code provided by the Wizard wizard (for a wizard with two panels and a prefix of 'NewFile'):
    private WizardDescriptor.Panel[] getPanels() {
    if (panels == null) {
    panels = new WizardDescriptor.Panel[] {
    new NewFileWizardPanel1(),
    new NewFileWizardPanel2()
    };
    String[] steps = createSteps();
    for (int i = 0; i > panels.length; i++) {
    Component c = panels[i].getComponent();
    if (steps[i] == null) {
    // Default step name to component name of panel. Mainly
    // useful for getting the name of the target chooser to
    // appear in the list of steps.
    steps[i] = c.getName();
    }
    if (c instanceof JComponent) { // assume Swing components
    JComponent jc = (JComponent) c;
    // Sets step number of a component
    jc.putClientProperty("WizardPanel_contentSelectedIndex", new Integer(i));
    // Sets steps names for a panel
    jc.putClientProperty("WizardPanel_contentData", steps);
    // Turn on subtitle creation on each step
    jc.putClientProperty("WizardPanel_autoWizardStyle", Boolean.TRUE);
    // Show steps on the left side with the image on the background
    jc.putClientProperty("WizardPanel_contentDisplayed", Boolean.TRUE);
    // Turn on numbering of all steps
    jc.putClientProperty("WizardPanel_contentNumbered", Boolean.TRUE);
    }
    }
    }
    return panels;
    }

    Add the following to the top of the method, click Alt-Shift-F, and choose org.netbeans.spi.project.ui.templates.support.Templates:

    Project project = Templates.getProject(wizard);
    Sources sources = (Sources)project.getLookup().lookup(Sources.class);
    SourceGroup[] groups = sources.getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA);

    Add this above new NewFileWizardPanel1:
    JavaTemplates.createPackageChooser(project,groups),

    Again you will need to fix imports to get the right package imported at the top of the file.

    So, if you used the Wizard wizard to create two panels, the iterator class will now contain this:

    panels = new WizardDescriptor.Panel[] {
    JavaTemplates.createPackageChooser(project,groups),
    new NewFileWizardPanel1(),
    new NewFileWizardPanel2()
    };

Embedding a Panel

But, maybe you want the second panel to appear inside the first panel. To do this, take the following steps (complete code is included at the end of this blog entry):

  1. Declare a new panel at the top of the class:

    private WizardDescriptor.Panel packageChooserPanel;

  2. Now define the panel in the class:

    packageChooserPanel = JavaTemplates.createPackageChooser(project,groups);

  3. Specify that the panel should be created, and when; here, the panel is created before the other two:

    panels = new WizardDescriptor.Panel[] {
    packageChooserPanel,
    new NewFileWizardPanel1(),
    new NewFileWizardPanel2()
    };

  4. However, if you define the panel as above, the one panel follows the other panel. But, like this, you embed your own panel inside the one provided by the IDE:

    packageChooserPanel = JavaTemplates.createPackageChooser(project,groups,new NewFileWizardPanel1());

  5. And then all you need to do is this:

    panels = new WizardDescriptor.Panel[] {
    packageChooserPanel,
    new NewFileWizardPanel2()
    };

Embedding one panel inside another is a pattern that many wizards in the IDE follow. For example, here's the New JSP wizard:

Here you can see what seems to be one panel. However, it is in fact two—the second is embedded inside the first. So this is what my getPanels() method looks like now (everything I added is highlighted below):

private WizardDescriptor.Panel[] getPanels() {Project project = Templates.getProject(wizard);
Sources sources = (Sources)project.getLookup().lookup(Sources.class);
SourceGroup[] groups = sources.getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA);
packageChooserPanel = JavaTemplates.createPackageChooser(project,groups,new NewFileWizardPanel1());

if (panels == null) {
panels = new WizardDescriptor.Panel[] {packageChooserPanel,
new NewFileWizardPanel2()
};
String[] steps = createSteps();
for (int i = 0; i < panels.length; i++) {
Component c = panels[i].getComponent();
if (steps[i] == null) {
// Default step name to component name of panel. Mainly
// useful for getting the name of the target chooser to
// appear in the list of steps.
steps[i] = c.getName();
}
if (c instanceof JComponent) { // assume Swing components
JComponent jc = (JComponent) c;
// Sets step number of a component
jc.putClientProperty("WizardPanel_contentSelectedIndex", new Integer(i));
// Sets steps names for a panel
jc.putClientProperty("WizardPanel_contentData", steps);
// Turn on subtitle creation on each step
jc.putClientProperty("WizardPanel_autoWizardStyle", Boolean.TRUE);
// Show steps on the left side with the image on the background
jc.putClientProperty("WizardPanel_contentDisplayed", Boolean.TRUE);
// Turn on numbering of all steps
jc.putClientProperty("WizardPanel_contentNumbered", Boolean.TRUE);
}
}
}
return panels;
}

Creating a File in a Package

In the Libraries panel of the Project Properties dialog box, declare module dependencies for 'Datasystems API', 'File System API', and 'Nodes API'. Then fill out the iterator class's instantiate() method as follows:

String className = Templates.getTargetName(wizard);
FileObject pkg = Templates.getTargetFolder(wizard);
DataFolder targetFolder = DataFolder.findFolder(pkg);
TemplateWizard template = (TemplateWizard)wizard;
DataObject doTemplate = template.getTemplate();
return Collections.EMPTY_SET;

That's it. At the end of the wizard, the class name and package name will be taken from the reused 'Name and Location' panel—and a package and file will be created for you.

If you add the following above the return statement, your newly created file will open in the Source Editor when you complete the wizard:

OpenCookie open = (OpenCookie) doTemplate.getCookie(OpenCookie.class);
if (open != null) {
open.open();
}

Creating Multiple Files

Here is the instantiate() method for a wizard that creates three files (using the specified class name as a prefix)—an HTML file, a Java source file, and a Java source file that has 'Application' appended on to the end of the classname:

public Set instantiate() throws IOException {
String className = Templates.getTargetName(wizard);
FileObject pkg = Templates.getTargetFolder(wizard);
DataFolder targetFolder = DataFolder.findFolder(pkg);
TemplateWizard template = (TemplateWizard)wizard;
DataObject doTemplate1 = template.getTemplate();
DataObject doTemplate2 = template.getTemplate();
DataObject doTemplate3 = template.getTemplate();
doTemplate1.createFromTemplate(targetFolder,className+".java");
doTemplate2.createFromTemplate(targetFolder,className+".html");
doTemplate3.createFromTemplate(targetFolder,className+"Application.java");
OpenCookie open = (OpenCookie) doTemplate1.getCookie(OpenCookie.class);
if (open != null) {
open.open();
}
return Collections.EMPTY_SET;
}

Using the above code, three files are created whenever I complete the wizard.

Update: There are more parts to this series... Part 1, Part 2, Part 3, and Part 4.

Join the discussion

Comments ( 4 )
  • Alied P&eacute;rez Thursday, June 3, 2010

    Hi, first, thanks for enlighting me to solve my problem.

    However, while it might be interesting for a java based project, many developers want a custom project to use the default new file panel.

    This can be acomplished with this:

    [code]

    Project project = Templates.getProject(wizard);

    Sources sources = ProjectUtils.getSources(project);

    SourceGroup[] groups = sources.getSourceGroups(Sources.TYPE_GENERIC);

    if (panels == null) {

    panels = new WizardDescriptor.Panel[]{

    Templates.createSimpleTargetChooser(project, groups),

    new FileWizardPanel1()

    };

    [/code]

    then, to create the file from a template, do instantiate() like this:

    [code]

    @Override

    public Set instantiate() throws IOException {

    String className = Templates.getTargetName(wizard);

    FileObject pkg = Templates.getTargetFolder(wizard);

    DataFolder targetFolder = DataFolder.findFolder(pkg);

    TemplateWizard template = (TemplateWizard) wizard;

    DataObject doTemplate = template.getTemplate();

    FileOutputStream f = new FileOutputStream(FileUtil.toFile(pkg).getAbsolutePath() +

    File.separator + className + ".prdt");

    ObjectOutput s = new ObjectOutputStream(f);

    s.writeObject(product);

    s.flush();

    f.close();

    return Collections.singleton(doTemplate.createFromTemplate(targetFolder, className + ".ext"));

    }

    [/code]

    BTW, Why did you return a Collections.EMPTY_SET instead of the actual file(s)?


  • Alied P&eacute;rez Friday, June 4, 2010

    [errata]

    this is not part of the proposed solution ;-)

    That's from my code

    [code]

    FileOutputStream f = new FileOutputStream(FileUtil.toFile(pkg).getAbsolutePath() +

    File.separator + className + ".prdt");

    ObjectOutput s = new ObjectOutputStream(f);

    s.writeObject(product);

    s.flush();

    f.close();

    [/code]


  • Keks Tuesday, July 29, 2014

    Thx for that great Tutorial.

    How can I add a further wizard panel to my existing two panels?


  • Geertjan Tuesday, July 29, 2014

    Copy one of the existing panels and then hook them into the iterator just like your existing panels.


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