package org.freehep.swing.wizard;
import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Cursor;
import java.awt.Dialog;
import java.awt.FlowLayout;
import java.awt.Frame;
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.JPanel;
import javax.swing.border.EtchedBorder;
/**
* The wizard is useful for getting user input on multiple pages. The WizardDialog supports a
* tree structure of WizardPages, 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 WizardPage 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 WizardPage
* @see HasNextPages
* @see Finishable
* @see HasHelpPage
*/
public class WizardDialog extends JDialog implements ActionListener
{
private CardLayout m_layout;
private Cursor m_default = Cursor.getDefaultCursor();
private Cursor m_wait = Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR);
private WizardPage m_firstPage;
private JPanel m_wizardPagePanel;
private WizardPage m_currentPage;
private JButton m_cancel;
private JButton m_finish;
private JButton m_help;
private JButton m_next;
private JButton m_prev;
private boolean m_currentPageHasAmbiguousDefault;
/**
* @param title the title for the wizard dialog
* @param firstPage the root of the tree structure and the page the wizard opens to
*/
public WizardDialog(String title, WizardPage firstPage)
{
super();
this.setTitle(title);
init(firstPage);
}
public WizardDialog(Frame owner, String title, WizardPage firstPage)
{
super(owner,title,true);
init(firstPage);
}
public WizardDialog(Dialog owner, String title, WizardPage firstPage)
{
super(owner,title,true);
init(firstPage);
}
private void init(WizardPage firstPage)
{
getContentPane().setLayout(new BorderLayout());
m_wizardPagePanel = new JPanel(m_layout = new CardLayout());
firstPage.addTo(m_wizardPagePanel, this, null); // recursive call
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 EtchedBorder());
getContentPane().add(BorderLayout.CENTER, m_wizardPagePanel);
getContentPane().add(BorderLayout.SOUTH, buttons);
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(AWTEvent.WINDOW_EVENT_MASK);
firstPage.beforeShowing();
}
/** Public as an implementation side effect; do not call. */
public void actionPerformed(ActionEvent e)
{
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();
}
/**
* Closes the wizard. Invoked by <code>dispose()</code> in WizardPage.
* @see WizardPage
* @see WizardPage#dispose()
*/
public void dispose()
{
m_firstPage.clear();
/*
* References in it and all pages linked to
* it are set to null.
*/
WizardPage.pageNumber = 0;
super.dispose();
}
/** Public as an implementation side effect; do not call. */
public void processWindowEvent(WindowEvent e)
{
int id = e.getID();
if (id == WindowEvent.WINDOW_CLOSING)
onCancel();
else if (id == WindowEvent.WINDOW_ACTIVATED)
setDefaultButton();
}
protected void handleError(String message, Throwable t)
{
t.printStackTrace();
}
void setDefaultButton()
{
if (m_currentPageHasAmbiguousDefault && m_next.isEnabled())
getRootPane().setDefaultButton(m_next);
else if (m_currentPageHasAmbiguousDefault && m_finish.isEnabled())
getRootPane().setDefaultButton(m_finish);
else
getRootPane().setDefaultButton(m_currentPage.hasNextPages() ? m_next : m_finish);
}
void setFinishEnabled(boolean b)
{
m_finish.setEnabled(b && m_currentPage.isFinishable());
}
void setNextEnabled(boolean b)
{
m_next.setEnabled(b && m_currentPage.hasNextPages());
}
void setToDefaultCursor()
{
setCursor(m_default);
}
void setToWaitCursor()
{
setCursor(m_wait);
}
void doPrevEnabled()
{
m_prev.setEnabled(m_currentPage.getPrev() != null);
}
private void setToCurrentPage(WizardPage 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 onCancel()
{
setToWaitCursor();
try
{
// This traveses all pages, calling their doCancel method
m_firstPage.doCancel();
dispose();
}
catch (Throwable t)
{
handleError("Error during wizard processing", t);
}
finally
{
setToDefaultCursor();
}
}
private void onFinish()
{
setToWaitCursor();
try
{
((Finishable) m_currentPage).onFinish();
// Notify all previous pages
WizardPage prev = m_currentPage.getPrev();
while (prev != null)
{
prev.onWasFinished();
prev = prev.getPrev();
}
}
catch (Throwable t)
{
handleError("Error during wizard processing", t);
}
finally
{
setToDefaultCursor();
}
}
private void onHelp()
{
setToWaitCursor();
// HasHelpPage h = ((HasHelpPage) m_currentPage);
// Application.getApplication().showHelpTopic(h.getHelpTopic(), this);
setToDefaultCursor();
}
private void onNext()
{
setToWaitCursor();
try
{
WizardPage 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();
}
}
private void onPrev()
{
setToWaitCursor();
try
{
WizardPage prev = m_currentPage.getPrev();
m_currentPage.onPrevious();
setToCurrentPage(prev);
}
catch (Throwable t)
{
handleError("Error during wizard processing", t);
}
finally
{
setToDefaultCursor();
}
}
}