/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2008, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.swing.wizard; import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.Component; import java.awt.Dialog; import java.awt.HeadlessException; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.WindowEvent; import java.util.HashMap; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSeparator; import javax.swing.border.EmptyBorder; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; /** * Swing does not provide a wizard construct (boo hiss) so this is a quick dialog that can step us * through a series of pages. * <p> * This code is based on <a * href="http://java.sun.com/developer/technicalArticles/GUI/swing/wizard/">Creating Wizard Dialogs * with Java</a>. * * @author Jody, gdavis * * @source $URL$ */ public class JWizard extends JDialog { private static final long serialVersionUID = 1L; /** * Indicates that the 'Finish' button was pressed to close the dialog. */ public static final int FINISH = 0; /** * Indicates that the 'Cancel' button was pressed to close the dialog, or the user pressed the * close box in the corner of the window. */ public static final int CANCEL = 1; /** * Indicates that the dialog closed due to an internal error. */ public static final int ERROR = 2; Controller controller = new Controller(); HashMap<String, JPage> model = new HashMap<String, JPage>(); JPage current; private JPanel cardPanel; private CardLayout cardLayout; private JButton backButton; private JButton nextButton; private JButton finishButton; private JButton cancelButton; private int returnCode; public JWizard(String title) throws HeadlessException { super(); setTitle(title); initComponents(); } public JWizard(Dialog owner, String title) throws HeadlessException { super(owner, title, true, null); initComponents(); } private void initComponents() { JPanel buttonPanel = new JPanel(); Box buttonBox = new Box(BoxLayout.X_AXIS); cardPanel = new JPanel(); cardPanel.setBorder(new EmptyBorder(new Insets(5, 10, 5, 10))); cardLayout = new CardLayout(); cardPanel.setLayout(cardLayout); backButton = new JButton("Back"); nextButton = new JButton("Next"); finishButton = new JButton("Finish"); cancelButton = new JButton("Cancel"); backButton.addActionListener(controller); nextButton.addActionListener(controller); finishButton.addActionListener(controller); cancelButton.addActionListener(controller); buttonPanel.setLayout(new BorderLayout()); buttonPanel.add(new JSeparator(), BorderLayout.NORTH); buttonBox.setBorder(new EmptyBorder(new Insets(5, 10, 5, 10))); buttonBox.add(backButton); buttonBox.add(Box.createHorizontalStrut(10)); buttonBox.add(nextButton); buttonBox.add(Box.createHorizontalStrut(30)); buttonBox.add(finishButton); buttonBox.add(Box.createHorizontalStrut(10)); buttonBox.add(cancelButton); buttonPanel.add(buttonBox, java.awt.BorderLayout.EAST); getContentPane().add(buttonPanel, java.awt.BorderLayout.SOUTH); getContentPane().add(new JScrollPane(cardPanel), java.awt.BorderLayout.CENTER); } public Boolean isCancelEnabled() { return cancelButton == null ? null : cancelButton.isEnabled(); } public void setCancelEnabled(Boolean isEnabled) { Boolean oldValue = cancelButton.isEnabled(); if (isEnabled != oldValue) { firePropertyChange("isCancelEnabled", oldValue, isEnabled); cancelButton.setEnabled(isEnabled); } } public Boolean isNextEnabled() { return nextButton == null ? null : nextButton.isEnabled(); } public void setNextEnabled(Boolean isEnabled) { Boolean oldValue = nextButton.isEnabled(); if (isEnabled != oldValue) { firePropertyChange("isNextEnabled", oldValue, isEnabled); nextButton.setEnabled(isEnabled); } } public void setFinishEnabled(Boolean isEnabled) { Boolean oldValue = finishButton.isEnabled(); if (isEnabled != oldValue) { firePropertyChange("isFinishEnabled", oldValue, isEnabled); finishButton.setEnabled(isEnabled); } } public Boolean isBackEnabled() { return backButton == null ? null : backButton.isEnabled(); } public void setBackEnabled(Boolean isEnabled) { Boolean oldValue = backButton.isEnabled(); if (isEnabled != oldValue) { firePropertyChange("isBackEnabled", oldValue, isEnabled); backButton.setEnabled(isEnabled); } } /** * Closes the dialog and sets the return code to the integer parameter. * * @param code * The return code. */ protected void close(int code) { returnCode = code; for( Component card : cardPanel.getComponents() ){ String id = card.getName(); JPage page = model.get(id); if( page != null ){ try { page.dispose(); } catch (Throwable t ){ System.out.println( id + " close: "+ t ); } } } dispose(); } public void windowClosing(WindowEvent e) { returnCode = CANCEL; } /** * Retrieves the last return code set by the dialog. * * @return An integer that identifies how the dialog was closed. See the *_RETURN_CODE constants * of this class for possible values. */ public int getReturnCode() { return returnCode; } /** * Convenience method that displays a modal wizard dialog and blocks until the dialog has * completed. * * @return Indicates how the dialog was closed one of CANCEL, ERROR, FINISH */ public int showModalDialog() { setModal(true); pack(); setVisible(true); return returnCode; } /** * Called to display a page. * <p> * * @param id */ public void setCurrentPanel(String id) { if (id == null) { close(ERROR); } JPage old = current; if (old != null) { old.preClosePanel(); } if (!model.containsKey(id)) { close(ERROR); } JPage page = model.get(id); if (page == null) { close(ERROR); } current = page; JPanel panel = null; // see if panel is already created for (Component card : cardPanel.getComponents()) { if (id.equals(card.getName())) { panel = (JPanel) card; break; } } if (panel == null) { // lazy create panel panel = page.getPanel(); panel.setName(id); if (panel == null) { close(ERROR); // card panel not provided } cardPanel.add(panel, id); } controller.syncButtonsToPage(); page.preDisplayPanel(); // Show the panel in the dialog. cardLayout.show(cardPanel, id); page.postDisplayPanel(); } public void registerWizardPanel(JPage page) { page.setJWizard(this); model.put(page.getPageIdentifier(), page); page.setJWizard(this); if (page.getPageIdentifier() == JPage.DEFAULT) { setCurrentPanel(page.getPageIdentifier()); } } /** * The controller can be hooked up to your own fields or lists; it will * call syncWizardButtons() which will use validate to update the buttons * in response to user input. * * @return Controller */ public Controller getController() { return controller; } /** The controller listens to everything and updates the buttons */ public class Controller implements ActionListener, KeyListener, DocumentListener, ListSelectionListener { public boolean listen = true; public void actionPerformed(ActionEvent e) { if( !listen ) return; if (e.getSource() == cancelButton || e.getActionCommand().equals("Canel")){ cancelButtonPressed(); } else if (e.getSource() == backButton || e.getActionCommand().equals("Back")){ backButtonPressed(); } else if (e.getSource() == nextButton || e.getActionCommand().equals("Next")){ nextButtonPressed(); } else if (e.getSource() == finishButton || e.getActionCommand().equals("Finish")){ finishButtonPressed(); } else { syncButtonsToPage(); } } private void cancelButtonPressed() { close(CANCEL); } private void finishButtonPressed() { // If it is a finishable panel, close down the dialog. Otherwise, // get the ID that the current panel identifies as the next panel, // and display it. if (current == null) { // we are lost - no page has been registered close(ERROR); return; } if( !current.isValid() ){ syncButtonsToPage(); return; // not valid so we cannot go on } current.preClosePanel(); close(FINISH); } private void nextButtonPressed() { // get the ID that the current panel identifies as the next panel, // and display it. if (current == null) { // we are lost - no page has been registered close(ERROR); return; } if( !current.isValid() ){ syncButtonsToPage(); return; // not valid so we cannot go on } String nextId = current.getNextPageIdentifier(); setCurrentPanel(nextId); } private void backButtonPressed() { if (current == null) { // we are lost - no page has been registered close(ERROR); return; } String backId = current.getBackPageIdentifier(); setCurrentPanel(backId); } /** * Set listen to false to update a field without the controller * passing on a notification. * * @param listen */ public void setListen( boolean listen ){ this.listen = listen; } public void syncButtonsToPage() { String backPageId = current.getBackPageIdentifier(); String nextPageId = current.getNextPageIdentifier(); boolean isValid = current.isValid(); setBackEnabled(backPageId != null); if( nextPageId == null ){ // next page has not been sorted out yet setNextEnabled( false ); setFinishEnabled( false ); } else if (nextPageId == JPage.FINISH) { setNextEnabled(false); setFinishEnabled( isValid ); } else { setNextEnabled( isValid ); setFinishEnabled( false ); } } public void keyPressed(KeyEvent e) { // ignore } public void keyReleased(KeyEvent e) { syncButtonsToPage(); } public void keyTyped(KeyEvent e) { syncButtonsToPage(); } public void changedUpdate(DocumentEvent e) { syncButtonsToPage(); } public void insertUpdate(DocumentEvent e) { syncButtonsToPage(); } public void removeUpdate(DocumentEvent e) { syncButtonsToPage(); } public void valueChanged(ListSelectionEvent e) { syncButtonsToPage(); } } }