package jas.util; import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.Cursor; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowEvent; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JRootPane; /** * The wizard is useful for getting user input on multiple pages. The JASWizard supports a * tree structure of JASWizardPages, and the path down that tree is determined by each node * when the "Next" button is clicked. At a leaf (or at any node), the "Finish" button can be * clicked and the wizard closes. The "Next" button is enabled when a JASWizardPage that implements * the interface HasNextPages is showing, the "Finish" button is enabled when a page that implements * the interface Finishable is showing, and the "Help" button is enabled when a page that implements * HasHelpPages is showing. * @author Jonas Gifford * @see JASWizardPage * @see HasNextPages * @see Finishable * @see HasHelpPage */ public class JASWizard extends JDialog implements ActionListener { /** * Creates a new wizard that shows itself automatically. * @param parent the parent JFrame * @param title the title for the wizard dialog * @firstPage the root of the tree structure and the page the wizard opens to */ public JASWizard(final JFrame parent, final String title, final JASWizardPage firstPage) { super(parent, title, true); m_frame = parent; m_contentPane = getContentPane(); m_contentPane.setLayout(new BorderLayout()); m_rootPane = getRootPane(); m_wizardPagePanel = new JPanel(m_layout = new CardLayout()); firstPage.addTo(m_wizardPagePanel, this, null); // recursive call final JPanel buttons = new JPanel(new FlowLayout()); buttons.add(m_cancel = new JButton("Cancel")); m_cancel.setMnemonic('C'); buttons.add(m_help = new JButton("Help")); m_help.setMnemonic('H'); buttons.add(m_prev = new JButton("<< Previous")); m_prev.setMnemonic('P'); buttons.add(m_next = new JButton("Next >>")); m_next.setMnemonic('N'); buttons.add(m_finish = new JButton("Finish")); m_finish.setMnemonic('F'); m_wizardPagePanel.setBorder(new javax.swing.border.EtchedBorder()); m_contentPane.add(BorderLayout.CENTER, m_wizardPagePanel); m_contentPane.add(BorderLayout.SOUTH, buttons); pack(); final Dimension mySize = getSize(); final Dimension parentSize = parent.getSize(); final Point position = parent.getLocation(); position.translate((parentSize.width-mySize.width)/2, (parentSize.height-mySize.height)/2); setLocation(position); m_layout.first(m_wizardPagePanel); (m_currentPage = m_firstPage = firstPage).doEnable(); m_cancel.addActionListener(this); m_help.addActionListener(this); m_prev.addActionListener(this); m_next.addActionListener(this); m_finish.addActionListener(this); m_currentPageHasAmbiguousDefault = m_currentPage.isFinishable() && m_currentPage.hasNextPages(); // setDefaultButton(); m_help.setEnabled(firstPage instanceof HasHelpPage); setResizable(false); enableEvents(java.awt.AWTEvent.WINDOW_EVENT_MASK); firstPage.beforeShowing(); Application app = Application.getApplication(); if (app != null) app.modalDialogOpening(this); show(); if (app != null) app.modalDialogClosing(this); } void setNextEnabled(final boolean b) { m_next.setEnabled(b && m_currentPage.hasNextPages()); } void setFinishEnabled(final boolean b) { m_finish.setEnabled(b && m_currentPage.isFinishable()); } void doPrevEnabled() { m_prev.setEnabled(m_currentPage.getPrev() != null); } private void onHelp() { setToWaitCursor(); HasHelpPage h = ((HasHelpPage) m_currentPage); Application.getApplication().showHelpTopic(h.getHelpTopic(),this); setToDefaultCursor(); } private void setToCurrentPage(final JASWizardPage page) { m_currentPage = page; m_currentPageHasAmbiguousDefault = m_currentPage.isFinishable() && m_currentPage.hasNextPages(); page.doEnable(); if (! m_currentPageHasAmbiguousDefault) setDefaultButton(); m_help.setEnabled(page instanceof HasHelpPage); page.beforeShowing(); m_layout.show(m_wizardPagePanel, page.toString()); } private void onPrev() { setToWaitCursor(); try { setToCurrentPage(m_currentPage.getPrev()); } catch (Throwable t) { handleError("Error during wizard processing",t); } finally { setToDefaultCursor(); } } private void onNext() { setToWaitCursor(); try { final JASWizardPage newPage = ((HasNextPages) m_currentPage).getNext(); if (newPage != null) { // Make sure this page was already added to the current (soon to be previous) page, // if not add it now. if (!m_wizardPagePanel.isAncestorOf(newPage)) { newPage.addTo(m_wizardPagePanel, this, m_currentPage); newPage.invalidate(); validate(); } setToCurrentPage(newPage); } } catch (Throwable t) { handleError("Error during wizard processing",t); } finally { setToDefaultCursor(); } } void setDefaultButton() { if (m_currentPageHasAmbiguousDefault && m_next.isEnabled()) m_rootPane.setDefaultButton(m_next); else if (m_currentPageHasAmbiguousDefault && m_finish.isEnabled()) m_rootPane.setDefaultButton(m_finish); else m_rootPane.setDefaultButton(m_currentPage.hasNextPages() ? m_next : m_finish); } /** * Closes the wizard. Invoked by dispose() in JASWizardPage. * @see JASWizardPage * @see JASWizardPage#dispose() */ public void dispose() { m_firstPage.clear(); /* * References in it and all pages linked to * it are set to null. */ JASWizardPage.pageNumber = 0; // next time the pages start at zero, so that the number doesn't get too big if (getCursor() == m_wait) m_frame.setCursor(m_wait); super.dispose(); } private void onFinish() { setToWaitCursor(); try { ((Finishable) m_currentPage).onFinish(); } catch (Throwable t) { handleError("Error during wizard processing",t); } finally { setToDefaultCursor(); } } private void onCancel() { setToWaitCursor(); try { m_firstPage.doCancel(); dispose(); } catch (Throwable t) { handleError("Error during wizard processing",t); } finally { setToDefaultCursor(); } } void setToWaitCursor() { // should set it on frame too, but doesn't work with modal dialog setCursor(m_wait); } void setToDefaultCursor() { setCursor(m_default); m_frame.setCursor(m_default); } /** Public as an implementation side effect; do not call. */ public void actionPerformed(final ActionEvent e) { final Object source = e.getSource(); if (source == m_cancel) onCancel(); else if (source == m_help) onHelp(); else if (source == m_prev) onPrev(); else if (source == m_next) onNext(); else if (source == m_finish) onFinish(); } /** Public as an implementation side effect; do not call. */ public void processWindowEvent(final WindowEvent e) { // super.processWindowEvent(e); final int id = e.getID(); if (id == WindowEvent.WINDOW_CLOSING) onCancel(); else if (id == WindowEvent.WINDOW_ACTIVATED) setDefaultButton(); } protected void handleError(String message, Throwable t) { Application app = Application.getApplication(); if (app != null) app.error("Error during wizard processing",t); else t.printStackTrace(); } private final JFrame m_frame; private final Cursor m_default = Cursor.getDefaultCursor(), m_wait = new Cursor(Cursor.WAIT_CURSOR); private boolean m_currentPageHasAmbiguousDefault; final private JRootPane m_rootPane; final private java.awt.Container m_contentPane; final private JPanel m_wizardPagePanel; final private JASWizardPage m_firstPage; private JASWizardPage m_currentPage; final private JButton m_cancel, m_help, m_prev, m_next, m_finish; final private CardLayout m_layout; }