alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2004 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.openide;

import java.awt.event.*;
import java.text.MessageFormat;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.Image;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.FocusTraversalPolicy;
import java.awt.Insets;
import java.awt.KeyboardFocusManager;
import java.awt.MediaTracker;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
import java.io.IOException;
import java.net.URL;
import java.lang.ref.WeakReference;

import org.openide.util.HelpCtx;
import org.openide.util.NbBundle;
import org.openide.util.Mutex;
import org.openide.util.WeakSet;
import org.openide.util.WeakListeners;
import org.openide.awt.HtmlBrowser;

import javax.accessibility.*;
import org.openide.util.RequestProcessor;
import org.openide.util.Utilities;

/**
 * Implements a basic "wizard" GUI system.
 * A list of wizard panels may be specified and these
 * may be traversed at the proper times using the "Previous"
 * and "Next" buttons (or "Finish" on the last one).
 * @see DialogDisplayer#createDialog
 * @see Wizard Guidebook
 * (describes the set of properties controlling the display of wizard panels)
 */
public class WizardDescriptor extends DialogDescriptor {

    /** "Next" button option.
    * @see #setOptions */
    public static final Object NEXT_OPTION = new String("NEXT_OPTION"); // NOI18N
    /** "Finish" button option.
    * @see #setOptions */
    public static final Object FINISH_OPTION = OK_OPTION;
    /** "Previous" button option.
    * @see #setOptions */
    public static final Object PREVIOUS_OPTION = new String("PREVIOUS_OPTION"); // NOI18N

    /** real buttons to be placed instead of the options */
    private final JButton nextButton = new JButton ();
    private final JButton finishButton = new JButton ();
    private final JButton cancelButton = new JButton ();
    private final JButton previousButton = new JButton ();
    
    private FinishAction finishOption;
    private Set/**/ newObjects = Collections.EMPTY_SET;

    /** a component with wait cursor */
    transient private Component waitingComponent;
    
    private static final ActionListener CLOSE_PREVENTER = new ActionListener () {
                public void actionPerformed (ActionEvent evt) {
                }
                public String toString() {
                    return "CLOSE_PREVENTER"; // NOI18N
                }
            };

    {
        // button init
        ResourceBundle b = NbBundle.getBundle ("org.openide.Bundle"); // NOI18N
        nextButton.setText (b.getString ("CTL_NEXT"));
        previousButton.setText (b.getString ("CTL_PREVIOUS"));
        finishButton.setText (b.getString ("CTL_FINISH"));
        finishButton.getAccessibleContext().setAccessibleDescription(b.getString ("ACSD_FINISH"));
        cancelButton.setText (b.getString ("CTL_CANCEL"));
        cancelButton.getAccessibleContext().setAccessibleDescription(b.getString ("ACSD_CANCEL"));        

        previousButton.setMnemonic (b.getString ("CTL_PREVIOUS_Mnemonic").charAt(0));
        finishButton.setMnemonic (b.getString ("CTL_FINISH_Mnemonic").charAt(0));

        finishButton.setDefaultCapable (true);
        nextButton.setDefaultCapable (true);
        previousButton.setDefaultCapable (false);
        cancelButton.setDefaultCapable (false);
    }
    
    /** Boolean property. The value is taken from WizardDescriptor.getProperty() or
     * ((JComponent)Panel.getComponent()).getClientProperty() in this order.
     * Set to true for enabling other properties. It is relevant only on 
     * initialization (client property in first panel).
     * When false or not present in JComponent.getClientProperty(), then supplied panel is 
     * used directly without content, help or panel name auto layout.
     */
    private static final String PROP_AUTO_WIZARD_STYLE = "WizardPanel_autoWizardStyle"; // NOI18N
    /** Boolean property. The value is taken from WizardDescriptor.getProperty() or
     * ((JComponent)Panel.getComponent()).getClientProperty() in this order.
     * Set to true for showing help pane in the left pane. It is relevant only on 
     * initialization (client property in first panel).
     */
    private static final String PROP_HELP_DISPLAYED = "WizardPanel_helpDisplayed"; // NOI18N
    /** Boolean property. The value is taken from WizardDescriptor.getProperty() or
     * ((JComponent)Panel.getComponent()).getClientProperty() in this order.
     * Set to true for showing content pane in the left pane. It is relevant only on 
     * initialization (client property in first panel).
     */
    private static final String PROP_CONTENT_DISPLAYED = "WizardPanel_contentDisplayed"; // NOI18N
    /** Boolean property. The value is taken from WizardDescriptor.getProperty() or
     * ((JComponent)Panel.getComponent()).getClientProperty() in this order.
     * Set to true for displaying numbers in the content. It is relevant only on 
     * initialization (client property in first panel).
     */
    private static final String PROP_CONTENT_NUMBERED = "WizardPanel_contentNumbered"; // NOI18N
    /** Integer property. The value is taken from WizardDescriptor.getProperty() or
     * ((JComponent)Panel.getComponent()).getClientProperty() in this order.
     * Represents index of content item which will be highlited.
     */
    private static final String PROP_CONTENT_SELECTED_INDEX = "WizardPanel_contentSelectedIndex"; // NOI18N
    /** String[] property. The value is taken from WizardDescriptor.getProperty() or
     * ((JComponent)Panel.getComponent()).getClientProperty() in this order.
     * Represents array of content items.
     */
    private static final String PROP_CONTENT_DATA = "WizardPanel_contentData"; // NOI18N
    /** Color property. The value is taken from WizardDescriptor.getProperty() or
     * ((JComponent)Panel.getComponent()).getClientProperty() in this order.
     * Set to background color of content pane.
     */
    private static final String PROP_CONTENT_BACK_COLOR = "WizardPanel_contentBackColor"; // NOI18N
    /** Color property. The value is taken from WizardDescriptor.getProperty() or
     * ((JComponent)Panel.getComponent()).getClientProperty() in this order.
     * Set to foreground color of content pane.
     */
    private static final String PROP_CONTENT_FOREGROUND_COLOR = "WizardPanel_contentForegroundColor"; // NOI18N
    /** Image property. The value is taken from WizardDescriptor.getProperty() or
     * ((JComponent)Panel.getComponent()).getClientProperty() in this order.
     * Set to image which should be displayed in the left pane (behind the content).
     */
    private static final String PROP_IMAGE = "WizardPanel_image"; // NOI18N
    /** String property. The value is taken from WizardDescriptor.getProperty() or
     * ((JComponent)Panel.getComponent()).getClientProperty() in this order.
     * Set to side where the image should be drawn.
     */
    private static final String PROP_IMAGE_ALIGNMENT = "WizardPanel_imageAlignment"; // NOI18N
    /** Dimension property. The value is taken from WizardDescriptor.getProperty() or
     * ((JComponent)Panel.getComponent()).getClientProperty() in this order.
     * Dimension of left pane, should be same as dimension of PROP_IMAGE.
     * It is relevant only on initialization (client property in first panel).
     */
    private static final String PROP_LEFT_DIMENSION = "WizardPanel_leftDimension"; // NOI18N
    /** URL property. The value is taken from WizardDescriptor.getProperty() or
     * ((JComponent)Panel.getComponent()).getClientProperty() in this order.
     * Represents URL of help displayed in left pane.
     */
    private static final String PROP_HELP_URL = "WizardPanel_helpURL"; // NOI18N
    /** String property. The value is taken from WizardDescriptor.getProperty().
     * If it contains non-null value the String is displayed the bottom of the wizard
     * and should inform user why the panel is invalid and why the Next/Finish
     * buttons were disabled.
     * @since 3.39
     */
    private static final String PROP_ERROR_MESSAGE = "WizardPanel_errorMessage"; // NOI18N

    /** Reference to default image */
    private static WeakReference defaultImage;
    
    /** Whether wizard panel will be constructed from WizardDescriptor.getProperty()/
     * (JComponent)Panel.getComponent() client properties or returned 
     * Component will be inserted to wizard dialog directly.
     */
    private boolean autoWizardStyle = false;
    /** Whether properties from first (JComponent)Panel.getComponent()
     * have been initialized.
     */
    private boolean init = false;
    /** Panel which is used when in AUTO_WIZARD_STYLE mode.*/
    private WizardPanel wizardPanel;
    /** Image */
    private Image image;
    /** Content data */
    private String[] contentData = new String[] {};
    /** Selected content index */
    private int contentSelectedIndex = -1;
    /** Background color*/
    private Color contentBackColor;
    /** Foreground color*/
    private Color contentForegroundColor;
    /** Help URL displayed in the left pane */
    private URL helpURL;
    /** Listener on a user component client property changes*/
    private PropL propListener;
    /** 'North' or 'South' */
    private String imageAlignment = "North"; // NOI18N
    
    /** Iterator between panels in the wizard */
    private Iterator panels;

    /** Change listener that invokes method update state */
    private Listener listener;

    /** current panel */
    private Panel current;

    /** settings to be used for the panels */
    private Object settings;

    /** message format to create title of the document */
    private MessageFormat titleFormat;

    /** hashtable with additional settings that is usually used
    * by Panels to store their data
    */
    private Map properties;

    /** Create a new wizard from a fixed list of panels, passing some settings to the panels.
    * @param wizardPanels the panels to use
    * @param settings the settings to pass to panels, or null
    * @see #WizardDescriptor(WizardDescriptor.Iterator, Object)
    */
    public WizardDescriptor (Panel[] wizardPanels, Object settings) {
        this (new ArrayIterator (wizardPanels), settings);
    }

    /** Create a new wizard from a fixed list of panels with settings
    * defaulted to this.
    *
    * @param wizardPanels the panels to use
    * @see #WizardDescriptor(WizardDescriptor.Iterator, Object)
    */
    public WizardDescriptor (Panel[] wizardPanels) {
        // passing CLOSE_PREVENTER which is treated especially
        this (wizardPanels, CLOSE_PREVENTER);
    }

    /** Create wizard for a sequence of panels, passing some settings to the panels.
    * @param panels iterator over all {@link WizardDescriptor.Panel}s that can appear in the wizard
    * @param settings the settings to provide to the panels (may be any data understood by them)
    * @see WizardDescriptor.Panel#readSettings
    * @see WizardDescriptor.Panel#storeSettings
    */
    public WizardDescriptor (Iterator panels, Object settings) {
        super ("", "", true, DEFAULT_OPTION, null, CLOSE_PREVENTER); // NOI18N
        
        this.settings = settings == CLOSE_PREVENTER ? this : settings;

        listener = new Listener ();

        try {
            nextButton.addActionListener ((ActionListener)WeakListeners.create(Class.forName("java.awt.event.ActionListener"),listener,nextButton));
            previousButton.addActionListener ((ActionListener)WeakListeners.create(Class.forName("java.awt.event.ActionListener"),listener,previousButton));
            finishButton.addActionListener ((ActionListener)WeakListeners.create(Class.forName("java.awt.event.ActionListener"),listener,finishButton));
            cancelButton.addActionListener ((ActionListener)WeakListeners.create(Class.forName("java.awt.event.ActionListener"),listener,cancelButton));
        } catch (ClassNotFoundException e) {
            // cannot happen, java.awt.event.ActionListener listener can always be found
        }
        
        finishOption = new WizardDescriptor.FinishAction ();

        super.setOptions (new Object[] { previousButton, nextButton, finishButton, cancelButton });
        super.setClosingOptions (new Object[] { finishOption, cancelButton });

        this.panels = panels;
        
        callInitialize ();
        
        panels.addChangeListener (WeakListeners.change(listener,panels));
    }

    /** Create wizard for a sequence of panels, with settings
    * defaulted to this.
    *
    * @param panels iterator over all {@link WizardDescriptor.Panel}s that can appear in the wizard
    */
    public WizardDescriptor(Iterator panels) {
        // passing CLOSE_PREVENTER which is treated especially
        this (panels, CLOSE_PREVENTER);
    }
    
    /** Initializes settings.
     */
    protected void initialize () {
        super.initialize ();

        updateState ();
    }

    /** Set a different list of panels.
    * Correctly updates the buttons.
    * @param panels the new list of {@link WizardDescriptor.Panel}s 
    */
    public final synchronized void setPanels (Iterator panels) {
        if (panels != null) {
            panels.removeChangeListener (listener);
        }
        this.panels = panels;
        panels.addChangeListener (WeakListeners.change(listener,panels));
        init = false;

        updateState ();
    }

    /** Set options permitted by the wizard considered as a DialogDescriptor.
    * Substitutes tokens such as {@link #NEXT_OPTION} with the actual button.
    *
    * @param options the options to set
    */
    public void setOptions (Object[] options) {
        super.setOptions (convertOptions (options));
    }

    /**
    * @param options the options to set
    */
    public void setAdditionalOptions (Object[] options) {
        super.setAdditionalOptions (convertOptions (options));
    }

    /**
    * @param options the options to set
    */
    public void setClosingOptions (Object[] options) {
        super.setClosingOptions (convertOptions (options));
    }

    /** Converts some options.
    */
    private Object[] convertOptions (Object[] options) {
        Object[] clonedOptions = (Object[])options.clone ();
        for (int i = clonedOptions.length - 1; i >= 0; i--) {
            if (clonedOptions[i] == NEXT_OPTION) clonedOptions[i] = nextButton;
            if (clonedOptions[i] == PREVIOUS_OPTION) clonedOptions[i] = previousButton;
            if (clonedOptions[i] == FINISH_OPTION) clonedOptions[i] = finishButton;
            if (clonedOptions[i] == CANCEL_OPTION) clonedOptions[i] = cancelButton;
        }
        return clonedOptions;
    }
    /** Overriden to ensure that returned value is one of 
     * the XXX_OPTION constants.
     */
    public Object getValue() {
        return backConvertOption(super.getValue());
    }
    
    /** Converts the option back to one of the constants.
     * It is called from getValue().
     */
    private Object backConvertOption(Object op) {
        if (op == nextButton) {
            return NEXT_OPTION;
        }
        if (op == previousButton) {
            return PREVIOUS_OPTION;
        }
        if (op == finishButton) {
            return FINISH_OPTION;
        }
        if (op == cancelButton) {
            return CANCEL_OPTION;
        }
        // if we don't know just return the original value
        return op;
    }

    /** Sets the message format to create title of the wizard.
    * The format can take two parameters. The name of the
    * current component and the name returned by the iterator that
    * defines the order of panels. The default value is something
    * like
    * 
    *   {0} wizard {1}
    * 
* That can be expanded to something like this *
    *   EJB wizard (1 of 8)
    * 
* This method allows anybody to provide own title format. * * @param format message format to the title */ public void setTitleFormat (MessageFormat format) { titleFormat = format; if (init) updateState (); } /** Getter for current format to be used to format title. * @return the format * @see #setTitleFormat */ public synchronized MessageFormat getTitleFormat () { if (titleFormat == null) { // ok, initialize the default one titleFormat = new MessageFormat ( NbBundle.getMessage (WizardDescriptor.class, "CTL_WizardName")); } return titleFormat; } /** Allows Panels that use WizardDescriptor as settings object to * store additional settings into it. * * @param name name of the property * @param value value of property */ public void putProperty (final String name, Object value) { Object oldValue = null; synchronized (this) { if (properties == null) { properties = new HashMap (7); } oldValue = properties.get (name); properties.put (name, value); } // bugfix #27738, firing changes in a value of the property firePropertyChange (name, oldValue, value); if (propListener != null) { Mutex.EVENT.readAccess(new Runnable() { public void run() { propListener.propertyChange( new PropertyChangeEvent(this, name, null, null) ); } }); } if (PROP_ERROR_MESSAGE.equals(name)) { WizardPanel wp = wizardPanel; if (wp != null) { wp.setErrorMessage((String)(value == null ? " " : value)); //NOI18N } } } /** Getter for stored property. * @param name name of the property * @return the value */ public synchronized Object getProperty (String name) { return properties == null ? null : properties.get (name); } public void setHelpCtx (final HelpCtx helpCtx) { if ((wizardPanel != null) && (helpCtx != null)) { HelpCtx.setHelpIDString(wizardPanel, helpCtx.getHelpID()); } // we call the inherited method after setting the ID // on the panel becuase super.setHelpCtx fires the change super.setHelpCtx(helpCtx); } /** Returns set of newly instantiated objects if the wizard has been correctly finished. * Returns the empty set as default. If the wizard uses the InstantiatingIterator * then WizardDescriptor returns a set of Object as same as InstantiatingIterator.instantiate(). * * @exception IllegalStateException if this method is called on the unfinished wizard * @return a set of Objects created * @since 4.41 */ public Set/**/ getInstantiatedObjects () { // if (!(FINISH_OPTION.equals (getValue ()))) { throw new IllegalStateException (); } return newObjects; } /** Updates buttons to reflect the current state of the panels. * Can be overridden by subclasses * to change the options to special values. In such a case use: *

    *   super.updateState ();
    *   setOptions (...);
    * 
*/ protected synchronized void updateState () { Panel p = panels.current (); // listeners on the panel if (current != p) { if (current != null) { // remove current.removeChangeListener (listener); current.storeSettings (settings); } // Hack - obtain current panel again // It's here to allow dynamic change of panels in wizard // (which can be done in storeSettings method) p = panels.current (); // add to new p.addChangeListener (WeakListeners.change(listener,p)); current = p; current.readSettings (settings); } boolean next = panels.hasNext (); boolean prev = panels.hasPrevious (); boolean valid = p.isValid (); nextButton.setEnabled (next && valid); previousButton.setEnabled (prev); if (current instanceof FinishablePanel) { // check if isFinishPanel if (((FinishablePanel)current).isFinishPanel ()) { finishButton.setEnabled (valid); } else { // XXX What if the last panel is not FinishPanel ??? enable ? finishButton.setEnabled (valid && !next); } } else { // original way finishButton.setEnabled ( valid && (!next || (current instanceof FinishPanel)) ); } // nextButton.setVisible (next); // finishButton.setVisible (!next || (current instanceof FinishPanel)); // XXX Why setValue on Next ? // if (next) { // setValue (nextButton); // } else { //// setValue (finishButton); // } setHelpCtx (p.getHelp ()); java.awt.Component c = p.getComponent (); if (c == null || c instanceof java.awt.Window) throw new IllegalStateException("Wizard panel " + p + " gave a strange component " + c); // NOI18N /* commented out - issue #32927. Replaced by javadoc info in WizardDescriptor.Panel if (c == p) { warnPanelIsComponent(p.getClass()); }*/ //initialize wizardPanel if (!init) { if (c instanceof JComponent) { autoWizardStyle = getBooleanProperty( (JComponent)c, PROP_AUTO_WIZARD_STYLE); if (autoWizardStyle) { wizardPanel = new WizardPanel( getBooleanProperty((JComponent)c, PROP_CONTENT_DISPLAYED), getBooleanProperty((JComponent)c, PROP_HELP_DISPLAYED), getBooleanProperty((JComponent)c, PROP_CONTENT_NUMBERED), getLeftDimension((JComponent)c) ); initBundleProperties(); } } if (propListener == null) propListener = new PropL(); init = true; } //update wizardPanel if (wizardPanel != null) { Component oldComp = wizardPanel.getRightComponent(); if (oldComp != null) oldComp.removePropertyChangeListener(propListener); if (c instanceof JComponent) { setPanelProperties((JComponent)c); wizardPanel.setContent(contentData); wizardPanel.setSelectedIndex(contentSelectedIndex); wizardPanel.setContentBackColor(contentBackColor); wizardPanel.setContentForegroundColor(contentForegroundColor); wizardPanel.setImage(image); wizardPanel.setImageAlignment(imageAlignment); wizardPanel.setHelpURL(helpURL); updateButtonAccessibleDescription(); c.addPropertyChangeListener(WeakListeners.propertyChange(propListener,c)); } if (wizardPanel.getRightComponent() != c) { wizardPanel.setRightComponent(c); if (wizardPanel != getMessage()) { setMessage(wizardPanel); } else { // force revalidate and repaint because the contents of // wizardPanel has changed. See NbPresenter code firePropertyChange(DialogDescriptor.PROP_MESSAGE, null, wizardPanel); } } } else if (c != getMessage ()) setMessage (c); String panelName = c.getName (); if (panelName == null) { panelName = ""; // NOI18N } Object[] args = { panelName, panels.name () }; MessageFormat mf = getTitleFormat (); if (autoWizardStyle) wizardPanel.setPanelName(mf.format(args)); else { setTitle (mf.format (args)); } Component fo = KeyboardFocusManager.getCurrentKeyboardFocusManager ().getFocusOwner (); if (fo != null && (!fo.isEnabled()) && wizardPanel != null) { wizardPanel.requestFocus (); } } /** Shows blocking wait cursor during updateState run */ private void updateStateWithFeedback () { try { showWaitCursor (); updateState(); } finally { showNormalCursor(); } } /** Shows next step in UI of wizards, displays wait cursot during the change. */ private void goToNextStep (Dimension previousSize) { try { showWaitCursor (); boolean alreadyUpdated = false; Font controlFont = (Font)UIManager.getDefaults().get("controlFont"); //NOI18N Integer defaultSize = (Integer) UIManager.get("nbDefaultFontSize"); if (defaultSize == null) { //Plastic look and feel... defaultSize = new Integer(11); } // enable auto-resizing policy only for fonts bigger thne default if ((controlFont != null) && (controlFont.getSize() > defaultSize.intValue())) { //NOI18N Window parentWindow = SwingUtilities.getWindowAncestor((Component)getMessage()); if (parentWindow != null) { // get tree lock to prevent from drawing in between - tries // to minimize flicker (hm, but not very succesfully, don't know why...) synchronized (parentWindow.getTreeLock()) { updateState(); alreadyUpdated = true; resizeWizard(parentWindow, previousSize); } } } if (!alreadyUpdated) { updateState(); } if (wizardPanel != null) { wizardPanel.requestFocus(); } } finally { showNormalCursor(); } } /** Tries to resize wizard wisely if needed. Keeps "display inertia" so that * wizard is only enlarged, not shrinked, and location is changed only when * wizard window exceeds screen bounds after resize. */ private void resizeWizard (Window parentWindow, Dimension prevSize) { Dimension curSize = panels.current().getComponent().getPreferredSize(); // only enlarge if needed, don't shrink if ((curSize.width > prevSize.width) || (curSize.height > prevSize.height)) { Rectangle origBounds = parentWindow.getBounds(); int newWidth = Math.max(origBounds.width + (curSize.width - prevSize.width), origBounds.width); int newHeight = Math.max(origBounds.height + (curSize.height - prevSize.height), origBounds.height); Rectangle screenBounds = Utilities.getUsableScreenBounds(); Rectangle newBounds; // don't allow to exceed screen size, center if needed if (((origBounds.x + newWidth) > screenBounds.width) || ((origBounds.y + newHeight) > screenBounds.height)) { newWidth = Math.min(screenBounds.width, newWidth); newHeight = Math.min(screenBounds.height, newHeight); newBounds = Utilities.findCenterBounds(new Dimension(newWidth, newHeight)); } else { newBounds = new Rectangle(origBounds.x, origBounds.y, newWidth, newHeight); } parentWindow.setBounds(newBounds); parentWindow.invalidate(); parentWindow.validate(); parentWindow.repaint(); } } private void showWaitCursor () { if (wizardPanel == null || wizardPanel.getRootPane () == null) { // if none root pane --> don't set wait cursor return ; } waitingComponent = wizardPanel.getRootPane ().getContentPane (); waitingComponent.setCursor (Cursor.getPredefinedCursor (Cursor.WAIT_CURSOR)); } private void showNormalCursor () { if (waitingComponent == null) { // none waitingComponent --> don't change cursor to normal return ; } waitingComponent.setCursor (null); waitingComponent = null; } /* commented out - issue #32927. Replaced by javadoc info in WizardDescriptor.Panel private static final Set warnedPanelIsComponent = new WeakSet(); // Set private static synchronized void warnPanelIsComponent(Class c) { if (warnedPanelIsComponent.add(c)) { StringBuffer buffer = new StringBuffer(150); buffer.append("WARNING - the WizardDescriptor.Panel implementation "); // NOI18N buffer.append(c.getName()); buffer.append(" provides itself as the result of getComponent().\n"); // NOI18N buffer.append("This hurts performance and can cause a clash when Component.isValid() is overridden.\n"); // NOI18N buffer.append("Please use a separate component class, see details at http://performance.netbeans.org/howto/dialogs/wizard-panels.html."); // NOI18N ErrorManager.getDefault().log(ErrorManager.WARNING, buffer.toString()); } } */ /** Tryes to get property from getProperty() if doesn't succeed then tryes at * supplied JComponents client property. * @param c origin of property * @param s name of property * @return boolean property */ private boolean getBooleanProperty(JComponent c, String s) { Object property = getProperty(s); if (property instanceof Boolean) return ((Boolean)property).booleanValue(); property = c.getClientProperty(s); if (property instanceof Boolean) return ((Boolean)property).booleanValue(); return false; } /** Tryes to get dimension of wizard panel's left pane from getProperty() * if doesn't succeed then tryes at * supplied JComponents client property. * @return Dimension dimension of wizard panel's left pane */ private Dimension getLeftDimension(JComponent c) { Dimension leftDimension; Object property = c.getClientProperty(PROP_LEFT_DIMENSION); if (property instanceof Dimension) leftDimension = (Dimension)property; else leftDimension = new Dimension(198, 233); return leftDimension; } /** Tryes to get properties from getProperty() if doesn't succeed then tryes at * supplied JComponents client properties and store them * to appropriate fields. * @param c origin of property * @param s name of property * @return boolean property */ private void setPanelProperties(JComponent c) { // TODO: Method should be devided into individual setter/getter methods !? Object property = getProperty(PROP_CONTENT_SELECTED_INDEX); if (property instanceof Integer) contentSelectedIndex = ((Integer)property).intValue(); else { property = c.getClientProperty(PROP_CONTENT_SELECTED_INDEX); if (property instanceof Integer) contentSelectedIndex = ((Integer)property).intValue(); } property = getProperty(PROP_CONTENT_DATA); if (property instanceof String[]) contentData = (String[])property; else { property = c.getClientProperty(PROP_CONTENT_DATA); if (property instanceof String[]) contentData = (String[])property; } property = getProperty(PROP_IMAGE); if (property instanceof Image) { image = (Image)property; } else if ((properties == null) || (!properties.containsKey(PROP_IMAGE))) { property = c.getClientProperty(PROP_IMAGE); if (property instanceof Image) image = (Image)property; } property = getProperty(PROP_IMAGE_ALIGNMENT); if (property instanceof String) { imageAlignment = (String)property; } else { property = c.getClientProperty(PROP_IMAGE_ALIGNMENT); if (property instanceof String) imageAlignment = (String)property; } property = getProperty(PROP_CONTENT_BACK_COLOR); if (property instanceof Color) contentBackColor = (Color)property; else { property = c.getClientProperty(PROP_CONTENT_BACK_COLOR); if (property instanceof Color) contentBackColor = (Color)property; } property = getProperty(PROP_CONTENT_FOREGROUND_COLOR); if (property instanceof Color) contentForegroundColor = (Color)property; else { property = c.getClientProperty(PROP_CONTENT_FOREGROUND_COLOR); if (property instanceof Color) contentForegroundColor = (Color)property; } property = c.getClientProperty(PROP_HELP_URL); if (property instanceof URL) helpURL = (URL)property; else if (property == null) helpURL = null; } private void initBundleProperties() { contentBackColor = new Color(getIntFromBundle("INT_WizardBackRed"), // NOI18N getIntFromBundle("INT_WizardBackGreen"), // NOI18N getIntFromBundle("INT_WizardBackBlue")); // NOI18N contentForegroundColor = new Color(getIntFromBundle("INT_WizardForegroundRed"), // NOI18N getIntFromBundle("INT_WizardForegroundGreen"), // NOI18N getIntFromBundle("INT_WizardForegroundBlue")); // NOI18N imageAlignment = bundle.getString("STRING_WizardImageAlignment"); //NOI18N } /** Overrides superclass method. Adds reseting of wizard * for CLOSED_OPTION. */ public void setValue(Object value) { //Bugfix #25820: Call resetWizard to make sure that storeSettings //is called before propertyChange. Object convertedValue = backConvertOption(value); // set new value w/o fire PROP_VALUE change Object oldValue = getValue (); setValueWithoutPCH (convertedValue); // #17360: Reset wizard on CLOSED_OPTION too. if (value == CLOSED_OPTION) { resetWizard (); } // notify listeners about PROP_VALUE change firePropertyChange (PROP_VALUE, oldValue, convertedValue); } /** Resets wizard when after closed/cancelled/finished the wizard dialog. */ private void resetWizard () { if(current != null) { current.storeSettings (settings); current.removeChangeListener(listener); current = null; if (wizardPanel != null) { wizardPanel.resetPreferredSize(); } } panels.removeChangeListener(listener); callUninitialize (); } ResourceBundle bundle = NbBundle.getBundle(WizardDescriptor.class); private int getIntFromBundle(String key) { return Integer.parseInt(bundle.getString(key)); } private static Image getDefaultImage() { Image img = null; if (defaultImage != null) img = (Image)defaultImage.get(); if (img == null) { java.net.URL url = NbBundle.getLocalizedFile( "org.openide.resources.defaultWizard", // NOI18N "gif" // NOI18N ); img = url == null ? null : java.awt.Toolkit.getDefaultToolkit().getImage(url); if (img != null) defaultImage = new WeakReference(img); } return img; } private void updateButtonAccessibleDescription() { String stepName = contentData != null && contentSelectedIndex > 0 && contentSelectedIndex - 1 < contentData.length ? contentData[contentSelectedIndex - 1] : ""; // NOI18N previousButton.getAccessibleContext().setAccessibleDescription( NbBundle.getMessage (WizardDescriptor.class, "ACSD_PREVIOUS", new Integer(contentSelectedIndex), stepName )); stepName = contentData != null && contentSelectedIndex < contentData.length - 1 && contentSelectedIndex + 1 >= 0 ? contentData[contentSelectedIndex + 1] : ""; // NOI18N nextButton.getAccessibleContext().setAccessibleDescription( NbBundle.getMessage (WizardDescriptor.class, "ACSD_NEXT", new Integer(contentSelectedIndex + 2), stepName )); } /** Iterator on the sequence of panels. * @see WizardDescriptor.Panel */ public interface Iterator { /** Get the current panel. * @return the panel */ public Panel current (); /** Get the name of the current panel. * @return the name */ public String name (); /** Test whether there is a next panel. * @return true if so */ public boolean hasNext (); /** Test whether there is a previous panel. * @return true if so */ public boolean hasPrevious (); /** Move to the next panel. * I.e. increment its index, need not actually change any GUI itself. * @exception NoSuchElementException if the panel does not exist */ public void nextPanel (); /** Move to the previous panel. * I.e. decrement its index, need not actually change any GUI itself. * @exception NoSuchElementException if the panel does not exist */ public void previousPanel (); /** Add a listener to changes of the current panel. * The listener is notified when the possibility to move forward/backward changes. * @param l the listener to add */ public void addChangeListener (ChangeListener l); /** Remove a listener to changes of the current panel. * @param l the listener to remove */ public void removeChangeListener (ChangeListener l); } /** One wizard panel with a component on it. * * For good performance, implementation of this interface should be as * lightweight as possible. Defer creation and initialization of * UI component of wizard panel into {@link #getComponent} method. * * Please see complete guide at http://performance.netbeans.org/howto/dialogs/wizard-panels.html */ public interface Panel { /** Get the component displayed in this panel. * * Note; method can be called from any thread, but not concurrently * with other methods of this interface. Please see complete guide at * http://performance.netbeans.org/howto/dialogs/wizard-panels.html for * correct implementation. * * @return the UI component of this wizard panel */ public java.awt.Component getComponent (); /** Help for this panel. * When the panel is active, this is used as the help for the wizard dialog. * @return the help or null if no help is supplied */ public HelpCtx getHelp (); /** Provides the wizard panel with the current data--either * the default data or already-modified settings, if the user used the previous and/or next buttons. * This method can be called multiple times on one instance of WizardDescriptor.Panel. *

The settings object is originally supplied to {@link WizardDescriptor#WizardDescriptor(WizardDescriptor.Iterator,Object)}. * In the case of a TemplateWizard.Iterator panel, the object is * in fact the TemplateWizard. * @param settings the object representing wizard panel state * @exception IllegalStateException if the the data provided * by the wizard are not valid. */ public void readSettings (Object settings); /** Provides the wizard panel with the opportunity to update the * settings with its current customized state. * Rather than updating its settings with every change in the GUI, it should collect them, * and then only save them when requested to by this method. * Also, the original settings passed to {@link #readSettings} should not be modified (mutated); * rather, the object passed in here should be mutated according to the collected changes, * in case it is a copy. * This method can be called multiple times on one instance of WizardDescriptor.Panel. *

The settings object is originally supplied to {@link WizardDescriptor#WizardDescriptor(WizardDescriptor.Iterator,Object)}. * In the case of a TemplateWizard.Iterator panel, the object is * in fact the TemplateWizard. * @param settings the object representing wizard panel state */ public void storeSettings (Object settings); /** Test whether the panel is finished and it is safe to proceed to the next one. * If the panel is valid, the "Next" (or "Finish") button will be enabled. *

Tip: if your panel is actually the component itself * (so {@link #getComponent} returns this), be sure to specifically * override this method, as the unrelated implementation in {@link java.awt.Component#isValid} * if not overridden could cause your wizard to behave erratically. * @return true if the user has entered satisfactory information */ public boolean isValid (); /** Add a listener to changes of the panel's validity. * @param l the listener to add * @see #isValid */ public void addChangeListener (ChangeListener l); /** Remove a listener to changes of the panel's validity. * @param l the listener to remove */ public void removeChangeListener (ChangeListener l); } /** A special interface for panels in middle of the * iterators path that would like to have the finish button * enabled. So both Next and Finish are enabled on panel * implementing this interface. * @deprecated 4.28 Use FinishablePanel instead. */ public interface FinishPanel extends Panel { } /** A special interface for panels that need to do additional * validation when Next or Finish button is clicked. * @since 4.28 */ public interface ValidatingPanel extends Panel { /** * Is called when Next of Finish buttons are clicked and * allows deeper check to find out that panel is in valid * state and it is ok to leave it. * * @throws WizardValidationException when validation fails * @since 4.28 */ public void validate () throws WizardValidationException; } /** A special interface for panel that needs to dynamically enabled * Finish button. * @since 4.28 */ public interface FinishablePanel extends Panel { /** Specify if this panel would enable Finish button. Finish button is * enabled if and only if isValid() returns true and isFinishPanel() * returns true. * * @return Finish button could be enabled * @since 4.28 */ boolean isFinishPanel(); } /** Special iterator that works on an array of Panels. */ public static class ArrayIterator extends Object implements Iterator { /** Array of items. */ private Panel[] panels; /** Index into the array */ private int index; /* Default constructor. It's here to allow subclasses to * be serializable easily. Panel initialization is done * through initializePanels() protected method. */ public ArrayIterator () { panels = initializePanels(); index = 0; } /** Construct an iterator. * @param array the list of panels to use */ public ArrayIterator (Panel[] array) { panels = array; index = 0; } /** Allows subclasses to initialize their arrays of panels when * constructed using default constructor. * (for example during deserialization. * Default implementation returns empty array. */ protected Panel[] initializePanels () { return new Panel[0]; } /* The current panel. */ public Panel current () { return panels[index]; } /* Current name of the panel */ public String name () { return NbBundle.getMessage (WizardDescriptor.class, "CTL_ArrayIteratorName", new Integer (index + 1), new Integer (panels.length)); } /* Is there a next panel? * @return true if so */ public boolean hasNext () { return index < panels.length - 1; } /* Is there a previous panel? * @return true if so */ public boolean hasPrevious () { return index > 0; } /* Moves to the next panel. * @exception NoSuchElementException if the panel does not exist */ public synchronized void nextPanel () { if (index + 1 == panels.length) throw new java.util.NoSuchElementException (); index++; } /* Moves to previous panel. * @exception NoSuchElementException if the panel does not exist */ public synchronized void previousPanel () { if (index == 0) throw new java.util.NoSuchElementException (); index--; } /* Ignores the listener, there are no changes in order of panels. */ public void addChangeListener (ChangeListener l) { } /* Ignored. */ public void removeChangeListener (ChangeListener l) { } /** Resets this iterator to initial state. * Called by subclasses when they need re-initialization of the iterator. */ protected void reset () { index = 0; } } /** * Iterator for a wizard that needs to somehow instantiate new objects. * (This interface can replace * TemplateWizard.Iterator * in a template's declaration.) * @since org.openide/1 4.33 */ public interface InstantiatingIterator extends Iterator { /** Returns set of instantiated objects. * * @throws IOException * @return a set of objects created (the exact type is at the discretion of the caller) */ public Set/*Object*/ instantiate () throws IOException; /** Initializes this iterator, called from WizardDescriptor's constructor. * * @param wizard wizard's descriptor */ public void initialize (WizardDescriptor wizard); /** Uninitializes this iterator, called when the wizard is being * closed, no matter what closing option invoked. * * @param wizard wizard's descriptor */ public void uninitialize (WizardDescriptor wizard); } private boolean lazyValidate (WizardDescriptor.Panel panel, WizardDescriptor.WizardPanel wizard) { if (panel instanceof ValidatingPanel) { ValidatingPanel v = (ValidatingPanel)panel; try { // try validation current panel v.validate(); } catch (WizardValidationException wve) { // cannot continue, notify user if (wizardPanel != null) { wizardPanel.setErrorMessage (wve.getLocalizedMessage ()); } // focus source of this problem if (wve.getSource () != null) { final JComponent comp = (JComponent) wve.getSource (); if (comp.isFocusable ()) { comp.requestFocus (); } } // lazy validation failed return false; } } return true; } /** Listener to changes in the iterator and panels. */ private final class Listener implements ChangeListener, ActionListener { Listener() {} /** Change in the observed objects */ public void stateChanged (ChangeEvent ev) { updateState (); } /** Action listener */ public void actionPerformed (ActionEvent ev) { if (wizardPanel != null) { wizardPanel.setErrorMessage(" "); //NOI18N } if (ev.getSource () == nextButton) { Dimension previousSize = panels.current().getComponent().getSize(); // do lazy validation if (!lazyValidate (panels.current (), wizardPanel)) { // if validation failed => cannot move to next panel return ; } panels.nextPanel (); try { // change UI to show next step, show wait cursor during // the change goToNextStep(previousSize); } catch (IllegalStateException ise) { panels.previousPanel(); if (ise.getMessage() != null) { // this is only for backward compatitility DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(ise.getMessage())); } else { // this should be used (it checks for exception // annotations and severity) ErrorManager.getDefault().notify(ise); } updateState(); } } if (ev.getSource () == previousButton) { panels.previousPanel (); // show wait cursor when updating previous button updateStateWithFeedback (); } if (ev.getSource () == finishButton) { // do lazy validation if (!lazyValidate (panels.current (), wizardPanel)) { // if validation failed => cannot move to next panel return ; } // all is OK // close wizrd finishOption.fireActionPerformed (); Object oldValue = getValue (); setValueWithoutPCH (OK_OPTION); callInstantiate (); resetWizard(); firePropertyChange (PROP_VALUE, oldValue, OK_OPTION); } if (ev.getSource () == cancelButton) { Object oldValue = getValue (); setValueWithoutPCH (CANCEL_OPTION); if (Arrays.asList(getClosingOptions()).contains(cancelButton)) { resetWizard(); } firePropertyChange (PROP_VALUE, oldValue, CANCEL_OPTION); } } } /** Listenes on a users client property changes */ private class PropL implements PropertyChangeListener { PropL() {} /** Accepts client property changes of user component */ public void propertyChange(PropertyChangeEvent e) { if (wizardPanel == null) return; String propName = e.getPropertyName(); setPanelProperties((JComponent)wizardPanel.getRightComponent()); if (propName.equals(PROP_CONTENT_DATA)) { wizardPanel.setContent(contentData); updateButtonAccessibleDescription(); } else if (propName.equals(PROP_CONTENT_SELECTED_INDEX)) { wizardPanel.setSelectedIndex(contentSelectedIndex); updateButtonAccessibleDescription(); } else if (propName.equals(PROP_CONTENT_BACK_COLOR)) wizardPanel.setContentBackColor(contentBackColor); else if (propName.equals(PROP_CONTENT_FOREGROUND_COLOR)) wizardPanel.setContentForegroundColor(contentForegroundColor); else if (propName.equals(PROP_IMAGE)) wizardPanel.setImage(image); else if (propName.equals(PROP_IMAGE_ALIGNMENT)) wizardPanel.setImageAlignment(imageAlignment); else if (propName.equals(PROP_HELP_URL)) wizardPanel.setHelpURL(helpURL); } } // helper methods which call to InstantiatingIterator private void callInitialize () { assert panels != null; if (panels instanceof InstantiatingIterator) { ((InstantiatingIterator)panels).initialize (this); } newObjects = Collections.EMPTY_SET; } private void callUninitialize () { assert panels != null; if (panels instanceof InstantiatingIterator) { ((InstantiatingIterator)panels).uninitialize (this); } } private void callInstantiate () { assert panels != null; // bugfix #44444, force store settings before do instantiate new objects panels.current ().storeSettings (this); if (panels instanceof InstantiatingIterator) { try { newObjects = ((InstantiatingIterator)panels).instantiate (); } catch (IOException ioe) { ErrorManager.getDefault ().notify (ioe); } } } // end of calling to InstantiatingIterator /** Panel which paints image as its background. */ private static class ImagedPanel extends JComponent implements Accessible, Runnable { /** background image */ Image image; /** helper variables for passing image between threads and painting * methods */ Image tempImage, image2Load; /** true if default image is used */ boolean isDefault = false; /** true if loading of image is in progress, false otherwise */ boolean loadPending = false; boolean north = true; /** sync lock for image variables access */ private final Object IMAGE_LOCK = new Object(); /** Constrcuts panel with given image on background. * @param im background image, null means default image */ public ImagedPanel(Image im) { setImage(im); setLayout(new BorderLayout()); setOpaque(true); } /** Overriden to paint backround image */ protected void paintComponent(Graphics graphics) { graphics.setColor(getBackground()); graphics.fillRect(0, 0, getWidth(), getHeight()); if (image != null) { graphics.drawImage(image, 0, north ? 0 : (getHeight() - image.getHeight(null)), this); } else if (image2Load != null) { loadImageInBackground(image2Load); image2Load = null; } } public void setImageAlignment(String align) { north = "North".equals(align); // NOI18N } /** Sets background image for this component. Image will be loaded * asynchronously if not loaded yet. Null means default image. */ public void setImage(Image im) { if (im != null) { loadImage(im); isDefault = false; return; } if (!isDefault) { loadImage(getDefaultImage()); isDefault = true; } } private void loadImage (Image im) { // check image and just set variable if fully loaded already MediaTracker mt = new MediaTracker(this); mt.addImage(im, 0); if (mt.checkID(0)) { image = im; if (isShowing()) { repaint(); } return; } // start loading in background or just mark that loading should // start when paint is invoked if (isShowing()) { loadImageInBackground(im); } else { synchronized (IMAGE_LOCK) { image = null; } image2Load = im; } } private void loadImageInBackground (Image image) { synchronized (IMAGE_LOCK) { tempImage = image; // coalesce with previous task if hasn't really started yet if (loadPending) { return; } loadPending = true; } // 30ms is safety time to ensure code will run asynchronously RequestProcessor.getDefault().post(this, 30); } /** Loads image stored in image2Load variable. * Then invokes repaint when image is fully loaded. */ public void run () { Image localImage = null; // grab value synchronized (IMAGE_LOCK) { localImage = tempImage; tempImage = null; loadPending = false; } // actually loads image ImageIcon localImageIcon = new ImageIcon(localImage); boolean shouldRepaint = false; synchronized (IMAGE_LOCK) { // don't commit results if another loading was started after us if (!loadPending) { image = localImageIcon.getImage(); // keep repaint call out of sync section shouldRepaint = true; } } if (shouldRepaint) { repaint(); } } } /** Text list cell renderer. Wraps text of items at specified width. Allows numbering * of items. */ private static class WrappedCellRenderer extends JPanel implements ListCellRenderer { JTextArea ta = new JTextArea(); JLabel numberLabel; int selected = -1; boolean contentNumbered; int taWidth; /** * @param contentNumbered Whether content will be numbered * @param wrappingWidth Width of list item. */ private WrappedCellRenderer(boolean contentNumbered, int wrappingWidth) { super(new BorderLayout()); this.contentNumbered = contentNumbered; ta.setOpaque(false); ta.setEditable(false); ta.setLineWrap(true); ta.setWrapStyleWord(true); ta.setFont(UIManager.getFont("Label.font")); // NOI18N ta.getAccessibleContext().setAccessibleDescription(""); // NOI18N taWidth = wrappingWidth - 12 - 12; numberLabel = new JLabel() { protected void paintComponent(Graphics g) { super.paintComponent(g); // #9804. Draw bullet if the content is not numbered. if(!WrappedCellRenderer.this.contentNumbered) { java.awt.Rectangle rect = g.getClipBounds(); g.fillOval(rect.x, rect.y, 7, 7); } } }; numberLabel.setLabelFor(ta); // a11y numberLabel.setHorizontalAlignment(SwingConstants.LEFT); numberLabel.setVerticalAlignment(SwingConstants.TOP); numberLabel.setFont(ta.getFont()); numberLabel.setOpaque(false); numberLabel.setPreferredSize(new Dimension(25, 0)); add(numberLabel, BorderLayout.WEST); taWidth -= 25; Insets taInsets = ta.getInsets(); ta.setSize(taWidth, taInsets.top + taInsets.bottom + 1); add(ta, BorderLayout.CENTER); setBorder(BorderFactory.createEmptyBorder(0, 12, 0, 12)); setOpaque(false); } public Component getListCellRendererComponent( JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { if (index == selected) { numberLabel.setFont(numberLabel.getFont().deriveFont(Font.BOLD)); ta.setFont(ta.getFont().deriveFont(Font.BOLD)); } else { numberLabel.setFont(numberLabel.getFont().deriveFont(Font.PLAIN)); ta.setFont(ta.getFont().deriveFont(Font.PLAIN)); } if (contentNumbered) { numberLabel.setText(Integer.toString(index + 1) + "."); // NOI18N } // #21322: on JDK1.4 wrapping width is cleared between two rendering runs Insets taInsets = ta.getInsets(); ta.setSize(taWidth, taInsets.top + taInsets.bottom + 1); ta.setText((String)value); return this; } private void setSelectedIndex(int i) { selected = i; } private void setForegroundColor(Color color) { if (numberLabel != null) { numberLabel.setForeground(color); numberLabel.setBackground(color); } ta.setForeground(color); } } /** Wizard panel. Allows auto layout of content, wizard panel name and input panel. */ private static class WizardPanel extends JPanel { /** Users panel is inserted into this panel. */ private JPanel rightPanel = new JPanel(new BorderLayout()); /** Name of the users panel. */ private JLabel panelName = new JLabel("Step"); //NOI18N /** List of content. */ private JList contentList; /** Users component. Should be held for removing from rightPanel */ private Component rightComponent; /** Panel which paints image */ private ImagedPanel contentPanel; /** Name of content. Can be switched off. */ private JPanel contentLabelPanel; /** Wrapped list cell renderer */ private WrappedCellRenderer cellRenderer; /** Tabbed pane is used only when both content and help are displayed */ private JTabbedPane tabbedPane; /** HTML Browser is used only when help is displayed in the left pane */ private HtmlBrowser htmlBrowser; /** Each wizard panel have to be larger or same as this */ private Dimension cachedDimension; /** Label of steps pane */ private JLabel label; /** Selected index of content */ private int selectedIndex; private javax.swing.JLabel m_lblMessage; /** Creates new WizardPanel. * @param contentDisplayed whether content will be displayed in the left pane * @param helpDisplayed whether help will be displayed in the left pane * @param contentNumbered whether content will be numbered * @param leftDimension dimension of content or help pane */ public WizardPanel(boolean contentDisplayed, boolean helpDispalyed, boolean contentNumbered, Dimension leftDimension) { super(new BorderLayout()); initComponents(contentDisplayed, helpDispalyed, contentNumbered, leftDimension); setOpaque(false); resetPreferredSize(); } private void initComponents(boolean contentDisplayed, boolean helpDisplayed, boolean contentNumbered, Dimension leftDimension) { if (contentDisplayed) { createContentPanel(contentNumbered, leftDimension); if (!helpDisplayed) add(contentPanel, BorderLayout.WEST); } if (helpDisplayed) { htmlBrowser = new BoundedHtmlBrowser(leftDimension); htmlBrowser.setPreferredSize(leftDimension); if (!contentDisplayed) add(htmlBrowser, BorderLayout.WEST); } if (helpDisplayed && contentDisplayed) { tabbedPane = new JTabbedPane(JTabbedPane.BOTTOM); tabbedPane.addTab(NbBundle.getMessage(WizardDescriptor.class, "CTL_ContentName"), contentPanel); tabbedPane.addTab(NbBundle.getMessage(WizardDescriptor.class, "CTL_HelpName"), htmlBrowser); tabbedPane.setEnabledAt(1, false); tabbedPane.setOpaque(false); // tabbedPane.setPreferredSize(leftDimension); add(tabbedPane, BorderLayout.WEST); } panelName.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, panelName.getForeground())); panelName.setFont(panelName.getFont().deriveFont(Font.BOLD)); JPanel labelPanel = new JPanel(new BorderLayout()); labelPanel.add(panelName, BorderLayout.NORTH); labelPanel.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 11)); rightPanel.setBorder(BorderFactory.createEmptyBorder(0, 12, 11, 11)); panelName.setLabelFor(labelPanel); Color c = javax.swing.UIManager.getColor("nb.errorForeground"); //NOI18N if (c == null) { c = new Color(89,79,191); // RGB suggested by Bruce in #28466 } JPanel errorPanel = new JPanel(new BorderLayout()); errorPanel.setBorder(BorderFactory.createEmptyBorder(0, 12, 12, 11)); m_lblMessage = new javax.swing.JLabel(" "); //NOI18N m_lblMessage.setForeground(c); errorPanel.add(m_lblMessage, BorderLayout.CENTER); JPanel fullRightPanel = new JPanel(new BorderLayout()); fullRightPanel.add(labelPanel, BorderLayout.NORTH); fullRightPanel.add(rightPanel, BorderLayout.CENTER); fullRightPanel.add(errorPanel, BorderLayout.SOUTH); JSeparator sep = new JSeparator(); sep.setForeground(Color.darkGray); add(fullRightPanel, BorderLayout.CENTER); add(sep, BorderLayout.SOUTH); } public void setErrorMessage(String msg) { m_lblMessage.setText(msg); if (msg != null && msg.trim ().length () > 0) { m_lblMessage.setToolTipText (msg); } else { m_lblMessage.setToolTipText (null); } } /** Creates content panel. * @param contentNumbered boolean whether content will be numbered * @param leftDimension Dimension dimension of content pane */ private void createContentPanel(boolean contentNumbered, Dimension leftDimension) { contentList = new JList(); cellRenderer = new WrappedCellRenderer(contentNumbered, leftDimension.width); cellRenderer.setOpaque(false); contentList.setCellRenderer(cellRenderer); contentList.setOpaque(false); contentList.setEnabled(false); contentList.getAccessibleContext().setAccessibleDescription(""); // NOI18N JScrollPane scroll = new JScrollPane(contentList); scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); scroll.getViewport().setOpaque(false); scroll.setBorder(null); scroll.setOpaque(false); label = new JLabel(NbBundle.getMessage(WizardDescriptor.class, "CTL_ContentName")); label.setForeground(Color.white); label.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, label.getForeground())); label.setFont(label.getFont().deriveFont(Font.BOLD)); contentLabelPanel = new JPanel(new BorderLayout()); contentLabelPanel.setBorder(BorderFactory.createEmptyBorder(12, 12, 11, 11)); contentLabelPanel.setOpaque(false); contentLabelPanel.add(label, BorderLayout.NORTH); contentPanel = new ImagedPanel(null); contentPanel.add(contentLabelPanel, BorderLayout.NORTH); contentPanel.add(scroll, BorderLayout.CENTER); contentPanel.setPreferredSize(leftDimension); label.setLabelFor(contentList); } /** Setter for lists items. * @param content Array of list items. */ public void setContent(final String[] content) { final JList list = contentList; if(list == null) { return; } // #18055: Ensure it runs in AWT thread. // Remove this when component handling will be assured // by other means that runs always in AWT. Mutex.EVENT.writeAccess(new Runnable() { public void run() { list.setListData(content); list.revalidate(); list.repaint(); contentLabelPanel.setVisible(content.length > 0); } }); } /** Setter for selected list item. * @param index Index of selected item in the list. */ public void setSelectedIndex(final int index) { selectedIndex = index; if (cellRenderer != null) { cellRenderer.setSelectedIndex(index); final JList list = contentList; if(list == null) { return; } // #18055. See previous #18055 comment. Mutex.EVENT.readAccess(new Runnable() { public void run() { list.ensureIndexIsVisible(index); // Fix of #10787. // This is workaround for swing bug - BasicListUI doesn't ask for preferred // size of rendered list cell as a result of property selectedIndex change. // It does only on certain JList property changes (e.g. fixedCellWidth). // Maybe subclassing BasicListUI could be better fix. list.setFixedCellWidth(0); list.setFixedCellWidth(-1); } }); } } /** Setter for content background color. * @param color content background color. */ public void setContentBackColor(Color color) { if (contentPanel != null) contentPanel.setBackground(color); } /** Setter for content foreground color. * @param color content foreground color. */ public void setContentForegroundColor(Color color) { if (cellRenderer == null) return; cellRenderer.setForegroundColor(color); label.setForeground(color); label.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, label.getForeground())); } /** Setter for content background image. * @param image content background image */ public void setImage(Image image) { if (contentPanel != null) contentPanel.setImage(image); } /** Setter for image alignment. * @param align image alignment - 'North', 'South' */ public void setImageAlignment(String align) { if (contentPanel != null) contentPanel.setImageAlignment(align); } /** Setter for user's component. * @param c user's component */ public void setRightComponent(Component c) { if (rightComponent != null) rightPanel.remove(rightComponent); rightComponent = c; rightPanel.add(rightComponent, BorderLayout.CENTER); // validate(); } /** Getter for user's component. * @return Component user's component */ public Component getRightComponent() { return rightComponent; } /** Setter for wizard panel name. * @param name panel name */ public void setPanelName(String name) { panelName.setText(name); } /** Setter for help URL. * @param helpURL help URL */ public void setHelpURL(URL helpURL) { if (htmlBrowser == null) return; if (helpURL != null) { if (!helpURL.equals(htmlBrowser.getDocumentURL())) htmlBrowser.setURL(helpURL); if (tabbedPane != null) tabbedPane.setEnabledAt(tabbedPane.indexOfComponent(htmlBrowser), true); } else if (tabbedPane != null){ tabbedPane.setSelectedComponent(contentPanel); tabbedPane.setEnabledAt(tabbedPane.indexOfComponent(htmlBrowser), false); } } public void resetPreferredSize() { cachedDimension = new Dimension(600, 365); } public Dimension getPreferredSize() { Dimension dim = super.getPreferredSize(); if (dim.height > cachedDimension.height) cachedDimension.height = dim.height; if (dim.width > cachedDimension.width) cachedDimension.width = dim.width; return cachedDimension; } /** Overriden to delegate call to user component. */ public void requestFocus() { if (rightComponent != null && rightComponent.isDisplayable()) { JComponent comp = (JComponent)rightComponent; Container rootAnc = comp.getFocusCycleRootAncestor(); FocusTraversalPolicy policy = rootAnc.getFocusTraversalPolicy(); Component focus = policy.getComponentAfter(comp.getFocusCycleRootAncestor(), comp); if (focus != null) { focus.requestFocus(); } else { comp.requestFocus(); } } else super.requestFocus(); } /** Overriden to delegate call to user component. */ public boolean requestDefaultFocus() { if (rightComponent instanceof JComponent) { return ((JComponent)rightComponent).requestDefaultFocus(); } return super.requestDefaultFocus(); } public javax.accessibility.AccessibleContext getAccessibleContext() { if (accessibleContext == null) { accessibleContext = new AccessibleWizardPanel(); } return accessibleContext; } private class AccessibleWizardPanel extends AccessibleJPanel { AccessibleWizardPanel() {} public String getAccessibleDescription() { if (accessibleDescription != null) { return accessibleDescription; } if (rightComponent instanceof Accessible) { if (rightComponent.getAccessibleContext().getAccessibleDescription() == null) { return null; } return NbBundle.getMessage(WizardDescriptor.class, "ACSD_WizardPanel", new Integer(selectedIndex + 1), panelName.getText(), rightComponent.getAccessibleContext().getAccessibleDescription() ); } return super.getAccessibleDescription(); } } } /** Overriden to return wished preferred size */ private static class BoundedHtmlBrowser extends HtmlBrowser { Dimension dim; public BoundedHtmlBrowser(Dimension d) { super(false, false); dim = d; } public Dimension getPreferredSize() { return dim; } } // support methods for xtesting final void doNextClick () { if (nextButton.isEnabled ()) { nextButton.doClick (); } } final void doPreviousClick () { if (previousButton.isEnabled ()) { previousButton.doClick (); } } final void doFinishClick () { if (finishButton.isEnabled ()) { finishButton.doClick (); } } final void doCancelClick () { if (cancelButton.isEnabled ()) { cancelButton.doClick (); } } // helper method, might be removed from code // returns false if Next button is disabled final boolean isNextEnabled () { return nextButton.isEnabled (); } // helper method, might be removed from code // returns false if Finish button is disabled final boolean isFinishEnabled () { return finishButton.isEnabled (); } // helper, make possible close wizard as finish static class FinishAction extends Object { ActionListener listner; public void addActionListener (ActionListener ac) { listner = ac; } public void removeActionListener (ActionListener ac) { listner = null; } public void fireActionPerformed () { if (listner != null) { listner.actionPerformed (new ActionEvent (this, 0, "")); } } } }

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.