0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.openide;
0043:
0044: import java.awt.BorderLayout;
0045: import java.awt.Color;
0046: import java.awt.Component;
0047: import java.awt.Container;
0048: import java.awt.Cursor;
0049: import java.awt.Dimension;
0050: import java.awt.FocusTraversalPolicy;
0051: import java.awt.Font;
0052: import java.awt.Graphics;
0053: import java.awt.Image;
0054: import java.awt.Insets;
0055: import java.awt.KeyboardFocusManager;
0056: import java.awt.MediaTracker;
0057: import java.awt.Rectangle;
0058: import java.awt.Window;
0059: import java.awt.event.ActionEvent;
0060: import java.awt.event.ActionListener;
0061: import java.awt.event.KeyEvent;
0062: import java.awt.event.WindowEvent;
0063: import java.awt.event.WindowListener;
0064: import java.beans.PropertyChangeEvent;
0065: import java.beans.PropertyChangeListener;
0066: import java.io.IOException;
0067: import java.net.URL;
0068: import java.text.MessageFormat;
0069: import java.util.Arrays;
0070: import java.util.Collections;
0071: import java.util.HashMap;
0072: import java.util.List;
0073: import java.util.Map;
0074: import java.util.ResourceBundle;
0075: import java.util.Set;
0076: import java.util.logging.Level;
0077: import java.util.logging.Logger;
0078: import javax.accessibility.Accessible;
0079: import javax.swing.BorderFactory;
0080: import javax.swing.ImageIcon;
0081: import javax.swing.JButton;
0082: import javax.swing.JComponent;
0083: import javax.swing.JLabel;
0084: import javax.swing.JList;
0085: import javax.swing.JPanel;
0086: import javax.swing.JProgressBar;
0087: import javax.swing.JScrollPane;
0088: import javax.swing.JSeparator;
0089: import javax.swing.JTabbedPane;
0090: import javax.swing.JTextArea;
0091: import javax.swing.KeyStroke;
0092: import javax.swing.ListCellRenderer;
0093: import javax.swing.SwingConstants;
0094: import javax.swing.SwingUtilities;
0095: import javax.swing.UIManager;
0096: import javax.swing.event.ChangeEvent;
0097: import javax.swing.event.ChangeListener;
0098: import org.openide.awt.HtmlBrowser;
0099: import org.openide.awt.Mnemonics;
0100: import org.openide.util.Exceptions;
0101: import org.openide.util.HelpCtx;
0102: import org.openide.util.Mutex;
0103: import org.openide.util.NbBundle;
0104: import org.openide.util.RequestProcessor;
0105: import org.openide.util.Utilities;
0106: import org.openide.util.WeakListeners;
0107: import org.netbeans.api.progress.ProgressHandle;
0108: import org.netbeans.api.progress.ProgressHandleFactory;
0109:
0110: /**
0111: * Implements a basic "wizard" GUI system.
0112: * A list of <em>wizard panels</em> may be specified and these
0113: * may be traversed at the proper times using the "Previous"
0114: * and "Next" buttons (or "Finish" on the last one).
0115: * @see DialogDisplayer#createDialog
0116: * @see <a href="doc-files/wizard-guidebook.html">Wizard Guidebook
0117: * (describes the set of properties controlling the display of wizard panels)</a>
0118: */
0119: public class WizardDescriptor extends DialogDescriptor {
0120: /** "Next" button option.
0121: * @see #setOptions */
0122: public static final Object NEXT_OPTION = new String("NEXT_OPTION"); // NOI18N
0123:
0124: /** "Finish" button option.
0125: * @see #setOptions */
0126: public static final Object FINISH_OPTION = OK_OPTION;
0127:
0128: /** "Previous" button option.
0129: * @see #setOptions */
0130: public static final Object PREVIOUS_OPTION = new String(
0131: "PREVIOUS_OPTION"); // NOI18N
0132: private static final ActionListener CLOSE_PREVENTER = new ActionListener() {
0133: public void actionPerformed(ActionEvent evt) {
0134: }
0135:
0136: @Override
0137: public String toString() {
0138: return "CLOSE_PREVENTER"; // NOI18N
0139: }
0140: };
0141:
0142: /** <CODE>Boolean</CODE> property. The value is taken from <CODE>WizardDescriptor.getProperty()</CODE> or
0143: * <CODE>((JComponent)Panel.getComponent()).getClientProperty()</CODE> in this order.
0144: * Set to <CODE>true</CODE> for enabling other properties. It is relevant only on
0145: * initialization (client property in first panel).
0146: * When false or not present in JComponent.getClientProperty(), then supplied panel is
0147: * used directly without content, help or panel name auto layout.
0148: */
0149: private static final String PROP_AUTO_WIZARD_STYLE = "WizardPanel_autoWizardStyle"; // NOI18N
0150:
0151: /** <CODE>Boolean</CODE> property. The value is taken from <CODE>WizardDescriptor.getProperty()</CODE> or
0152: * <CODE>((JComponent)Panel.getComponent()).getClientProperty()</CODE> in this order.
0153: * Set to <CODE>true</CODE> for showing help pane in the left pane. It is relevant only on
0154: * initialization (client property in first panel).
0155: */
0156: private static final String PROP_HELP_DISPLAYED = "WizardPanel_helpDisplayed"; // NOI18N
0157:
0158: /** <CODE>Boolean</CODE> property. The value is taken from <CODE>WizardDescriptor.getProperty()</CODE> or
0159: * <CODE>((JComponent)Panel.getComponent()).getClientProperty()</CODE> in this order.
0160: * Set to <CODE>true</CODE> for showing content pane in the left pane. It is relevant only on
0161: * initialization (client property in first panel).
0162: */
0163: private static final String PROP_CONTENT_DISPLAYED = "WizardPanel_contentDisplayed"; // NOI18N
0164:
0165: /** <CODE>Boolean</CODE> property. The value is taken from <CODE>WizardDescriptor.getProperty()</CODE> or
0166: * <CODE>((JComponent)Panel.getComponent()).getClientProperty()</CODE> in this order.
0167: * Set to <CODE>true</CODE> for displaying numbers in the content. It is relevant only on
0168: * initialization (client property in first panel).
0169: */
0170: private static final String PROP_CONTENT_NUMBERED = "WizardPanel_contentNumbered"; // NOI18N
0171:
0172: /** <CODE>Integer</CODE> property. The value is taken from <CODE>WizardDescriptor.getProperty()</CODE> or
0173: * <CODE>((JComponent)Panel.getComponent()).getClientProperty()</CODE> in this order.
0174: * Represents index of content item which will be highlited.
0175: */
0176: private static final String PROP_CONTENT_SELECTED_INDEX = "WizardPanel_contentSelectedIndex"; // NOI18N
0177:
0178: /** <CODE>String[]</CODE> property. The value is taken from <CODE>WizardDescriptor.getProperty()</CODE> or
0179: * <CODE>((JComponent)Panel.getComponent()).getClientProperty()</CODE> in this order.
0180: * Represents array of content items.
0181: */
0182: private static final String PROP_CONTENT_DATA = "WizardPanel_contentData"; // NOI18N
0183:
0184: /** <CODE>Color</CODE> property. The value is taken from <CODE>WizardDescriptor.getProperty()</CODE> or
0185: * <CODE>((JComponent)Panel.getComponent()).getClientProperty()</CODE> in this order.
0186: * Set to background color of content pane.
0187: */
0188: private static final String PROP_CONTENT_BACK_COLOR = "WizardPanel_contentBackColor"; // NOI18N
0189:
0190: /** <CODE>Color</CODE> property. The value is taken from <CODE>WizardDescriptor.getProperty()</CODE> or
0191: * <CODE>((JComponent)Panel.getComponent()).getClientProperty()</CODE> in this order.
0192: * Set to foreground color of content pane.
0193: */
0194: private static final String PROP_CONTENT_FOREGROUND_COLOR = "WizardPanel_contentForegroundColor"; // NOI18N
0195:
0196: /** <CODE>Image</CODE> property. The value is taken from <CODE>WizardDescriptor.getProperty()</CODE> or
0197: * <CODE>((JComponent)Panel.getComponent()).getClientProperty()</CODE> in this order.
0198: * Set to image which should be displayed in the left pane (behind the content).
0199: */
0200: private static final String PROP_IMAGE = "WizardPanel_image"; // NOI18N
0201:
0202: /** <CODE>String</CODE> property. The value is taken from <CODE>WizardDescriptor.getProperty()</CODE> or
0203: * <CODE>((JComponent)Panel.getComponent()).getClientProperty()</CODE> in this order.
0204: * Set to side where the image should be drawn.
0205: */
0206: private static final String PROP_IMAGE_ALIGNMENT = "WizardPanel_imageAlignment"; // NOI18N
0207:
0208: /** <CODE>Dimension</CODE> property. The value is taken from <CODE>WizardDescriptor.getProperty()</CODE> or
0209: * <CODE>((JComponent)Panel.getComponent()).getClientProperty()</CODE> in this order.
0210: * Dimension of left pane, should be same as dimension of <CODE>PROP_IMAGE</CODE>.
0211: * It is relevant only on initialization (client property in first panel).
0212: */
0213: private static final String PROP_LEFT_DIMENSION = "WizardPanel_leftDimension"; // NOI18N
0214:
0215: /** <CODE>URL</CODE> property. The value is taken from <CODE>WizardDescriptor.getProperty()</CODE> or
0216: * <CODE>((JComponent)Panel.getComponent()).getClientProperty()</CODE> in this order.
0217: * Represents URL of help displayed in left pane.
0218: */
0219: private static final String PROP_HELP_URL = "WizardPanel_helpURL"; // NOI18N
0220:
0221: /** <CODE>String</CODE> property. The value is taken from <CODE>WizardDescriptor.getProperty()</CODE>.
0222: * If it contains non-null value the String is displayed the bottom of the wizard
0223: * and should inform user why the panel is invalid and why the Next/Finish
0224: * buttons were disabled.
0225: * @since 3.39
0226: */
0227: private static final String PROP_ERROR_MESSAGE = "WizardPanel_errorMessage"; // NOI18N
0228:
0229: private static Logger err = Logger.getLogger(WizardDescriptor.class
0230: .getName());
0231:
0232: /** real buttons to be placed instead of the options */
0233: private final JButton nextButton = new JButton();
0234: private final JButton finishButton = new JButton();
0235: private final JButton cancelButton = new JButton();
0236: private final JButton previousButton = new JButton();
0237: private FinishAction finishOption;
0238: private Set /*<Object>*/newObjects = Collections.EMPTY_SET;
0239:
0240: /** a component with wait cursor */
0241: private Component waitingComponent;
0242: private boolean changeStateInProgress = false;
0243:
0244: /** Whether wizard panel will be constructed from <CODE>WizardDescriptor.getProperty()</CODE>/
0245: * <CODE>(JComponent)Panel.getComponent()</CODE> client properties or returned
0246: * <CODE>Component</CODE> will be inserted to wizard dialog directly.
0247: */
0248: private boolean autoWizardStyle = false;
0249:
0250: /** Whether properties from first <CODE>(JComponent)Panel.getComponent()</CODE>
0251: * have been initialized.
0252: */
0253: private boolean init = false;
0254:
0255: /** Panel which is used when in <CODE>AUTO_WIZARD_STYLE</CODE> mode.*/
0256: private WizardPanel wizardPanel;
0257:
0258: /** Image */
0259: private Image image;
0260:
0261: /** Content data */
0262: private String[] contentData = new String[] {};
0263:
0264: /** Selected content index */
0265: private int contentSelectedIndex = -1;
0266:
0267: /** Background color*/
0268: private Color contentBackColor;
0269:
0270: /** Foreground color*/
0271: private Color contentForegroundColor;
0272:
0273: /** Help URL displayed in the left pane */
0274: private URL helpURL;
0275:
0276: /** Listener on a user component client property changes*/
0277: private PropL propListener;
0278:
0279: /** 'North' or 'South' */
0280: private String imageAlignment = "North"; // NOI18N
0281:
0282: /** Iterator between panels in the wizard and its settings */
0283: private SettingsAndIterator<?> data;
0284:
0285: /** Change listener that invokes method update state */
0286: private ChangeListener weakChangeListener;
0287: private PropertyChangeListener weakPropertyChangeListener;
0288: private ActionListener weakNextButtonListener;
0289: private ActionListener weakPreviousButtonListener;
0290: private ActionListener weakFinishButtonListener;
0291: private ActionListener weakCancelButtonListener;
0292:
0293: // base listener which won't be directly attached, will only wrapped by WeakListener
0294: private Listener baseListener;
0295:
0296: /** message format to create title of the document */
0297: private MessageFormat titleFormat;
0298:
0299: /** hashtable with additional settings that is usually used
0300: * by Panels to store their data
0301: */
0302: private Map<String, Object> properties;
0303: ResourceBundle bundle = NbBundle.getBundle(WizardDescriptor.class);
0304:
0305: /** Request processor that is used for asynchronous jobs (background validation,
0306: * asynchronous instantiation i.e.) and supports Thread.interrupted().
0307: * It's package-private to accessible for unit tests.
0308: */
0309: static final RequestProcessor ASYNCHRONOUS_JOBS_RP = new RequestProcessor(
0310: "wizard-descriptor-asynchronous-jobs", 1, true); // NOI18N
0311:
0312: private RequestProcessor.Task backgroundValidationTask;
0313:
0314: private boolean validationRuns;
0315:
0316: private ProgressHandle handle;
0317:
0318: private static final String PROGRESS_BAR_DISPLAY_NAME = NbBundle
0319: .getMessage(WizardDescriptor.class,
0320: "CTL_InstantiateProgress_Title"); // NOI18N
0321:
0322: private ActionListener escapeActionListener;
0323:
0324: {
0325: // button init
0326: ResourceBundle b = NbBundle.getBundle("org.openide.Bundle"); // NOI18N
0327: Mnemonics.setLocalizedText(nextButton, b.getString("CTL_NEXT"));
0328: Mnemonics.setLocalizedText(previousButton, b
0329: .getString("CTL_PREVIOUS"));
0330: Mnemonics.setLocalizedText(finishButton, b
0331: .getString("CTL_FINISH"));
0332: finishButton.getAccessibleContext().setAccessibleDescription(
0333: b.getString("ACSD_FINISH"));
0334: Mnemonics.setLocalizedText(cancelButton, b
0335: .getString("CTL_CANCEL"));
0336: cancelButton.getAccessibleContext().setAccessibleDescription(
0337: b.getString("ACSD_CANCEL"));
0338:
0339: finishButton.setDefaultCapable(true);
0340: nextButton.setDefaultCapable(true);
0341: previousButton.setDefaultCapable(false);
0342: cancelButton.setDefaultCapable(false);
0343: }
0344:
0345: /** Create a new wizard from a fixed list of panels, passing some settings to the panels.
0346: * @param wizardPanels the panels to use
0347: * @param settings the settings to pass to panels, or <code>null</code>
0348: * @see #WizardDescriptor(WizardDescriptor.Iterator, Object)
0349: */
0350: public <Data> WizardDescriptor(Panel<Data>[] wizardPanels,
0351: Data settings) {
0352: this (new SettingsAndIterator<Data>(new ArrayIterator<Data>(
0353: wizardPanels), settings));
0354: }
0355:
0356: /** Create a new wizard from a fixed list of panels with settings
0357: * defaulted to <CODE>this</CODE>.
0358: *
0359: * @param wizardPanels the panels to use
0360: * @see #WizardDescriptor(WizardDescriptor.Iterator, Object)
0361: */
0362: public WizardDescriptor(Panel<WizardDescriptor>[] wizardPanels) {
0363: this (SettingsAndIterator
0364: .create(new ArrayIterator<WizardDescriptor>(
0365: wizardPanels)));
0366: }
0367:
0368: /** Create wizard for a sequence of panels, passing some settings to the panels.
0369: * @param panels iterator over all {@link WizardDescriptor.Panel}s that can appear in the wizard
0370: * @param settings the settings to provide to the panels (may be any data understood by them)
0371: * @see WizardDescriptor.Panel#readSettings
0372: * @see WizardDescriptor.Panel#storeSettings
0373: */
0374: public <Data> WizardDescriptor(Iterator<Data> panels, Data settings) {
0375: this (new SettingsAndIterator<Data>(panels, settings));
0376: }
0377:
0378: /** Constructor for subclasses. The expected use is to call this
0379: * constructor and then call {@link #setPanelsAndSettings} to provide
0380: * the right iterator, panels and data the wizard should use. This
0381: * allows to eliminate unchecked warnings as described in
0382: * <a href="http://www.netbeans.org/issues/show_bug.cgi?id=102261">issue 102261</a>.
0383: * @since 7.4
0384: */
0385: protected WizardDescriptor() {
0386: this (SettingsAndIterator.empty());
0387: }
0388:
0389: private <Data> WizardDescriptor(SettingsAndIterator<Data> data) {
0390: super ("", "", true, DEFAULT_OPTION, null, CLOSE_PREVENTER); // NOI18N
0391:
0392: this .data = data;
0393:
0394: baseListener = new Listener();
0395:
0396: weakNextButtonListener = WeakListeners.create(
0397: ActionListener.class, baseListener, nextButton); // NOI18N
0398: weakPreviousButtonListener = WeakListeners.create(
0399: ActionListener.class, baseListener, previousButton); // NOI18N
0400: weakFinishButtonListener = WeakListeners.create(
0401: ActionListener.class, baseListener, finishButton); // NOI18N
0402: weakCancelButtonListener = WeakListeners.create(
0403: ActionListener.class, baseListener, cancelButton); // NOI18N
0404:
0405: nextButton.addActionListener(weakNextButtonListener);
0406: previousButton.addActionListener(weakPreviousButtonListener);
0407: finishButton.addActionListener(weakFinishButtonListener);
0408: cancelButton.addActionListener(weakCancelButtonListener);
0409:
0410: finishOption = new WizardDescriptor.FinishAction();
0411:
0412: super .setOptions(new Object[] { previousButton, nextButton,
0413: finishButton, cancelButton });
0414: super .setClosingOptions(new Object[] { finishOption,
0415: cancelButton });
0416:
0417: // attach the change listener to iterator
0418: weakChangeListener = WeakListeners.change(baseListener, data
0419: .getIterator(this ));
0420: data.getIterator(this ).addChangeListener(weakChangeListener);
0421:
0422: callInitialize();
0423: }
0424:
0425: /** Create wizard for a sequence of panels, with settings
0426: * defaulted to <CODE>this</CODE>.
0427: *
0428: * @param panels iterator over all {@link WizardDescriptor.Panel}s that can appear in the wizard
0429: */
0430: public WizardDescriptor(Iterator<WizardDescriptor> panels) {
0431: this (SettingsAndIterator.create(panels));
0432: }
0433:
0434: /** Initializes settings.
0435: */
0436: @Override
0437: protected void initialize() {
0438: super .initialize();
0439:
0440: updateState();
0441:
0442: // #81938: special handling WizardDescriptor to avoid close wizard during instantiate
0443: SwingUtilities.invokeLater(new Runnable() {
0444: public void run() {
0445: final Window w = SwingUtilities
0446: .getWindowAncestor((Component) getMessage());
0447: if (w != null) {
0448: w.addWindowListener(new WindowListener() {
0449: public void windowActivated(WindowEvent e) {
0450: }
0451:
0452: public void windowClosed(WindowEvent e) {
0453: }
0454:
0455: public void windowClosing(WindowEvent e) {
0456: if (!changeStateInProgress) {
0457: if (WizardDescriptor.this .getValue() == null) {
0458: WizardDescriptor.this
0459: .setValueWithoutPCH(NotifyDescriptor.CLOSED_OPTION);
0460: }
0461: w.setVisible(false);
0462: w.dispose();
0463: }
0464: }
0465:
0466: public void windowDeactivated(WindowEvent e) {
0467: }
0468:
0469: public void windowDeiconified(WindowEvent e) {
0470: }
0471:
0472: public void windowIconified(WindowEvent e) {
0473: }
0474:
0475: public void windowOpened(WindowEvent e) {
0476: }
0477: });
0478: }
0479: }
0480: });
0481: }
0482:
0483: /** Set a different list of panels.
0484: * Correctly updates the buttons.
0485: * @param panels the new list of {@link WizardDescriptor.Panel}s
0486: * @deprecated use setPanelsAndSettings if needed.
0487: */
0488: @Deprecated
0489: @SuppressWarnings("unchecked")
0490: public final synchronized void setPanels(Iterator panels) {
0491: if (data.getIterator(this ) != null) {
0492: data.getIterator(this ).removeChangeListener(
0493: weakChangeListener);
0494: }
0495:
0496: data = data.clone(panels);
0497: weakChangeListener = WeakListeners.change(baseListener, data
0498: .getIterator(this ));
0499: data.getIterator(this ).addChangeListener(weakChangeListener);
0500: init = false;
0501:
0502: updateState();
0503: }
0504:
0505: /** Set a different list of panels.
0506: * Correctly updates the buttons.
0507: * @param panels the new list of {@link WizardDescriptor.Panel}s
0508: * @param settings the new settings that will be passed to the panels
0509: * @since 7.2
0510: */
0511: public final synchronized <Data> void setPanelsAndSettings(
0512: Iterator<Data> panels, Data settings) {
0513: if (data.getIterator(this ) != null) {
0514: data.getIterator(this ).removeChangeListener(
0515: weakChangeListener);
0516: }
0517:
0518: data = new SettingsAndIterator<Data>(panels, settings);
0519: weakChangeListener = WeakListeners.change(baseListener, data
0520: .getIterator(this ));
0521: data.getIterator(this ).addChangeListener(weakChangeListener);
0522: init = false;
0523:
0524: updateState();
0525: }
0526:
0527: /** Set options permitted by the wizard considered as a <code>DialogDescriptor</code>.
0528: * Substitutes tokens such as {@link #NEXT_OPTION} with the actual button.
0529: *
0530: * @param options the options to set
0531: */
0532: @Override
0533: public void setOptions(Object[] options) {
0534: super .setOptions(convertOptions(options));
0535: }
0536:
0537: /**
0538: * @param options the options to set
0539: */
0540: @Override
0541: public void setAdditionalOptions(Object[] options) {
0542: super .setAdditionalOptions(convertOptions(options));
0543: }
0544:
0545: /**
0546: * @param options the options to set
0547: */
0548: @Override
0549: public void setClosingOptions(Object[] options) {
0550: super .setClosingOptions(convertOptions(options));
0551: }
0552:
0553: /** Converts some options.
0554: */
0555: private Object[] convertOptions(Object[] options) {
0556: Object[] clonedOptions = options.clone();
0557:
0558: for (int i = clonedOptions.length - 1; i >= 0; i--) {
0559: if (clonedOptions[i] == NEXT_OPTION) {
0560: clonedOptions[i] = nextButton;
0561: }
0562:
0563: if (clonedOptions[i] == PREVIOUS_OPTION) {
0564: clonedOptions[i] = previousButton;
0565: }
0566:
0567: if (clonedOptions[i] == FINISH_OPTION) {
0568: clonedOptions[i] = finishButton;
0569: }
0570:
0571: if (clonedOptions[i] == CANCEL_OPTION) {
0572: clonedOptions[i] = cancelButton;
0573: }
0574: }
0575:
0576: return clonedOptions;
0577: }
0578:
0579: /** Overriden to ensure that returned value is one of
0580: * the XXX_OPTION constants.
0581: */
0582: @Override
0583: public Object getValue() {
0584: return backConvertOption(super .getValue());
0585: }
0586:
0587: /** Converts the option back to one of the constants.
0588: * It is called from getValue().
0589: */
0590: private Object backConvertOption(Object op) {
0591: if (op == nextButton) {
0592: return NEXT_OPTION;
0593: }
0594:
0595: if (op == previousButton) {
0596: return PREVIOUS_OPTION;
0597: }
0598:
0599: if (op == finishButton) {
0600: return FINISH_OPTION;
0601: }
0602:
0603: if (op == cancelButton) {
0604: return CANCEL_OPTION;
0605: }
0606:
0607: // if we don't know just return the original value
0608: return op;
0609: }
0610:
0611: /** Sets the message format to create title of the wizard.
0612: * The format can take two parameters. The name of the
0613: * current component and the name returned by the iterator that
0614: * defines the order of panels. The default value is something
0615: * like
0616: * <PRE>
0617: * {0} wizard {1}
0618: * </PRE>
0619: * That can be expanded to something like this
0620: * <PRE>
0621: * EJB wizard (1 of 8)
0622: * </PRE>
0623: * This method allows anybody to provide own title format.
0624: *
0625: * @param format message format to the title
0626: */
0627: public void setTitleFormat(MessageFormat format) {
0628: titleFormat = format;
0629:
0630: if (init) {
0631: updateState();
0632: }
0633: }
0634:
0635: /** Getter for current format to be used to format title.
0636: * @return the format
0637: * @see #setTitleFormat
0638: */
0639: public synchronized MessageFormat getTitleFormat() {
0640: if (titleFormat == null) {
0641: // ok, initialize the default one
0642: titleFormat = new MessageFormat(NbBundle.getMessage(
0643: WizardDescriptor.class, "CTL_WizardName"));
0644: }
0645:
0646: return titleFormat;
0647: }
0648:
0649: /** Allows Panels that use WizardDescriptor as settings object to
0650: * store additional settings into it.
0651: *
0652: * @param name name of the property
0653: * @param value value of property
0654: */
0655: public void putProperty(final String name, final Object value) {
0656: Object oldValue;
0657:
0658: synchronized (this ) {
0659: if (properties == null) {
0660: properties = new HashMap<String, Object>(7);
0661: }
0662:
0663: oldValue = properties.get(name);
0664: properties.put(name, value);
0665: }
0666:
0667: // bugfix #27738, firing changes in a value of the property
0668: firePropertyChange(name, oldValue, value);
0669:
0670: if (propListener != null) {
0671: Mutex.EVENT.readAccess(new Runnable() {
0672: public void run() {
0673: propListener
0674: .propertyChange(new PropertyChangeEvent(
0675: this , name, null, null));
0676: }
0677: });
0678: }
0679:
0680: if (PROP_ERROR_MESSAGE.equals(name)) {
0681: // #76318: New Entity wizard shows unreadable red error
0682: if (init && OK_OPTION.equals(getValue()))
0683: return; // call getValue() only on initialized WD
0684: if (wizardPanel != null) {
0685: SwingUtilities.invokeLater(new Runnable() {
0686: public void run() {
0687: wizardPanel
0688: .setErrorMessage(
0689: (String) ((value == null) ? " "
0690: : value),
0691: (nextButton.isEnabled() || finishButton
0692: .isEnabled()) ? Boolean.TRUE
0693: : Boolean.FALSE); //NOI18N
0694: }
0695: });
0696: }
0697: }
0698: }
0699:
0700: /** Getter for stored property.
0701: * @param name name of the property
0702: * @return the value
0703: */
0704: public synchronized Object getProperty(String name) {
0705: return (properties == null) ? null : properties.get(name);
0706: }
0707:
0708: /** Read only map with stored properties.
0709: * @return read only map of properties stored using {@link #putProperty} method
0710: * @since 7.2
0711: */
0712: public synchronized Map<String, Object> getProperties() {
0713: return properties == null ? Collections
0714: .<String, Object> emptyMap()
0715: : new HashMap<String, Object>(properties);
0716: }
0717:
0718: @Override
0719: public void setHelpCtx(final HelpCtx helpCtx) {
0720: if ((wizardPanel != null) && (helpCtx != null)) {
0721: HelpCtx.setHelpIDString(wizardPanel, helpCtx.getHelpID());
0722: }
0723:
0724: // we call the inherited method after setting the ID
0725: // on the panel becuase super.setHelpCtx fires the change
0726: super .setHelpCtx(helpCtx);
0727: }
0728:
0729: /** Returns set of newly instantiated objects if the wizard has been correctly finished.
0730: * Returns the empty set as default. If the wizard uses the InstantiatingIterator
0731: * then WizardDescriptor returns a set of Object as same as InstantiatingIterator.instantiate().
0732: *
0733: * @exception IllegalStateException if this method is called on the unfinished wizard
0734: * @return a set of Objects created
0735: * @since 4.41
0736: */
0737: public Set /*<Object>*/getInstantiatedObjects() {
0738: //
0739: if (!(FINISH_OPTION.equals(getValue()))) {
0740: throw new IllegalStateException();
0741: }
0742:
0743: return newObjects;
0744: }
0745:
0746: /** Updates buttons to reflect the current state of the panels.
0747: * Can be overridden by subclasses
0748: * to change the options to special values. In such a case use:
0749: * <p><code><PRE>
0750: * super.updateState ();
0751: * setOptions (...);
0752: * </PRE></code>
0753: */
0754: protected synchronized void updateState() {
0755: updateStateOpen(data);
0756: }
0757:
0758: private <A> void updateStateOpen(SettingsAndIterator<A> data) {
0759: Panel<A> p = data.getIterator(this ).current();
0760:
0761: // listeners on the panel
0762: if (data.current != p) {
0763: if (data.current != null) {
0764: // remove
0765: data.current.removeChangeListener(weakChangeListener);
0766: data.current.storeSettings(data.getSettings(this ));
0767: }
0768:
0769: // Hack - obtain current panel again
0770: // It's here to allow dynamic change of panels in wizard
0771: // (which can be done in storeSettings method)
0772: p = data.getIterator(this ).current();
0773:
0774: // add to new, detach old change listener and attach new one
0775: data.getIterator(this ).removeChangeListener(
0776: weakChangeListener);
0777: weakChangeListener = WeakListeners.change(baseListener, p);
0778: data.getIterator(this )
0779: .addChangeListener(weakChangeListener);
0780: p.addChangeListener(weakChangeListener);
0781:
0782: data.current = p;
0783: p.readSettings(data.getSettings(this ));
0784: }
0785:
0786: boolean next = data.getIterator(this ).hasNext();
0787: boolean prev = data.getIterator(this ).hasPrevious();
0788: boolean valid = p.isValid();
0789:
0790: // AWT sensitive code
0791: if (SwingUtilities.isEventDispatchThread()) {
0792: updateStateInAWT();
0793: } else {
0794: SwingUtilities.invokeLater(new Runnable() {
0795: public void run() {
0796: updateStateInAWT();
0797: }
0798: });
0799: }
0800: // end of AWT sensitive code
0801:
0802: // nextButton.setVisible (next);
0803: // finishButton.setVisible (!next || (current instanceof FinishPanel));
0804: // XXX Why setValue on Next ?
0805: // if (next) {
0806: // setValue (nextButton);
0807: // } else {
0808: //// setValue (finishButton);
0809: // }
0810: setHelpCtx(p.getHelp());
0811:
0812: java.awt.Component c = p.getComponent();
0813:
0814: if ((c == null) || c instanceof java.awt.Window) {
0815: throw new IllegalStateException("Wizard panel " + p
0816: + " gave a strange component " + c); // NOI18N
0817: }
0818:
0819: if (!init) {
0820: if (c instanceof JComponent) {
0821: autoWizardStyle = getBooleanProperty((JComponent) c,
0822: PROP_AUTO_WIZARD_STYLE);
0823:
0824: if (autoWizardStyle) {
0825: wizardPanel = new WizardPanel(getBooleanProperty(
0826: (JComponent) c, PROP_CONTENT_DISPLAYED),
0827: getBooleanProperty((JComponent) c,
0828: PROP_HELP_DISPLAYED),
0829: getBooleanProperty((JComponent) c,
0830: PROP_CONTENT_NUMBERED),
0831: getLeftDimension((JComponent) c));
0832: initBundleProperties();
0833: }
0834: }
0835:
0836: if (propListener == null) {
0837: propListener = new PropL();
0838: }
0839:
0840: init = true;
0841: }
0842:
0843: //update wizardPanel
0844: if (wizardPanel != null) {
0845: Component oldComp = wizardPanel.getRightComponent();
0846:
0847: if (oldComp != null) {
0848: oldComp
0849: .removePropertyChangeListener(weakPropertyChangeListener);
0850: }
0851:
0852: if (c instanceof JComponent) {
0853: setPanelProperties((JComponent) c);
0854: wizardPanel.setContent(contentData);
0855: wizardPanel.setSelectedIndex(contentSelectedIndex);
0856: wizardPanel.setContentBackColor(contentBackColor);
0857: wizardPanel
0858: .setContentForegroundColor(contentForegroundColor);
0859: wizardPanel.setImage(image);
0860: wizardPanel.setImageAlignment(imageAlignment);
0861: wizardPanel.setHelpURL(helpURL);
0862: updateButtonAccessibleDescription();
0863: weakPropertyChangeListener = WeakListeners
0864: .propertyChange(propListener, c);
0865: c.addPropertyChangeListener(weakPropertyChangeListener);
0866: }
0867:
0868: if (wizardPanel.getRightComponent() != c) {
0869: wizardPanel.setRightComponent(c);
0870:
0871: if (wizardPanel != getMessage()) {
0872: setMessage(wizardPanel);
0873: } else {
0874: // force revalidate and repaint because the contents of
0875: // wizardPanel has changed. See NbPresenter code
0876: firePropertyChange(DialogDescriptor.PROP_MESSAGE,
0877: null, wizardPanel);
0878: }
0879: }
0880: } else if (c != getMessage()) {
0881: setMessage(c);
0882: }
0883:
0884: String panelName = c.getName();
0885:
0886: if (panelName == null) {
0887: panelName = ""; // NOI18N
0888: }
0889:
0890: Object[] args = { panelName, data.getIterator(this ).name() };
0891: MessageFormat mf = getTitleFormat();
0892:
0893: if (autoWizardStyle) {
0894: wizardPanel.setPanelName(mf.format(args));
0895: } else {
0896: setTitle(mf.format(args));
0897: }
0898:
0899: Component fo = KeyboardFocusManager
0900: .getCurrentKeyboardFocusManager().getFocusOwner();
0901:
0902: if ((fo != null) && (!fo.isEnabled()) && (wizardPanel != null)) {
0903: wizardPanel.requestFocus();
0904: }
0905: }
0906:
0907: // for xtesting usage only
0908: boolean isForwardEnabled() {
0909: return data.getIterator(this ).current().isValid()
0910: && !validationRuns;
0911: }
0912:
0913: private void updateStateInAWT() {
0914: Panel<?> p = data.getIterator(this ).current();
0915: boolean next = data.getIterator(this ).hasNext();
0916: boolean prev = data.getIterator(this ).hasPrevious();
0917: boolean valid = p.isValid() && !validationRuns;
0918:
0919: nextButton.setEnabled(next && valid);
0920: previousButton.setEnabled(prev);
0921: cancelButton.setEnabled(true);
0922:
0923: if (data.current instanceof FinishablePanel) {
0924: // check if isFinishPanel
0925: if (((FinishablePanel) data.current).isFinishPanel()) {
0926: finishButton.setEnabled(valid);
0927: } else {
0928: // XXX What if the last panel is not FinishPanel ??? enable ?
0929: finishButton.setEnabled(valid && !next);
0930: }
0931: } else {
0932: // original way
0933: finishButton
0934: .setEnabled(valid
0935: && (!next || (data.current instanceof FinishPanel)));
0936: }
0937: }
0938:
0939: /** Shows blocking wait cursor during updateState run */
0940: private void updateStateWithFeedback() {
0941: try {
0942: showWaitCursor();
0943: updateState();
0944: } finally {
0945: showNormalCursor();
0946: }
0947: }
0948:
0949: /** Shows next step in UI of wizards, displays wait cursot during the change.
0950: */
0951: private void goToNextStep(Dimension previousSize) {
0952: try {
0953: showWaitCursor();
0954:
0955: boolean alreadyUpdated = false;
0956: Font controlFont = (Font) UIManager.getDefaults().get(
0957: "controlFont"); //NOI18N
0958: Integer defaultSize = (Integer) UIManager
0959: .get("nbDefaultFontSize");
0960:
0961: if (defaultSize == null) {
0962: //Plastic look and feel...
0963: defaultSize = new Integer(11);
0964: }
0965:
0966: // enable auto-resizing policy only for fonts bigger thne default
0967: if ((controlFont != null)
0968: && (controlFont.getSize() > defaultSize.intValue())) { //NOI18N
0969:
0970: Window parentWindow = SwingUtilities
0971: .getWindowAncestor((Component) getMessage());
0972:
0973: if (parentWindow != null) {
0974: updateState();
0975: alreadyUpdated = true;
0976: resizeWizard(parentWindow, previousSize);
0977: }
0978: }
0979:
0980: if (!alreadyUpdated) {
0981: updateState();
0982: }
0983:
0984: if (wizardPanel != null) {
0985: wizardPanel.requestFocus();
0986: }
0987: } finally {
0988: showNormalCursor();
0989: }
0990: }
0991:
0992: /** Tries to resize wizard wisely if needed. Keeps "display inertia" so that
0993: * wizard is only enlarged, not shrinked, and location is changed only when
0994: * wizard window exceeds screen bounds after resize.
0995: */
0996: private void resizeWizard(Window parentWindow, Dimension prevSize) {
0997: Dimension curSize = data.getIterator(this ).current()
0998: .getComponent().getPreferredSize();
0999:
1000: // only enlarge if needed, don't shrink
1001: if ((curSize.width > prevSize.width)
1002: || (curSize.height > prevSize.height)) {
1003: Rectangle origBounds = parentWindow.getBounds();
1004: int newWidth = Math.max(origBounds.width
1005: + (curSize.width - prevSize.width),
1006: origBounds.width);
1007: int newHeight = Math.max(origBounds.height
1008: + (curSize.height - prevSize.height),
1009: origBounds.height);
1010: Rectangle screenBounds = Utilities.getUsableScreenBounds();
1011: Rectangle newBounds;
1012:
1013: // don't allow to exceed screen size, center if needed
1014: if (((origBounds.x + newWidth) > screenBounds.width)
1015: || ((origBounds.y + newHeight) > screenBounds.height)) {
1016: newWidth = Math.min(screenBounds.width, newWidth);
1017: newHeight = Math.min(screenBounds.height, newHeight);
1018: newBounds = Utilities.findCenterBounds(new Dimension(
1019: newWidth, newHeight));
1020: } else {
1021: newBounds = new Rectangle(origBounds.x, origBounds.y,
1022: newWidth, newHeight);
1023: }
1024:
1025: parentWindow.setBounds(newBounds);
1026: parentWindow.invalidate();
1027: parentWindow.validate();
1028: parentWindow.repaint();
1029: }
1030: }
1031:
1032: private void showWaitCursor() {
1033: if ((wizardPanel == null)
1034: || (wizardPanel.getRootPane() == null)) {
1035: // if none root pane --> don't set wait cursor
1036: return;
1037: }
1038:
1039: // bugfix #92539: JR: I don't see the reason this code, I have tried comment out
1040: // Window parentWindow = SwingUtilities.getWindowAncestor((Component) getMessage());
1041: // if (parentWindow != null) {
1042: // parentWindow.setEnabled (false);
1043: // }
1044: //
1045: if (wizardPanel != null) {
1046: // save escapeActionListener for normal state
1047: KeyStroke ks = KeyStroke
1048: .getKeyStroke(KeyEvent.VK_ESCAPE, 0);
1049: escapeActionListener = wizardPanel.getRootPane()
1050: .getActionForKeyStroke(ks);
1051: wizardPanel.getRootPane().unregisterKeyboardAction(ks);
1052: }
1053:
1054: waitingComponent = wizardPanel.getRootPane().getContentPane();
1055: waitingComponent.setCursor(Cursor
1056: .getPredefinedCursor(Cursor.WAIT_CURSOR));
1057: changeStateInProgress = true;
1058: }
1059:
1060: private void showNormalCursor() {
1061: if (waitingComponent == null) {
1062: // none waitingComponent --> don't change cursor to normal
1063: return;
1064: }
1065:
1066: Window parentWindow = SwingUtilities
1067: .getWindowAncestor((Component) getMessage());
1068: if (parentWindow != null) {
1069: parentWindow.setEnabled(true);
1070: }
1071:
1072: if (wizardPanel != null) {
1073: // set back escapeActionListener as same as NbPresenter does
1074: if (escapeActionListener != null) {
1075: wizardPanel.getRootPane().registerKeyboardAction(
1076: escapeActionListener, "Escape",
1077: KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
1078: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
1079: }
1080: }
1081:
1082: waitingComponent.setCursor(null);
1083: waitingComponent = null;
1084: changeStateInProgress = false;
1085: }
1086:
1087: /* commented out - issue #32927. Replaced by javadoc info in WizardDescriptor.Panel
1088: private static final Set warnedPanelIsComponent = new WeakSet(); // Set<Class>
1089: private static synchronized void warnPanelIsComponent(Class c) {
1090: if (warnedPanelIsComponent.add(c)) {
1091: StringBuffer buffer = new StringBuffer(150);
1092: buffer.append("WARNING - the WizardDescriptor.Panel implementation "); // NOI18N
1093: buffer.append(c.getName());
1094: buffer.append(" provides itself as the result of getComponent().\n"); // NOI18N
1095: buffer.append("This hurts performance and can cause a clash when Component.isValid() is overridden.\n"); // NOI18N
1096: buffer.append("Please use a separate component class, see details at http://performance.netbeans.org/howto/dialogs/wizard-panels.html."); // NOI18N
1097: err.log(ErrorManager.WARNING, buffer.toString());
1098: }
1099: }
1100: */
1101:
1102: /** Tryes to get property from getProperty() if doesn't succeed then tryes at
1103: * supplied <CODE>JComponent</CODE>s client property.
1104: * @param c origin of property
1105: * @param s name of property
1106: * @return boolean property
1107: */
1108: private boolean getBooleanProperty(JComponent c, String s) {
1109: Object property = getProperty(s);
1110:
1111: if (property instanceof Boolean) {
1112: return ((Boolean) property).booleanValue();
1113: }
1114:
1115: property = c.getClientProperty(s);
1116:
1117: if (property instanceof Boolean) {
1118: return ((Boolean) property).booleanValue();
1119: }
1120:
1121: return false;
1122: }
1123:
1124: /** Tryes to get dimension of wizard panel's left pane from getProperty()
1125: * if doesn't succeed then tryes at
1126: * supplied <CODE>JComponent</CODE>s client property.
1127: * @return <CODE>Dimension</CODE> dimension of wizard panel's left pane
1128: */
1129: private Dimension getLeftDimension(JComponent c) {
1130: Dimension leftDimension;
1131: Object property = c.getClientProperty(PROP_LEFT_DIMENSION);
1132:
1133: if (property instanceof Dimension) {
1134: leftDimension = (Dimension) property;
1135: } else {
1136: leftDimension = new Dimension(198, 233);
1137: }
1138:
1139: return leftDimension;
1140: }
1141:
1142: /** Tryes to get properties from getProperty() if doesn't succeed then tryes at
1143: * supplied <CODE>JComponent</CODE>s client properties and store them
1144: * to appropriate fields.
1145: * @param c origin of property
1146: * @param s name of property
1147: * @return boolean property
1148: */
1149: private void setPanelProperties(JComponent c) {
1150: // TODO: Method should be devided into individual setter/getter methods !?
1151: Object property = getProperty(PROP_CONTENT_SELECTED_INDEX);
1152:
1153: if (property instanceof Integer) {
1154: contentSelectedIndex = ((Integer) property).intValue();
1155: } else {
1156: property = c.getClientProperty(PROP_CONTENT_SELECTED_INDEX);
1157:
1158: if (property instanceof Integer) {
1159: contentSelectedIndex = ((Integer) property).intValue();
1160: }
1161: }
1162:
1163: property = getProperty(PROP_CONTENT_DATA);
1164:
1165: if (property instanceof String[]) {
1166: contentData = (String[]) property;
1167: } else {
1168: property = c.getClientProperty(PROP_CONTENT_DATA);
1169:
1170: if (property instanceof String[]) {
1171: contentData = (String[]) property;
1172: }
1173: }
1174:
1175: property = getProperty(PROP_IMAGE);
1176:
1177: if (property instanceof Image) {
1178: image = (Image) property;
1179: } else if ((properties == null)
1180: || (!properties.containsKey(PROP_IMAGE))) {
1181: property = c.getClientProperty(PROP_IMAGE);
1182:
1183: if (property instanceof Image) {
1184: image = (Image) property;
1185: }
1186: }
1187:
1188: property = getProperty(PROP_IMAGE_ALIGNMENT);
1189:
1190: if (property instanceof String) {
1191: imageAlignment = (String) property;
1192: } else {
1193: property = c.getClientProperty(PROP_IMAGE_ALIGNMENT);
1194:
1195: if (property instanceof String) {
1196: imageAlignment = (String) property;
1197: }
1198: }
1199:
1200: property = getProperty(PROP_CONTENT_BACK_COLOR);
1201:
1202: if (property instanceof Color) {
1203: contentBackColor = (Color) property;
1204: } else {
1205: property = c.getClientProperty(PROP_CONTENT_BACK_COLOR);
1206:
1207: if (property instanceof Color) {
1208: contentBackColor = (Color) property;
1209: }
1210: }
1211:
1212: property = getProperty(PROP_CONTENT_FOREGROUND_COLOR);
1213:
1214: if (property instanceof Color) {
1215: contentForegroundColor = (Color) property;
1216: } else {
1217: property = c
1218: .getClientProperty(PROP_CONTENT_FOREGROUND_COLOR);
1219:
1220: if (property instanceof Color) {
1221: contentForegroundColor = (Color) property;
1222: }
1223: }
1224:
1225: property = c.getClientProperty(PROP_HELP_URL);
1226:
1227: if (property instanceof URL) {
1228: helpURL = (URL) property;
1229: } else if (property == null) {
1230: helpURL = null;
1231: }
1232: }
1233:
1234: private void initBundleProperties() {
1235: contentBackColor = new Color(
1236: getIntFromBundle("INT_WizardBackRed"), // NOI18N
1237: getIntFromBundle("INT_WizardBackGreen"), // NOI18N
1238: getIntFromBundle("INT_WizardBackBlue") // NOI18N
1239: ); // NOI18N
1240:
1241: contentForegroundColor = new Color(
1242: getIntFromBundle("INT_WizardForegroundRed"), // NOI18N
1243: getIntFromBundle("INT_WizardForegroundGreen"), // NOI18N
1244: getIntFromBundle("INT_WizardForegroundBlue") // NOI18N
1245: ); // NOI18N
1246: imageAlignment = bundle
1247: .getString("STRING_WizardImageAlignment"); //NOI18N
1248: }
1249:
1250: /** Overrides superclass method. Adds reseting of wizard
1251: * for <code>CLOSED_OPTION</code>. */
1252: @Override
1253: public void setValue(Object value) {
1254: setValueOpen(value, data);
1255: }
1256:
1257: private <A> void setValueOpen(Object value,
1258: SettingsAndIterator<A> data) {
1259: Object convertedValue = backConvertOption(value);
1260: // set new value w/o fire PROP_VALUE change
1261: Object oldValue = getValue();
1262: setValueWithoutPCH(convertedValue);
1263:
1264: // #17360: Reset wizard on CLOSED_OPTION too.
1265: if (CLOSED_OPTION.equals(convertedValue)) {
1266: try {
1267: resetWizard();
1268: } catch (RuntimeException x) {
1269: // notify to log
1270: err.log(Level.INFO, null, x);
1271: }
1272: } else if (FINISH_OPTION.equals(convertedValue)
1273: || NEXT_OPTION.equals(convertedValue)) {
1274: //Bugfix #25820: make sure that storeSettings
1275: //is called before propertyChange.
1276: if (data.current != null) {
1277: data.current.storeSettings(data.getSettings(this ));
1278: }
1279: }
1280:
1281: // notify listeners about PROP_VALUE change
1282: firePropertyChange(PROP_VALUE, oldValue, convertedValue);
1283: }
1284:
1285: /** Resets wizard when after closed/cancelled/finished the wizard dialog. */
1286: private void resetWizard() {
1287: resetWizardOpen(data);
1288: }
1289:
1290: private <A> void resetWizardOpen(SettingsAndIterator<A> data) {
1291: if (data.current != null) {
1292: data.current.storeSettings(data.getSettings(this ));
1293: data.current.removeChangeListener(weakChangeListener);
1294: data.current = null;
1295:
1296: if (wizardPanel != null) {
1297: wizardPanel.resetPreferredSize();
1298: }
1299: }
1300:
1301: callUninitialize();
1302:
1303: // detach the change listener at the end of wizard
1304: data.getIterator(this ).removeChangeListener(weakChangeListener);
1305: }
1306:
1307: private int getIntFromBundle(String key) {
1308: return Integer.parseInt(bundle.getString(key));
1309: }
1310:
1311: private static Image getDefaultImage() {
1312: return Utilities.loadImage(
1313: "org/netbeans/modules/dialogs/defaultWizard.gif", true);
1314: }
1315:
1316: private void updateButtonAccessibleDescription() {
1317: String stepName = ((contentData != null)
1318: && (contentSelectedIndex > 0) && ((contentSelectedIndex - 1) < contentData.length)) ? contentData[contentSelectedIndex - 1]
1319: : ""; // NOI18N
1320: previousButton.getAccessibleContext().setAccessibleDescription(
1321: NbBundle.getMessage(WizardDescriptor.class,
1322: "ACSD_PREVIOUS", new Integer(
1323: contentSelectedIndex), stepName));
1324:
1325: stepName = ((contentData != null)
1326: && (contentSelectedIndex < (contentData.length - 1)) && ((contentSelectedIndex + 1) >= 0)) ? contentData[contentSelectedIndex + 1]
1327: : ""; // NOI18N
1328: nextButton.getAccessibleContext().setAccessibleDescription(
1329: NbBundle.getMessage(WizardDescriptor.class,
1330: "ACSD_NEXT", new Integer(
1331: contentSelectedIndex + 2), stepName));
1332: }
1333:
1334: private void lazyValidate(final WizardDescriptor.Panel panel,
1335: final Runnable onValidPerformer) {
1336:
1337: Runnable validationPeformer = new Runnable() {
1338: public void run() {
1339:
1340: err.log(Level.FINE, "validationPeformer entry."); // NOI18N
1341: ValidatingPanel v = (ValidatingPanel) panel;
1342:
1343: try {
1344: // try validation current panel
1345: v.validate();
1346: validationRuns = false;
1347:
1348: // validation succesfull
1349: if (SwingUtilities.isEventDispatchThread()) {
1350: err
1351: .log(Level.FINE,
1352: "Runs onValidPerformer directly in EDT."); // NOI18N
1353: onValidPerformer.run();
1354: } else {
1355: err.log(Level.FINE,
1356: "invokeLater onValidPerformer."); // NOI18N
1357: SwingUtilities.invokeLater(new Runnable() {
1358: public void run() {
1359: err
1360: .log(Level.FINE,
1361: "Runs onValidPerformer from invokeLater."); // NOI18N
1362: onValidPerformer.run();
1363: }
1364: });
1365: }
1366: } catch (WizardValidationException wve) {
1367: validationRuns = false;
1368: updateState();
1369:
1370: // cannot continue, notify user
1371: if (wizardPanel != null) {
1372: wizardPanel.setErrorMessage(wve
1373: .getLocalizedMessage(), Boolean.FALSE);
1374: }
1375:
1376: // focus source of this problem
1377: final JComponent comp = wve.getSource();
1378: if (comp != null && comp.isFocusable()) {
1379: comp.requestFocus();
1380: }
1381: }
1382:
1383: }
1384: };
1385:
1386: if (panel instanceof AsynchronousValidatingPanel) {
1387: AsynchronousValidatingPanel p = (AsynchronousValidatingPanel) panel;
1388: validationRuns = true; // disable Next> Finish buttons
1389: p.prepareValidation();
1390: err
1391: .log(Level.FINE,
1392: "Do ASYNCHRONOUS_JOBS_RP.post(validationPeformer)."); // NOI18N
1393: updateStateWithFeedback();
1394: backgroundValidationTask = ASYNCHRONOUS_JOBS_RP
1395: .post(validationPeformer);
1396: } else if (panel instanceof ValidatingPanel) {
1397: validationRuns = true;
1398: err.log(Level.FINE, "Runs validationPeformer."); // NOI18N
1399: validationPeformer.run();
1400: } else {
1401: err.log(Level.FINE, "Runs onValidPerformer."); // NOI18N
1402: onValidPerformer.run();
1403: }
1404:
1405: }
1406:
1407: // helper methods which call to InstantiatingIterator
1408: private void callInitialize() {
1409: assert data.getIterator(this ) != null;
1410:
1411: if (data.getIterator(this ) instanceof InstantiatingIterator) {
1412: ((InstantiatingIterator) data.getIterator(this ))
1413: .initialize(this );
1414: }
1415:
1416: newObjects = Collections.EMPTY_SET;
1417: }
1418:
1419: private void callUninitialize() {
1420: assert data.getIterator(this ) != null;
1421:
1422: if (data.getIterator(this ) instanceof InstantiatingIterator) {
1423: ((InstantiatingIterator) data.getIterator(this ))
1424: .uninitialize(this );
1425: }
1426: }
1427:
1428: private void callInstantiate() throws IOException {
1429: callInstantiateOpen(data);
1430: }
1431:
1432: private <A> void callInstantiateOpen(SettingsAndIterator<A> data)
1433: throws IOException {
1434: Iterator<A> panels = data.getIterator(this );
1435:
1436: assert panels != null;
1437:
1438: err
1439: .log(
1440: Level.FINE,
1441: "Is AsynchronousInstantiatingIterator? "
1442: + (panels instanceof AsynchronousInstantiatingIterator));
1443: err.log(Level.FINE, "Is ProgressInstantiatingIterator? "
1444: + (panels instanceof ProgressInstantiatingIterator));
1445: if (panels instanceof ProgressInstantiatingIterator) {
1446: handle = ProgressHandleFactory
1447: .createHandle(PROGRESS_BAR_DISPLAY_NAME);
1448:
1449: JComponent progressComp = ProgressHandleFactory
1450: .createProgressComponent(handle);
1451: if (wizardPanel != null) {
1452: wizardPanel.setProgressComponent(progressComp,
1453: ProgressHandleFactory
1454: .createDetailLabelComponent(handle));
1455: }
1456:
1457: err.log(Level.FINE,
1458: "Show progressPanel controlled by iterator later.");
1459: } else if (panels instanceof AsynchronousInstantiatingIterator) {
1460: handle = ProgressHandleFactory
1461: .createHandle(PROGRESS_BAR_DISPLAY_NAME);
1462:
1463: JComponent progressComp = ProgressHandleFactory
1464: .createProgressComponent(handle);
1465: if (wizardPanel != null) {
1466: wizardPanel.setProgressComponent(progressComp,
1467: ProgressHandleFactory
1468: .createMainLabelComponent(handle));
1469: }
1470:
1471: handle.start();
1472: err.log(Level.FINE, "Show progressPanel later.");
1473: }
1474:
1475: // bugfix #44444, force store settings before do instantiate new objects
1476: panels.current().storeSettings(data.getSettings(this ));
1477:
1478: if (panels instanceof InstantiatingIterator) {
1479: showWaitCursor();
1480:
1481: try {
1482: assert !(panels instanceof AsynchronousInstantiatingIterator)
1483: || !SwingUtilities.isEventDispatchThread() : "Cannot invoked within EDT if AsynchronousInstantiatingIterator!";
1484:
1485: if (panels instanceof ProgressInstantiatingIterator) {
1486: assert handle != null : "ProgressHandle must be not null.";
1487: err.log(Level.FINE,
1488: "Calls instantiate(ProgressHandle) on iterator: "
1489: + panels.getClass().getName());
1490: newObjects = ((ProgressInstantiatingIterator) panels)
1491: .instantiate(handle);
1492: } else {
1493: err.log(Level.FINE,
1494: "Calls instantiate() on iterator: "
1495: + panels.getClass().getName());
1496: newObjects = ((InstantiatingIterator) panels)
1497: .instantiate();
1498: }
1499: } finally {
1500:
1501: // set cursor back to normal
1502: showNormalCursor();
1503:
1504: }
1505: }
1506: }
1507:
1508: private static Font doDeriveFont(Font original, int style) {
1509: if (Utilities.isMac()) {
1510: // don't use deriveFont() - see #49973 for details
1511: return new Font(original.getName(), style, original
1512: .getSize());
1513: }
1514:
1515: return original.deriveFont(style);
1516: }
1517:
1518: // support methods for xtesting
1519: final void doNextClick() {
1520: if (nextButton.isEnabled()) {
1521: nextButton.doClick();
1522: }
1523: }
1524:
1525: final void doPreviousClick() {
1526: if (previousButton.isEnabled()) {
1527: previousButton.doClick();
1528: }
1529: }
1530:
1531: final void doFinishClick() {
1532: if (finishButton.isEnabled()) {
1533: finishButton.doClick();
1534: }
1535: }
1536:
1537: final void doCancelClick() {
1538: if (cancelButton.isEnabled()) {
1539: cancelButton.doClick();
1540: }
1541: }
1542:
1543: // helper method, might be removed from code
1544: // returns false if Next button is disabled
1545: final boolean isNextEnabled() {
1546: return nextButton.isEnabled();
1547: }
1548:
1549: // helper method, might be removed from code
1550: // returns false if Finish button is disabled
1551: final boolean isFinishEnabled() {
1552: return finishButton.isEnabled();
1553: }
1554:
1555: /** Iterator on the sequence of panels.
1556: * @see WizardDescriptor.Panel
1557: */
1558: public interface Iterator<Data> {
1559: /** Get the current panel.
1560: * @return the panel
1561: */
1562: public Panel<Data> current();
1563:
1564: /** Get the name of the current panel.
1565: * @return the name
1566: */
1567: public String name();
1568:
1569: /** Test whether there is a next panel.
1570: * @return <code>true</code> if so
1571: */
1572: public boolean hasNext();
1573:
1574: /** Test whether there is a previous panel.
1575: * @return <code>true</code> if so
1576: */
1577: public boolean hasPrevious();
1578:
1579: /** Move to the next panel.
1580: * I.e. increment its index, need not actually change any GUI itself.
1581: * @exception NoSuchElementException if the panel does not exist
1582: */
1583: public void nextPanel();
1584:
1585: /** Move to the previous panel.
1586: * I.e. decrement its index, need not actually change any GUI itself.
1587: * @exception NoSuchElementException if the panel does not exist
1588: */
1589: public void previousPanel();
1590:
1591: /** Add a listener to changes of the current panel.
1592: * The listener is notified when the possibility to move forward/backward changes.
1593: * @param l the listener to add
1594: */
1595: public void addChangeListener(ChangeListener l);
1596:
1597: /** Remove a listener to changes of the current panel.
1598: * @param l the listener to remove
1599: */
1600: public void removeChangeListener(ChangeListener l);
1601: }
1602:
1603: /** One wizard panel with a component on it.
1604: *
1605: * For good performance, implementation of this interface should be as
1606: * lightweight as possible. Defer creation and initialization of
1607: * UI component of wizard panel into {@link #getComponent} method.
1608: *
1609: * Please see complete guide at http://performance.netbeans.org/howto/dialogs/wizard-panels.html
1610: */
1611: public interface Panel<Data> {
1612: /** Get the component displayed in this panel.
1613: *
1614: * Note; method can be called from any thread, but not concurrently
1615: * with other methods of this interface. Please see complete guide at
1616: * http://performance.netbeans.org/howto/dialogs/wizard-panels.html for
1617: * correct implementation.
1618: *
1619: * @return the UI component of this wizard panel
1620: */
1621: public java.awt.Component getComponent();
1622:
1623: /** Help for this panel.
1624: * When the panel is active, this is used as the help for the wizard dialog.
1625: * @return the help or <code>null</code> if no help is supplied
1626: */
1627: public HelpCtx getHelp();
1628:
1629: /** Provides the wizard panel with the current data--either
1630: * the default data or already-modified settings, if the user used the previous and/or next buttons.
1631: * This method can be called multiple times on one instance of <code>WizardDescriptor.Panel</code>.
1632: * <p>The settings object is originally supplied to {@link WizardDescriptor#WizardDescriptor(WizardDescriptor.Iterator,Object)}.
1633: * In the case of a <code>TemplateWizard.Iterator</code> panel, the object is
1634: * in fact the <code>TemplateWizard</code>.
1635: * @param settings the object representing wizard panel state
1636: * @exception IllegalStateException if the the data provided
1637: * by the wizard are not valid.
1638: */
1639: public void readSettings(Data settings);
1640:
1641: /** Provides the wizard panel with the opportunity to update the
1642: * settings with its current customized state.
1643: * Rather than updating its settings with every change in the GUI, it should collect them,
1644: * and then only save them when requested to by this method.
1645: * Also, the original settings passed to {@link #readSettings} should not be modified (mutated);
1646: * rather, the object passed in here should be mutated according to the collected changes,
1647: * in case it is a copy.
1648: * This method can be called multiple times on one instance of <code>WizardDescriptor.Panel</code>.
1649: * <p>The settings object is originally supplied to {@link WizardDescriptor#WizardDescriptor(WizardDescriptor.Iterator,Object)}.
1650: * In the case of a <code>TemplateWizard.Iterator</code> panel, the object is
1651: * in fact the <code>TemplateWizard</code>.
1652: * @param settings the object representing wizard panel state
1653: */
1654: public void storeSettings(Data settings);
1655:
1656: /** Test whether the panel is finished and it is safe to proceed to the next one.
1657: * If the panel is valid, the "Next" (or "Finish") button will be enabled.
1658: * <p><strong>Tip:</strong> if your panel is actually the component itself
1659: * (so {@link #getComponent} returns <code>this</code>), be sure to specifically
1660: * override this method, as the unrelated implementation in {@link java.awt.Component#isValid}
1661: * if not overridden could cause your wizard to behave erratically.
1662: * @return <code>true</code> if the user has entered satisfactory information
1663: */
1664: public boolean isValid();
1665:
1666: /** Add a listener to changes of the panel's validity.
1667: * @param l the listener to add
1668: * @see #isValid
1669: */
1670: public void addChangeListener(ChangeListener l);
1671:
1672: /** Remove a listener to changes of the panel's validity.
1673: * @param l the listener to remove
1674: */
1675: public void removeChangeListener(ChangeListener l);
1676: }
1677:
1678: /** A special interface for panels in middle of the
1679: * iterators path that would like to have the finish button
1680: * enabled. So both Next and Finish are enabled on panel
1681: * implementing this interface.
1682: * @deprecated 4.28 Use FinishablePanel instead.
1683: */
1684: @Deprecated
1685: public interface FinishPanel<Data> extends Panel<Data> {
1686: }
1687:
1688: /** A special interface for panels that need to do additional
1689: * validation when Next or Finish button is clicked.
1690: * @since 4.28
1691: */
1692: public interface ValidatingPanel<Data> extends Panel<Data> {
1693: /**
1694: * Is called when Next of Finish buttons are clicked and
1695: * allows deeper check to find out that panel is in valid
1696: * state and it is ok to leave it.
1697: *
1698: * @throws WizardValidationException when validation fails
1699: * @since 4.28
1700: */
1701: public void validate() throws WizardValidationException;
1702: }
1703:
1704: /**
1705: * A special interface for panels that need to do additional
1706: * asynchronous validation when Next or Finish button is clicked.
1707: *
1708: * <p>During backround validation is Next or Finish button
1709: * disabled. On validation success wizard automatically
1710: * progress to next panel or finishes.
1711: *
1712: * <p>During backround validation Cancel button is hooked
1713: * to signal the validation thread using interrupt().
1714: *
1715: * @since 6.2 (16 May 2005)
1716: */
1717: public interface AsynchronousValidatingPanel<Data> extends
1718: ValidatingPanel<Data> {
1719:
1720: /**
1721: * Called synchronously from UI thread when Next
1722: * of Finish buttons clicked. It allows to lock user
1723: * input to assure official data for background validation.
1724: */
1725: public void prepareValidation();
1726:
1727: /**
1728: * Is called in separate thread when Next of Finish buttons
1729: * are clicked and allows deeper check to find out that panel
1730: * is in valid state and it is ok to leave it.
1731: *
1732: * @throws WizardValidationException when validation fails
1733: */
1734: public void validate() throws WizardValidationException;
1735: }
1736:
1737: /** A special interface for panel that needs to dynamically enabled
1738: * Finish button.
1739: * @since 4.28
1740: */
1741: public interface FinishablePanel<Data> extends Panel<Data> {
1742: /** Specify if this panel would enable Finish button. Finish button is
1743: * enabled if and only if isValid() returns true and isFinishPanel()
1744: * returns true.
1745: *
1746: * @return Finish button could be enabled
1747: * @since 4.28
1748: */
1749: boolean isFinishPanel();
1750: }
1751:
1752: /**
1753: * Iterator for a wizard that needs to somehow instantiate new objects.
1754: * (This interface can replace
1755: * <a href="@OPENIDE/LOADERS@/org/openide/loaders/TemplateWizard.Iterator.html"><code>TemplateWizard.Iterator</code></a>
1756: * in a template's declaration.)
1757: * @since org.openide/1 4.33
1758: */
1759: public interface InstantiatingIterator<Data> extends Iterator<Data> {
1760: /** Returns set of instantiated objects. If instantiation fails then wizard remains open to enable correct values.
1761: *
1762: * @throws IOException
1763: * @return a set of objects created (the exact type is at the discretion of the caller)
1764: */
1765: public Set/*<?>*/instantiate() throws IOException;
1766:
1767: /** Initializes this iterator, called from WizardDescriptor's constructor.
1768: *
1769: * @param wizard wizard's descriptor
1770: */
1771: public void initialize(WizardDescriptor wizard);
1772:
1773: /** Uninitializes this iterator, called when the wizard is being
1774: * closed, no matter what closing option invoked.
1775: *
1776: * @param wizard wizard's descriptor
1777: */
1778: public void uninitialize(WizardDescriptor wizard);
1779: }
1780:
1781: /**
1782: * Iterator for a wizard that needs to somehow instantiate new objects outside ATW queue.
1783: * (This interface can replace
1784: * <a href="@OPENIDE/LOADERS@/org/openide/loaders/TemplateWizard.Iterator.html"><code>TemplateWizard.Iterator</code></a>
1785: * in a template's declaration.)
1786: * @since org.openide/1 6.5
1787: */
1788: public interface AsynchronousInstantiatingIterator<Data> extends
1789: InstantiatingIterator<Data> {
1790:
1791: /**
1792: * Is called in separate thread when the Finish button
1793: * are clicked and allows implement asynchronous
1794: * instantating of newly created objects.
1795: *
1796: * @throws IOException when instantiate fails
1797: * @return a set of objects created (the exact type is at the discretion of the caller)
1798: */
1799: public Set/*<?>*/instantiate() throws IOException;
1800:
1801: }
1802:
1803: /**
1804: * Iterator for a wizard that wants to notify users while instantiate is running by a progress bar.
1805: * The method <code>instantiate</code> is called outside ATW queue.
1806: * (This interface can replace
1807: * <a href="@OPENIDE/LOADERS@/org/openide/loaders/TemplateWizard.Iterator.html"><code>TemplateWizard.Iterator</code></a>
1808: * in a template's declaration.)
1809: * @since org.openide.dialogs 7.1
1810: */
1811: public interface ProgressInstantiatingIterator<Data> extends
1812: AsynchronousInstantiatingIterator<Data> {
1813:
1814: /**
1815: * Is called in separate thread when the Finish button
1816: * are clicked and allows implement asynchronous instantating of newly created objects.
1817: * While instantiating users are notified by progress bar in wizard's panel. Notfication will
1818: * be visualized by a progress bar.
1819: * Note: The <code>ProgressHandle</code> is not started, need to start it and report progress by
1820: * messages in the <code>progress()</code> method.
1821: *
1822: * @param handle progress bar handle
1823: * @throws IOException when instantiate fails
1824: * @return a set of objects created (the exact type is at the discretion of the caller)
1825: */
1826: public Set/*<?>*/instantiate(ProgressHandle handle)
1827: throws IOException;
1828:
1829: }
1830:
1831: /** Special iterator that works on an array of <code>Panel</code>s.
1832: */
1833: public static class ArrayIterator<Data> extends Object implements
1834: Iterator<Data> {
1835: /** Array of items.
1836: */
1837: private Panel<Data>[] panels;
1838:
1839: /** Index into the array
1840: */
1841: private int index;
1842:
1843: /* Default constructor. It's here to allow subclasses to
1844: * be serializable easily. Panel initialization is done
1845: * through initializePanels() protected method. */
1846: public ArrayIterator() {
1847: panels = initializePanels();
1848: index = 0;
1849: }
1850:
1851: /** Construct an iterator.
1852: * @param array the list of panels to use
1853: */
1854: public ArrayIterator(Panel<Data>[] array) {
1855: panels = array;
1856: index = 0;
1857: }
1858:
1859: /**
1860: * Construct an iterator.
1861: * @param panels the list of panels to use
1862: * @since org.openide.dialogs 7.5
1863: */
1864: @SuppressWarnings("unchecked")
1865: // exists so that other code does not have to do it
1866: public ArrayIterator(List<Panel<Data>> panels) {
1867: this .panels = panels.toArray(new Panel[panels.size()]);
1868: index = 0;
1869: }
1870:
1871: /** Allows subclasses to initialize their arrays of panels when
1872: * constructed using default constructor.
1873: * (for example during deserialization.
1874: * Default implementation returns empty array. */
1875: @SuppressWarnings("unchecked")
1876: protected Panel<Data>[] initializePanels() {
1877: return new Panel[0];
1878: }
1879:
1880: /* The current panel.
1881: */
1882: public Panel<Data> current() {
1883: return panels[index];
1884: }
1885:
1886: /* Current name of the panel */
1887: public String name() {
1888: return NbBundle.getMessage(WizardDescriptor.class,
1889: "CTL_ArrayIteratorName", index + 1, panels.length);
1890: }
1891:
1892: /* Is there a next panel?
1893: * @return true if so
1894: */
1895: public boolean hasNext() {
1896: return index < (panels.length - 1);
1897: }
1898:
1899: /* Is there a previous panel?
1900: * @return true if so
1901: */
1902: public boolean hasPrevious() {
1903: return index > 0;
1904: }
1905:
1906: /* Moves to the next panel.
1907: * @exception NoSuchElementException if the panel does not exist
1908: */
1909: public synchronized void nextPanel() {
1910: if ((index + 1) == panels.length) {
1911: throw new java.util.NoSuchElementException();
1912: }
1913:
1914: index++;
1915: }
1916:
1917: /* Moves to previous panel.
1918: * @exception NoSuchElementException if the panel does not exist
1919: */
1920: public synchronized void previousPanel() {
1921: if (index == 0) {
1922: throw new java.util.NoSuchElementException();
1923: }
1924:
1925: index--;
1926: }
1927:
1928: /* Ignores the listener, there are no changes in order of panels.
1929: */
1930: public void addChangeListener(ChangeListener l) {
1931: }
1932:
1933: /* Ignored.
1934: */
1935: public void removeChangeListener(ChangeListener l) {
1936: }
1937:
1938: /** Resets this iterator to initial state.
1939: * Called by subclasses when they need re-initialization of the iterator.
1940: */
1941: protected void reset() {
1942: index = 0;
1943: }
1944: }
1945:
1946: /** Listener to changes in the iterator and panels.
1947: */
1948: private final class Listener implements ChangeListener,
1949: ActionListener {
1950: Listener() {
1951: }
1952:
1953: /** Change in the observed objects */
1954: public void stateChanged(ChangeEvent ev) {
1955: updateState();
1956: }
1957:
1958: /** Action listener */
1959: public void actionPerformed(ActionEvent ev) {
1960: final Iterator<?> panels = data
1961: .getIterator(WizardDescriptor.this );
1962: if (wizardPanel != null) {
1963: wizardPanel.setErrorMessage(" ", null); //NOI18N
1964: }
1965:
1966: Object src = ev.getSource();
1967: err
1968: .log(Level.FINE, "actionPerformed entry. Source: "
1969: + src); // NOI18N
1970: if (src == nextButton) {
1971: final Dimension previousSize = panels.current()
1972: .getComponent().getSize();
1973: Runnable onValidPerformer = new Runnable() {
1974:
1975: public void run() {
1976: err
1977: .log(Level.FINE,
1978: "onValidPerformer on next button entry.");
1979: panels.nextPanel();
1980: try {
1981: // change UI to show next step, show wait cursor during
1982: // the change
1983: goToNextStep(previousSize);
1984: } catch (IllegalStateException ise) {
1985: panels.previousPanel();
1986: String msg = ise.getMessage();
1987: if (msg != null) {
1988: // this is only for backward compatitility
1989: DialogDisplayer.getDefault().notify(
1990: new NotifyDescriptor.Message(
1991: msg));
1992: } else {
1993: // this should be used (it checks for exception
1994: // annotations and severity)
1995: Exceptions.printStackTrace(ise);
1996: }
1997: updateState();
1998: }
1999: err
2000: .log(Level.FINE,
2001: "onValidPerformer on next button exit.");
2002: }
2003: };
2004: lazyValidate(panels.current(), onValidPerformer);
2005: }
2006:
2007: if (ev.getSource() == previousButton) {
2008: panels.previousPanel();
2009:
2010: // show wait cursor when updating previous button
2011: updateStateWithFeedback();
2012: }
2013:
2014: if (ev.getSource() == finishButton) {
2015: Runnable onValidPerformer = new Runnable() {
2016: public void run() {
2017: err
2018: .log(Level.FINE,
2019: "onValidPerformer on finish button entry."); // NOI18N
2020:
2021: // disable all buttons to indicate that instantiate runs
2022: previousButton.setEnabled(false);
2023: nextButton.setEnabled(false);
2024: finishButton.setEnabled(false);
2025: cancelButton.setEnabled(false);
2026:
2027: Runnable performFinish = new Runnable() {
2028: public void run() {
2029: err.log(Level.FINE,
2030: "performFinish entry."); // NOI18N
2031: Object oldValue = getValue();
2032:
2033: // do instantiate
2034: try {
2035: callInstantiate();
2036: setValueWithoutPCH(OK_OPTION);
2037: resetWizard();
2038: } catch (IOException ioe) {
2039: // notify to log
2040: err.log(Level.INFO, null, ioe);
2041:
2042: setValueWithoutPCH(NEXT_OPTION);
2043: updateStateWithFeedback();
2044:
2045: // notify user by the wizard's status line
2046: putProperty(PROP_ERROR_MESSAGE, ioe
2047: .getLocalizedMessage());
2048:
2049: // if validation failed => cannot move to next panel
2050: return;
2051: } catch (RuntimeException x) {
2052: // notify to log
2053: err.log(Level.WARNING, null, x);
2054:
2055: setValueWithoutPCH(NEXT_OPTION);
2056: updateStateWithFeedback();
2057:
2058: // notify user by the wizard's status line
2059: putProperty(PROP_ERROR_MESSAGE, x
2060: .getLocalizedMessage());
2061:
2062: // if validation failed => cannot move to next panel
2063: return;
2064: }
2065: firePropertyChange(PROP_VALUE,
2066: oldValue, OK_OPTION);
2067:
2068: SwingUtilities
2069: .invokeLater(new Runnable() {
2070: public void run() {
2071: // all is OK
2072: // close wizrad
2073: err
2074: .log(
2075: Level.FINE,
2076: "WD.finishOption.fireActionPerformed()");
2077: finishOption
2078: .fireActionPerformed();
2079: err
2080: .log(
2081: Level.FINE,
2082: "Set value to OK_OPTION.");
2083: setValue(OK_OPTION);
2084: }
2085: });
2086: err.log(Level.FINE,
2087: "performFinish exit."); // NOI18N
2088: }
2089: };
2090:
2091: if (panels instanceof AsynchronousInstantiatingIterator) {
2092: err
2093: .log(Level.FINE,
2094: "Do ASYNCHRONOUS_JOBS_RP.post(performFinish)."); // NOI18N
2095: ASYNCHRONOUS_JOBS_RP.post(performFinish);
2096: } else {
2097: err.log(Level.FINE, "Run performFinish."); // NOI18N
2098: performFinish.run();
2099: }
2100:
2101: err
2102: .log(Level.FINE,
2103: "onValidPerformer on finish button exit."); // NOI18N
2104:
2105: }
2106: };
2107: lazyValidate(panels.current(), onValidPerformer);
2108: }
2109:
2110: if (ev.getSource() == cancelButton) {
2111: if (backgroundValidationTask != null) {
2112: backgroundValidationTask.cancel();
2113: }
2114: Object oldValue = getValue();
2115: setValueWithoutPCH(CANCEL_OPTION);
2116:
2117: if (Arrays.asList(getClosingOptions()).contains(
2118: cancelButton)) {
2119: try {
2120: resetWizard();
2121: } catch (RuntimeException x) {
2122: // notify to log
2123: err.log(Level.INFO, null, x);
2124: }
2125:
2126: }
2127:
2128: firePropertyChange(PROP_VALUE, oldValue, CANCEL_OPTION);
2129: }
2130: }
2131: }
2132:
2133: /** Listenes on a users client property changes
2134: */
2135: private class PropL implements PropertyChangeListener {
2136: PropL() {
2137: }
2138:
2139: /** Accepts client property changes of user component */
2140: public void propertyChange(PropertyChangeEvent e) {
2141: if (wizardPanel == null) {
2142: return;
2143: }
2144:
2145: String propName = e.getPropertyName();
2146: setPanelProperties((JComponent) wizardPanel
2147: .getRightComponent());
2148:
2149: if (PROP_CONTENT_DATA.equals(propName)) {
2150: wizardPanel.setContent(contentData);
2151: updateButtonAccessibleDescription();
2152: } else if (PROP_CONTENT_SELECTED_INDEX.equals(propName)) {
2153: wizardPanel.setSelectedIndex(contentSelectedIndex);
2154: updateButtonAccessibleDescription();
2155: } else if (PROP_CONTENT_BACK_COLOR.equals(propName)) {
2156: wizardPanel.setContentBackColor(contentBackColor);
2157: } else if (PROP_CONTENT_FOREGROUND_COLOR.equals(propName)) {
2158: wizardPanel
2159: .setContentForegroundColor(contentForegroundColor);
2160: } else if (PROP_IMAGE.equals(propName)) {
2161: wizardPanel.setImage(image);
2162: } else if (PROP_IMAGE_ALIGNMENT.equals(propName)) {
2163: wizardPanel.setImageAlignment(imageAlignment);
2164: } else if (PROP_HELP_URL.equals(propName)) {
2165: wizardPanel.setHelpURL(helpURL);
2166: }
2167: }
2168: }
2169:
2170: // end of calling to InstantiatingIterator
2171:
2172: /** Panel which paints image as its background.
2173: */
2174: private static class ImagedPanel extends JComponent implements
2175: Accessible, Runnable {
2176: /** background image */
2177: Image image;
2178:
2179: /** helper variables for passing image between threads and painting
2180: * methods */
2181: Image tempImage;
2182:
2183: /** helper variables for passing image between threads and painting
2184: * methods */
2185: Image image2Load;
2186:
2187: /** true if default image is used */
2188: boolean isDefault = false;
2189:
2190: /** true if loading of image is in progress, false otherwise */
2191: boolean loadPending = false;
2192: boolean north = true;
2193:
2194: /** sync lock for image variables access */
2195: private final Object IMAGE_LOCK = new Object();
2196:
2197: /** Constrcuts panel with given image on background.
2198: * @param im background image, null means default image
2199: */
2200: public ImagedPanel(Image im) {
2201: setImage(im);
2202: setLayout(new BorderLayout());
2203: setOpaque(true);
2204: }
2205:
2206: /** Overriden to paint backround image */
2207: @Override
2208: protected void paintComponent(Graphics graphics) {
2209: graphics.setColor(getBackground());
2210: graphics.fillRect(0, 0, getWidth(), getHeight());
2211:
2212: if (image != null) {
2213: graphics.drawImage(image, 0, north ? 0
2214: : (getHeight() - image.getHeight(null)), this );
2215: } else if (image2Load != null) {
2216: loadImageInBackground(image2Load);
2217: image2Load = null;
2218: }
2219: }
2220:
2221: public void setImageAlignment(String align) {
2222: north = "North".equals(align); // NOI18N
2223: }
2224:
2225: /** Sets background image for this component. Image will be loaded
2226: * asynchronously if not loaded yet. Null means default image.
2227: */
2228: public void setImage(Image im) {
2229: if (im != null) {
2230: loadImage(im);
2231: isDefault = false;
2232:
2233: return;
2234: }
2235:
2236: if (!isDefault) {
2237: loadImage(getDefaultImage());
2238: isDefault = true;
2239: }
2240: }
2241:
2242: private void loadImage(Image im) {
2243: // check image and just set variable if fully loaded already
2244: MediaTracker mt = new MediaTracker(this );
2245: mt.addImage(im, 0);
2246:
2247: if (mt.checkID(0)) {
2248: image = im;
2249:
2250: if (isShowing()) {
2251: repaint();
2252: }
2253:
2254: return;
2255: }
2256:
2257: // start loading in background or just mark that loading should
2258: // start when paint is invoked
2259: if (isShowing()) {
2260: loadImageInBackground(im);
2261: } else {
2262: synchronized (IMAGE_LOCK) {
2263: image = null;
2264: }
2265:
2266: image2Load = im;
2267: }
2268: }
2269:
2270: private void loadImageInBackground(Image image) {
2271: synchronized (IMAGE_LOCK) {
2272: tempImage = image;
2273:
2274: // coalesce with previous task if hasn't really started yet
2275: if (loadPending) {
2276: return;
2277: }
2278:
2279: loadPending = true;
2280: }
2281:
2282: // 30ms is safety time to ensure code will run asynchronously
2283: RequestProcessor.getDefault().post(this , 30);
2284: }
2285:
2286: /** Loads image stored in image2Load variable.
2287: * Then invokes repaint when image is fully loaded.
2288: */
2289: public void run() {
2290: Image localImage;
2291:
2292: // grab value
2293: synchronized (IMAGE_LOCK) {
2294: localImage = tempImage;
2295: tempImage = null;
2296: loadPending = false;
2297: }
2298:
2299: // actually loads image
2300: ImageIcon localImageIcon = new ImageIcon(localImage);
2301: boolean shouldRepaint = false;
2302:
2303: synchronized (IMAGE_LOCK) {
2304: // don't commit results if another loading was started after us
2305: if (!loadPending) {
2306: image = localImageIcon.getImage();
2307:
2308: // keep repaint call out of sync section
2309: shouldRepaint = true;
2310: }
2311: }
2312:
2313: if (shouldRepaint) {
2314: repaint();
2315: }
2316: }
2317: }
2318:
2319: /** Text list cell renderer. Wraps text of items at specified width. Allows numbering
2320: * of items.
2321: */
2322: private static class WrappedCellRenderer extends JPanel implements
2323: ListCellRenderer {
2324: JTextArea ta = new JTextArea();
2325: JLabel numberLabel;
2326: int selected = -1;
2327: boolean contentNumbered;
2328: int taWidth;
2329:
2330: /**
2331: * @param contentNumbered Whether content will be numbered
2332: * @param wrappingWidth Width of list item.
2333: */
2334: private WrappedCellRenderer(boolean contentNumbered,
2335: int wrappingWidth) {
2336: super (new BorderLayout());
2337: this .contentNumbered = contentNumbered;
2338:
2339: ta.setOpaque(false);
2340: ta.setEditable(false);
2341: ta.setLineWrap(true);
2342: ta.setWrapStyleWord(true);
2343: ta.setFont(UIManager.getFont("Label.font")); // NOI18N
2344: ta.getAccessibleContext().setAccessibleDescription(""); // NOI18N
2345:
2346: taWidth = wrappingWidth - 12 - 12;
2347:
2348: numberLabel = new JLabel() {
2349: @Override
2350: protected void paintComponent(Graphics g) {
2351: super .paintComponent(g);
2352:
2353: // #9804. Draw bullet if the content is not numbered.
2354: if (!WrappedCellRenderer.this .contentNumbered) {
2355: java.awt.Rectangle rect = g.getClipBounds();
2356: g.fillOval(rect.x, rect.y, 7, 7);
2357: }
2358: }
2359: };
2360: numberLabel.setLabelFor(ta); // a11y
2361: numberLabel.setHorizontalAlignment(SwingConstants.LEFT);
2362: numberLabel.setVerticalAlignment(SwingConstants.TOP);
2363: numberLabel.setFont(ta.getFont());
2364: numberLabel.setOpaque(false);
2365: numberLabel.setPreferredSize(new Dimension(25, 0));
2366: add(numberLabel, BorderLayout.WEST);
2367: taWidth -= 25;
2368:
2369: Insets taInsets = ta.getInsets();
2370: ta.setSize(taWidth, taInsets.top + taInsets.bottom + 1);
2371:
2372: add(ta, BorderLayout.CENTER);
2373: setBorder(BorderFactory.createEmptyBorder(0, 12, 0, 12));
2374: setOpaque(false);
2375: }
2376:
2377: public Component getListCellRendererComponent(JList list,
2378: Object value, int index, boolean isSelected,
2379: boolean cellHasFocus) {
2380: if (index == selected) {
2381: numberLabel.setFont(doDeriveFont(numberLabel.getFont(),
2382: Font.BOLD));
2383: ta.setFont(doDeriveFont(ta.getFont(), Font.BOLD));
2384: } else {
2385: numberLabel.setFont(doDeriveFont(numberLabel.getFont(),
2386: Font.PLAIN));
2387: ta.setFont(doDeriveFont(ta.getFont(), Font.PLAIN));
2388: }
2389:
2390: if (contentNumbered) {
2391: numberLabel.setText(Integer.toString(index + 1) + "."); // NOI18N
2392: }
2393:
2394: // #21322: on JDK1.4 wrapping width is cleared between two rendering runs
2395: Insets taInsets = ta.getInsets();
2396: ta.setSize(taWidth, taInsets.top + taInsets.bottom + 1);
2397: ta.setText((String) value);
2398:
2399: return this ;
2400: }
2401:
2402: private void setSelectedIndex(int i) {
2403: selected = i;
2404: }
2405:
2406: private void setForegroundColor(Color color) {
2407: if (numberLabel != null) {
2408: numberLabel.setForeground(color);
2409: numberLabel.setBackground(color);
2410: }
2411:
2412: ta.setForeground(color);
2413: }
2414: }
2415:
2416: /** Wizard panel. Allows auto layout of content, wizard panel name and input panel.
2417: */
2418: private static class WizardPanel extends JPanel {
2419: /** Users panel is inserted into this panel. */
2420: private JPanel rightPanel = new JPanel(new BorderLayout());
2421:
2422: /** Name of the users panel. */
2423: private JLabel panelName = new JLabel("Step"); //NOI18N
2424:
2425: /** List of content. */
2426: private JList contentList;
2427:
2428: /** Users component. Should be held for removing from rightPanel */
2429: private Component rightComponent;
2430:
2431: /** Panel which paints image */
2432: private ImagedPanel contentPanel;
2433:
2434: /** Name of content. Can be switched off. */
2435: private JPanel contentLabelPanel;
2436:
2437: /** Wrapped list cell renderer */
2438: private WrappedCellRenderer cellRenderer;
2439:
2440: /** Tabbed pane is used only when both content and help are displayed */
2441: private JTabbedPane tabbedPane;
2442:
2443: /** HTML Browser is used only when help is displayed in the left pane */
2444: private HtmlBrowser htmlBrowser;
2445:
2446: /** Each wizard panel have to be larger or same as this */
2447: private Dimension cachedDimension;
2448:
2449: /** Label of steps pane */
2450: private JLabel label;
2451:
2452: private JPanel progressBarPanel;
2453:
2454: /** Selected index of content */
2455: private int selectedIndex;
2456: private javax.swing.JLabel m_lblMessage;
2457: private Color nbErrorForeground;
2458: private Color nbWarningForeground;
2459:
2460: /** Creates new <CODE>WizardPanel<CODE>.
2461: * @param contentDisplayed whether content will be displayed in the left pane
2462: * @param helpDisplayed whether help will be displayed in the left pane
2463: * @param contentNumbered whether content will be numbered
2464: * @param leftDimension dimension of content or help pane
2465: */
2466: public WizardPanel(boolean contentDisplayed,
2467: boolean helpDispalyed, boolean contentNumbered,
2468: Dimension leftDimension) {
2469: super (new BorderLayout());
2470: initComponents(contentDisplayed, helpDispalyed,
2471: contentNumbered, leftDimension);
2472: setOpaque(false);
2473: resetPreferredSize();
2474: }
2475:
2476: private void initComponents(boolean contentDisplayed,
2477: boolean helpDisplayed, boolean contentNumbered,
2478: Dimension leftDimension) {
2479: if (contentDisplayed) {
2480: createContentPanel(contentNumbered, leftDimension);
2481:
2482: if (!helpDisplayed) {
2483: add(contentPanel, BorderLayout.WEST);
2484: }
2485: }
2486:
2487: if (helpDisplayed) {
2488: htmlBrowser = new BoundedHtmlBrowser(leftDimension);
2489: htmlBrowser.setPreferredSize(leftDimension);
2490:
2491: if (!contentDisplayed) {
2492: add(htmlBrowser, BorderLayout.WEST);
2493: }
2494: }
2495:
2496: if (helpDisplayed && contentDisplayed) {
2497: tabbedPane = new JTabbedPane(JTabbedPane.BOTTOM);
2498: tabbedPane.addTab(NbBundle.getMessage(
2499: WizardDescriptor.class, "CTL_ContentName"),
2500: contentPanel);
2501: tabbedPane.addTab(NbBundle.getMessage(
2502: WizardDescriptor.class, "CTL_HelpName"),
2503: htmlBrowser);
2504: tabbedPane.setEnabledAt(1, false);
2505: tabbedPane.setOpaque(false);
2506:
2507: // tabbedPane.setPreferredSize(leftDimension);
2508: add(tabbedPane, BorderLayout.WEST);
2509: }
2510:
2511: panelName.setBorder(BorderFactory.createMatteBorder(0, 0,
2512: 1, 0, panelName.getForeground()));
2513: panelName.setFont(doDeriveFont(panelName.getFont(),
2514: Font.BOLD));
2515:
2516: JPanel labelPanel = new JPanel(new BorderLayout());
2517: labelPanel.add(panelName, BorderLayout.NORTH);
2518: labelPanel.setBorder(BorderFactory.createEmptyBorder(12,
2519: 12, 12, 11));
2520: rightPanel.setBorder(BorderFactory.createEmptyBorder(0, 12,
2521: 11, 11));
2522: panelName.setLabelFor(labelPanel);
2523:
2524: nbErrorForeground = UIManager
2525: .getColor("nb.errorForeground"); //NOI18N
2526: if (nbErrorForeground == null) {
2527: //nbErrorForeground = new Color(89, 79, 191); // RGB suggested by Bruce in #28466
2528: nbErrorForeground = new Color(255, 0, 0); // RGB suggested by jdinga in #65358
2529: }
2530:
2531: nbWarningForeground = UIManager
2532: .getColor("nb.warningForeground"); //NOI18N
2533: if (nbWarningForeground == null) {
2534: nbWarningForeground = new Color(51, 51, 51); // Label.foreground
2535: }
2536:
2537: JPanel errorPanel = new JPanel(new BorderLayout());
2538: errorPanel.setBorder(BorderFactory.createEmptyBorder(0, 12,
2539: 12, 11));
2540: m_lblMessage = new FixedHeightLabel();
2541: m_lblMessage.setForeground(nbErrorForeground);
2542: errorPanel.add(m_lblMessage, BorderLayout.CENTER);
2543:
2544: progressBarPanel = new JPanel(new BorderLayout());
2545: progressBarPanel.setVisible(false);
2546:
2547: if (contentDisplayed) {
2548: // place for visualize progress bar in content panel (if contentDisplayed)
2549: progressBarPanel.setOpaque(false);
2550: progressBarPanel.setBorder(BorderFactory
2551: .createEmptyBorder(0, 4, 7, 4));
2552: contentPanel.add(progressBarPanel, BorderLayout.SOUTH);
2553: } else {
2554: // placeholder for progress bar components in WizardPanel (if no contentDisplayed set)
2555: progressBarPanel.add(new JLabel(), BorderLayout.NORTH);
2556: JProgressBar pb = new JProgressBar();
2557: pb.setOrientation(JProgressBar.HORIZONTAL);
2558: pb.setAlignmentX(0.5f);
2559: pb.setAlignmentY(0.5f);
2560: pb.setString("0"); // NOI18N
2561: progressBarPanel.add(pb, BorderLayout.CENTER);
2562:
2563: progressBarPanel.setBorder(BorderFactory
2564: .createEmptyBorder(4, 0, 0, 0));
2565: errorPanel.add(progressBarPanel, BorderLayout.SOUTH);
2566: }
2567:
2568: JPanel fullRightPanel = new JPanel(new BorderLayout());
2569: fullRightPanel.add(labelPanel, BorderLayout.NORTH);
2570: fullRightPanel.add(rightPanel, BorderLayout.CENTER);
2571: fullRightPanel.add(errorPanel, BorderLayout.SOUTH);
2572:
2573: // #65506: the wizard panel should fit into window w/o scrollbar
2574: add(fullRightPanel, BorderLayout.CENTER);
2575:
2576: if ((getBorder() == null)
2577: || "GTK".equals(UIManager.getLookAndFeel().getID())) {
2578: // Look & Feel has not set the border already
2579: JSeparator sep = new JSeparator();
2580: sep.setForeground(Color.darkGray);
2581: add(sep, BorderLayout.SOUTH);
2582: }
2583: }
2584:
2585: public void setErrorMessage(String msg, Boolean canContinue) {
2586: m_lblMessage.setForeground(nbErrorForeground);
2587: if (msg != null && msg.trim().length() > 0
2588: && canContinue != null) {
2589: if (canContinue.booleanValue()) {
2590: m_lblMessage
2591: .setIcon(new ImageIcon(
2592: Utilities
2593: .loadImage("org/netbeans/modules/dialogs/warning.gif"))); // NOI18N
2594: m_lblMessage.setForeground(nbWarningForeground);
2595: } else {
2596: m_lblMessage
2597: .setIcon(new ImageIcon(
2598: Utilities
2599: .loadImage("org/netbeans/modules/dialogs/error.gif"))); // NOI18N
2600: }
2601: m_lblMessage.setToolTipText(msg);
2602: } else {
2603: m_lblMessage.setIcon(null);
2604: m_lblMessage.setToolTipText(null);
2605: }
2606:
2607: m_lblMessage.setText(msg);
2608: }
2609:
2610: private void setProgressComponent(JComponent progressComp,
2611: JLabel progressLabel) {
2612: if (progressLabel != null) {
2613: progressLabel.setText(PROGRESS_BAR_DISPLAY_NAME);
2614: progressBarPanel.add(progressLabel, BorderLayout.NORTH);
2615: }
2616: progressBarPanel.add(progressComp, BorderLayout.CENTER);
2617: progressBarPanel.setVisible(true);
2618: }
2619:
2620: /** Creates content panel.
2621: * @param contentNumbered <CODE>boolean</CODE> whether content will be numbered
2622: * @param leftDimension <CODE>Dimension</CODE> dimension of content pane
2623: */
2624: private void createContentPanel(boolean contentNumbered,
2625: Dimension leftDimension) {
2626: contentList = new JList();
2627: cellRenderer = new WrappedCellRenderer(contentNumbered,
2628: leftDimension.width);
2629: cellRenderer.setOpaque(false);
2630: contentList.setCellRenderer(cellRenderer);
2631: contentList.setOpaque(false);
2632: contentList.setEnabled(false);
2633: contentList.getAccessibleContext()
2634: .setAccessibleDescription(""); // NOI18N
2635:
2636: JScrollPane scroll = new JScrollPane(contentList);
2637: scroll
2638: .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
2639: scroll.getViewport().setOpaque(false);
2640: scroll.setBorder(null);
2641: scroll.setOpaque(false);
2642: // #89392: remove GTK's viewport border
2643: scroll.setViewportBorder(null);
2644:
2645: label = new JLabel(NbBundle.getMessage(
2646: WizardDescriptor.class, "CTL_ContentName"));
2647: label.setForeground(Color.white);
2648: label.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0,
2649: label.getForeground()));
2650: label.setFont(doDeriveFont(label.getFont(), Font.BOLD));
2651: contentLabelPanel = new JPanel(new BorderLayout());
2652: contentLabelPanel.setBorder(BorderFactory
2653: .createEmptyBorder(12, 12, 11, 11));
2654: contentLabelPanel.setOpaque(false);
2655: contentLabelPanel.add(label, BorderLayout.NORTH);
2656:
2657: contentPanel = new ImagedPanel(null);
2658: contentPanel.add(contentLabelPanel, BorderLayout.NORTH);
2659: contentPanel.add(scroll, BorderLayout.CENTER);
2660:
2661: contentPanel.setPreferredSize(leftDimension);
2662: label.setLabelFor(contentList);
2663: }
2664:
2665: /** Setter for lists items.
2666: * @param content Array of list items.
2667: */
2668: public void setContent(final String[] content) {
2669: final JList list = contentList;
2670:
2671: if (list == null) {
2672: return;
2673: }
2674:
2675: // #18055: Ensure it runs in AWT thread.
2676: // Remove this when component handling will be assured
2677: // by other means that runs always in AWT.
2678: Mutex.EVENT.writeAccess(new Runnable() {
2679: public void run() {
2680: list.setListData(content);
2681: list.revalidate();
2682: list.repaint();
2683: contentLabelPanel.setVisible(content.length > 0);
2684: }
2685: });
2686: }
2687:
2688: /** Setter for selected list item.
2689: * @param index Index of selected item in the list.
2690: */
2691: public void setSelectedIndex(final int index) {
2692: selectedIndex = index;
2693:
2694: if (cellRenderer != null) {
2695: cellRenderer.setSelectedIndex(index);
2696:
2697: final JList list = contentList;
2698:
2699: if (list == null) {
2700: return;
2701: }
2702:
2703: // #18055. See previous #18055 comment.
2704: Mutex.EVENT.readAccess(new Runnable() {
2705: public void run() {
2706: list.ensureIndexIsVisible(index);
2707:
2708: // Fix of #10787.
2709: // This is workaround for swing bug - BasicListUI doesn't ask for preferred
2710: // size of rendered list cell as a result of property selectedIndex change.
2711: // It does only on certain JList property changes (e.g. fixedCellWidth).
2712: // Maybe subclassing BasicListUI could be better fix.
2713: list.setFixedCellWidth(0);
2714: list.setFixedCellWidth(-1);
2715: }
2716: });
2717: }
2718: }
2719:
2720: /** Setter for content background color.
2721: * @param color content background color.
2722: */
2723: public void setContentBackColor(Color color) {
2724: if (contentPanel != null) {
2725: contentPanel.setBackground(color);
2726: }
2727: }
2728:
2729: /** Setter for content foreground color.
2730: * @param color content foreground color.
2731: */
2732: public void setContentForegroundColor(Color color) {
2733: if (cellRenderer == null) {
2734: return;
2735: }
2736:
2737: cellRenderer.setForegroundColor(color);
2738: label.setForeground(color);
2739: label.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0,
2740: label.getForeground()));
2741: }
2742:
2743: /** Setter for content background image.
2744: * @param image content background image
2745: */
2746: public void setImage(Image image) {
2747: if (contentPanel != null) {
2748: contentPanel.setImage(image);
2749: }
2750: }
2751:
2752: /** Setter for image alignment.
2753: * @param align image alignment - 'North', 'South'
2754: */
2755: public void setImageAlignment(String align) {
2756: if (contentPanel != null) {
2757: contentPanel.setImageAlignment(align);
2758: }
2759: }
2760:
2761: /** Setter for user's component.
2762: * @param c user's component
2763: */
2764: public void setRightComponent(Component c) {
2765: if (rightComponent != null) {
2766: rightPanel.remove(rightComponent);
2767: }
2768:
2769: rightComponent = c;
2770: rightPanel.add(rightComponent, BorderLayout.CENTER);
2771:
2772: // validate();
2773: }
2774:
2775: /** Getter for user's component.
2776: * @return <CODE>Component</CODE> user's component
2777: */
2778: public Component getRightComponent() {
2779: return rightComponent;
2780: }
2781:
2782: /** Setter for wizard panel name.
2783: * @param name panel name
2784: */
2785: public void setPanelName(String name) {
2786: panelName.setText(name);
2787: }
2788:
2789: /** Setter for help URL.
2790: * @param helpURL help URL
2791: */
2792: public void setHelpURL(URL helpURL) {
2793: if (htmlBrowser == null) {
2794: return;
2795: }
2796:
2797: if (helpURL != null) {
2798: if (!helpURL.equals(htmlBrowser.getDocumentURL())) {
2799: htmlBrowser.setURL(helpURL);
2800: }
2801:
2802: if (tabbedPane != null) {
2803: tabbedPane.setEnabledAt(tabbedPane
2804: .indexOfComponent(htmlBrowser), true);
2805: }
2806: } else if (tabbedPane != null) {
2807: tabbedPane.setSelectedComponent(contentPanel);
2808: tabbedPane.setEnabledAt(tabbedPane
2809: .indexOfComponent(htmlBrowser), false);
2810: }
2811: }
2812:
2813: public void resetPreferredSize() {
2814: cachedDimension = new Dimension(600, 365);
2815: }
2816:
2817: @Override
2818: public Dimension getPreferredSize() {
2819: Dimension dim = super .getPreferredSize();
2820:
2821: if (dim.height > cachedDimension.height) {
2822: cachedDimension.height = dim.height;
2823: }
2824:
2825: if (dim.width > cachedDimension.width) {
2826: cachedDimension.width = dim.width;
2827: }
2828:
2829: return cachedDimension;
2830: }
2831:
2832: /** Overriden to delegate call to user component.
2833: */
2834: @Override
2835: public void requestFocus() {
2836: if ((rightComponent != null)
2837: && rightComponent.isDisplayable()) {
2838: JComponent comp = (JComponent) rightComponent;
2839: Container rootAnc = comp.getFocusCycleRootAncestor();
2840: FocusTraversalPolicy policy = rootAnc
2841: .getFocusTraversalPolicy();
2842: Component focus = policy.getComponentAfter(rootAnc,
2843: comp);
2844:
2845: if (focus != null) {
2846: focus.requestFocus();
2847: } else {
2848: comp.requestFocus();
2849: }
2850: } else {
2851: super .requestFocus();
2852: }
2853: }
2854:
2855: /** Overriden to delegate call to user component.
2856: */
2857: @Deprecated
2858: @Override
2859: public boolean requestDefaultFocus() {
2860: if (rightComponent instanceof JComponent) {
2861: return ((JComponent) rightComponent)
2862: .requestDefaultFocus();
2863: }
2864:
2865: return super .requestDefaultFocus();
2866: }
2867:
2868: @Override
2869: public javax.accessibility.AccessibleContext getAccessibleContext() {
2870: if (accessibleContext == null) {
2871: accessibleContext = new AccessibleWizardPanel();
2872: }
2873:
2874: return accessibleContext;
2875: }
2876:
2877: private class AccessibleWizardPanel extends AccessibleJPanel {
2878: AccessibleWizardPanel() {
2879: }
2880:
2881: @Override
2882: public String getAccessibleDescription() {
2883: if (accessibleDescription != null) {
2884: return accessibleDescription;
2885: }
2886:
2887: if (rightComponent instanceof Accessible) {
2888: if (rightComponent.getAccessibleContext()
2889: .getAccessibleDescription() == null) {
2890: return null;
2891: }
2892:
2893: return NbBundle.getMessage(WizardDescriptor.class,
2894: "ACSD_WizardPanel", new Integer(
2895: selectedIndex + 1), panelName
2896: .getText(), rightComponent
2897: .getAccessibleContext()
2898: .getAccessibleDescription());
2899: }
2900:
2901: return super .getAccessibleDescription();
2902: }
2903: }
2904: }
2905:
2906: /** Overriden to return wished preferred size */
2907: private static class BoundedHtmlBrowser extends HtmlBrowser {
2908: Dimension dim;
2909:
2910: public BoundedHtmlBrowser(Dimension d) {
2911: super (false, false);
2912: dim = d;
2913: }
2914:
2915: @Override
2916: public Dimension getPreferredSize() {
2917: return dim;
2918: }
2919: }
2920:
2921: // helper, make possible close wizard as finish
2922: static class FinishAction extends Object {
2923: ActionListener listner;
2924:
2925: public void addActionListener(ActionListener ac) {
2926: listner = ac;
2927: }
2928:
2929: public void removeActionListener(ActionListener ac) {
2930: listner = null;
2931: }
2932:
2933: public void fireActionPerformed() {
2934: if (listner != null) {
2935: listner.actionPerformed(new ActionEvent(this , 0, ""));
2936: }
2937: }
2938: }
2939:
2940: private static final class FixedHeightLabel extends JLabel {
2941:
2942: private static final int ESTIMATED_HEIGHT = 16;
2943:
2944: public FixedHeightLabel() {
2945: super ();
2946: }
2947:
2948: @Override
2949: public Dimension getPreferredSize() {
2950: Dimension preferredSize = super .getPreferredSize();
2951: assert ESTIMATED_HEIGHT == Utilities.loadImage(
2952: "org/netbeans/modules/dialogs/warning.gif")
2953: .getHeight(null) : "Use only 16px icon.";
2954: preferredSize.height = Math.max(ESTIMATED_HEIGHT,
2955: preferredSize.height);
2956: return preferredSize;
2957: }
2958: }
2959:
2960: private static final class SettingsAndIterator<Data> {
2961: private final Iterator<Data> panels;
2962: private final Data settings;
2963: private final boolean useThis;
2964: /** current panel */
2965: private Panel<Data> current;
2966:
2967: public SettingsAndIterator(Iterator<Data> iterator,
2968: Data settings) {
2969: this (iterator, settings, false);
2970: }
2971:
2972: public SettingsAndIterator(Iterator<Data> iterator,
2973: Data settings, boolean useThis) {
2974: this .panels = iterator;
2975: this .settings = settings;
2976: this .useThis = useThis;
2977: }
2978:
2979: public static SettingsAndIterator<WizardDescriptor> create(
2980: Iterator<WizardDescriptor> iterator) {
2981: return new SettingsAndIterator<WizardDescriptor>(iterator,
2982: null, true);
2983: }
2984:
2985: public static SettingsAndIterator<Void> empty() {
2986: return new SettingsAndIterator<Void>(new EmptyPanel(),
2987: (Void) null);
2988: }
2989:
2990: public Iterator<Data> getIterator(WizardDescriptor caller) {
2991: return panels;
2992: }
2993:
2994: @SuppressWarnings("unchecked")
2995: public Data getSettings(WizardDescriptor caller) {
2996: return useThis ? (Data) caller : settings;
2997: }
2998:
2999: public SettingsAndIterator<Data> clone(Iterator<Data> it) {
3000: SettingsAndIterator<Data> s = new SettingsAndIterator<Data>(
3001: it, settings, useThis);
3002: return s;
3003: }
3004: }
3005:
3006: private static final class EmptyPanel implements Panel<Void>,
3007: Iterator<Void> {
3008: public Component getComponent() {
3009: return new JPanel();
3010: }
3011:
3012: public HelpCtx getHelp() {
3013: return HelpCtx.DEFAULT_HELP;
3014: }
3015:
3016: public void readSettings(Void settings) {
3017: }
3018:
3019: public void storeSettings(Void settings) {
3020: }
3021:
3022: public boolean isValid() {
3023: return true;
3024: }
3025:
3026: public void addChangeListener(ChangeListener l) {
3027: }
3028:
3029: public void removeChangeListener(ChangeListener l) {
3030: }
3031:
3032: public Panel<Void> current() {
3033: return this ;
3034: }
3035:
3036: public String name() {
3037: return ""; // NORTH
3038: }
3039:
3040: public boolean hasNext() {
3041: return false;
3042: }
3043:
3044: public boolean hasPrevious() {
3045: return false;
3046: }
3047:
3048: public void nextPanel() {
3049: }
3050:
3051: public void previousPanel() {
3052: }
3053: } // end of EmptyPanel
3054: }
|