package hermes.browser.dialog;
/**
* Title: JOptionsDialog Utility Abstract Class
* Description: An abstract base class that provides the basic plumbing for an
* options dialog. Specifically, it manages the creation, layout, display
* and event handling of the three option buttons OK, APPLY and CANCEL.
* Copyright: Copyright (c) 2001
**/
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.WindowEvent;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JPanel;
/**
* <p>
* An abstract base class which provides the basic plumbing for an options
* dialog. Specifically, it manages the creation, layout, display and event
* handling of the three option buttons OK, APPLY and CANCEL.
* </p>
*
* <p>
* The appearance and features of the dialog can be configured using the various
* style flags defined in the class. The configuration can be performed during
* both compile-time and run-time. The user can choose to show any combination
* of the option buttons. The user can also change the alignment and size of
* these buttons.
* </p>
*
* <p>
* This class features the "dirty behaviour". The "dirty behaviour" defines two
* states; DIRTY and NOT DIRTY. When in the NOT DIRTY state (e.g. when the
* dialog was first initialized), the apply button is disabled and clicking on
* the OK button will not call <code>updateData(true)</code>. When in the
* DIRTY state (e.g. by calling <code>setDirty()</code>), the apply button
* will be enabled and clicking on the OK button will result in a call to
* <code>updateData(true)</code>. Use the style flag BEHAVE_DIRTYALWAYS to
* cause the dialog to be in the DIRTY state at all times.
* </p>
*
* @author Sidney Chong
* @version $Revision: 1.3 $ $Date: 2007/01/10 15:59:49 $
*/
abstract public class AbstractOptionDialog extends JDialog
{
/**
*
*/
private static final long serialVersionUID = 5881175544998150130L;
/**
* Constant for declaring the OK button. Used in the setDialogProperties()
* method.
*/
public static final int OPTION_OK = 1;
/**
* Constant for declaring the Cancel button. Used in the
* setDialogProperties() method.
*/
public static final int OPTION_CANCEL = 2;
/**
* Constant for declaring the Apply button. Used in the
* setDialogProperties() method.
*/
public static final int OPTION_APPLY = 4;
/**
* Ease-of-use constant for declaring a combination of the OK and Cancel
* buttons. Used in the setDialogProperties() method.
*/
public static final int OPTION_OK_CANCEL = OPTION_OK | OPTION_CANCEL;
/**
* Ease-of-use constant for declaring a combination of the OK, Cancel, Apply
* buttons. Used in the setDialogProperties() method.
*/
public static final int OPTION_OK_CANCEL_APPLY = OPTION_OK | OPTION_CANCEL | OPTION_APPLY;
/**
* Constant for declaring a "left align" style for the option buttons
* layout. Used in the setDialogProperties() method.
*/
public static final int STYLE_LEFT_ALIGN = 8;
/**
* Constant for declaring a "right align" style for the option buttons
* layout. Used in the setDialogProperties() method.
*/
public static final int STYLE_RIGHT_ALIGN = 16;
/**
* Constant for declaring a "center align" style for the option buttons
* layout. Used in the setDialogProperties() method.
*/
public static final int STYLE_CENTER_ALIGN = 32;
/**
* Constant for declaring a "center align" style for the option buttons
* layout. Used in the setDialogProperties() method.
*/
public static final int DISPLAY_CENTER_DIALOG = 64;
/**
* Constant for declaring the behaviour "always dirty", which in essense,
* turns OFF the "dirty behaviour". i.e. The apply button will always be
* enabled and clicking on the OK button will always call
* <code>updateData(true)</code>. Used in the setDialogProperties()
* method.
*/
public static final int BEHAVE_DIRTY_ALWAYS = 1073741824;
/**
* Stores the dimension for the OK, Cancel and Apply buttons.
*/
private Dimension m_buttonDim;
/**
* The panel that contains all the option buttons.
*/
private JPanel m_optionsPane;
/**
* The generic container that stores the other components which make up the
* body of the dialog.
*/
private Container m_bodyPane;
/**
* The individual option buttons.
*/
private JButton m_applyButton, m_okButton, m_cancelButton;
/**
* The style of the dialog. It should contain an ORed combination of the
* available style constants.
*/
private int m_dialogProperties = OPTION_OK_CANCEL_APPLY | STYLE_RIGHT_ALIGN;
/**
* The style of the dialog. It should contain an ORed combination of the
* available style constants.
*/
private boolean m_bIsDirty = false;
/**
* Indicates the last option button clicked by the user.
*/
private int m_returnCode = -1;
/**
* Class constructor.
*
* @param parent
* the parent of this dialog.
* @param name
* the text to be displayed in the title bar of the dialog.
* @param modal
* whether the dialog show be modal or not.
*/
public AbstractOptionDialog(Frame parent, String name, boolean modal)
{
super(parent, name, modal);
/*
* addKeyListener(new KeyAdapter() { public void keyTyped(KeyEvent evt) {
* System.out.println ("keytyped event hit!"); int keycode =
* evt.getKeyCode(); if (keycode == KeyEvent.VK_ACCEPT || keycode ==
* KeyEvent.VK_ENTER) { //fake a mouse hit on OK button
* System.out.println ("enter hit!"); } if (keycode ==
* KeyEvent.VK_CANCEL || keycode == KeyEvent.VK_ESCAPE) { //fake a mouse
* hit on CANCEL button System.out.println ("cancel hit!"); } } });
*/
}
public void init()
{
initComponents();
setDialogProperties(m_dialogProperties, false);
setButtonDimension(new Dimension(73, 27), false);
//pack();
}
/**
* Convenience method to show the dialog in a modal loop and returns the
* code of the option button that was last clicked when the dialog closes.
*
* @return the code of the last option button that was clicked. -1 if none
* of the buttons were clicked (or if the CLOSE button on title bar
* was clicked).
*/
public int doModal()
{
//reset variables
m_returnCode = -1;
boolean tmpVar = isModal();
setModal(true);
show();
setModal(tmpVar);
return m_returnCode;
}
/**
* A helper function to center the dialog box relative to its owner. Note
* that if its owner is not visible or is null, then the dialog box is
* centered relative to the screen.
*/
public void centerDialog()
{
Window owner = getOwner();
Dimension dim;
if (owner == null || owner.isVisible() == false)
{
dim = getToolkit().getScreenSize();
}
else
{
dim = owner.getSize();
}
setLocation((dim.width - getWidth()) / 2, (dim.height - getHeight()) / 2);
}
/**
* Returns the dimension of the buttons. Note that all buttons share the
* same dimension.
*
* @return the dimension of the buttons.
*/
public Dimension getButtonDimension()
{
return m_buttonDim;
}
/**
* Convenience method for
* <code>setButtonDimension(Dimension dim, boolean shouldPack)
* </code> with
* <code>shouldPack</code> set to <code>true</code>.
*
* @param dim
* the new dimension of the buttons.
*/
public void setButtonDimension(Dimension dim)
{
setButtonDimension(dim, true);
}
/**
* Sets the dimension of the buttons. Note that all buttons share the same
* dimension.
*
* @param dim
* the new dimension of the buttons.
* @param shouldPack
* if true, the method will call the <code>pack()</code> method
* after setting the dimension of the buttons.
*/
public void setButtonDimension(Dimension dim, boolean shouldPack)
{
m_buttonDim = dim;
m_okButton.setPreferredSize(m_buttonDim);
m_applyButton.setPreferredSize(m_buttonDim);
m_cancelButton.setPreferredSize(m_buttonDim);
if (shouldPack) pack();
}
/**
* Returns the dialog style.
*
* @return an integer representing the dialog style.
*/
public int getDialogStyle()
{
return m_dialogProperties;
}
/**
* Convenience method for
* <code>setDialogProperties(int flags, boolean shouldPack)
* </code> with
* <code>shouldPack</code> set to <code>true</code>.
*
* @param flags
* the new dialog styles to be set.
*/
public void setDialogProperties(int flags)
{
setDialogProperties(flags, true);
}
/**
* Sets the style of the dialog. Note that the dialog defaults to
* STYLE_RIGHT_ALIGN and OPTION_OK_CANCEL_APPLY if an invalid flag is passed
* in.
*
* @param flags
* the new dialog styles to be set.
* @param shouldPack
* if true, the method will call the <code>pack()</code> method
* after setting the style of the dialog.
*/
public void setDialogProperties(int flags, boolean shouldPack)
{
//reset our style flag
m_dialogProperties = 0;
if (((flags & STYLE_RIGHT_ALIGN) > 0 ? true : false) || ((flags & (STYLE_LEFT_ALIGN | STYLE_CENTER_ALIGN)) == 0 ? true : false))
{
m_optionsPane.setLayout(new FlowLayout(FlowLayout.RIGHT));
m_dialogProperties |= STYLE_RIGHT_ALIGN; //set right align style in
// our style flag
}
else
{
if ((flags & STYLE_LEFT_ALIGN) > 0 ? true : false)
{
m_optionsPane.setLayout(new FlowLayout(FlowLayout.LEFT));
m_dialogProperties |= STYLE_LEFT_ALIGN; //set left align style
// in our style flag
}
else
{
m_optionsPane.setLayout(new FlowLayout(FlowLayout.CENTER));
m_dialogProperties |= STYLE_CENTER_ALIGN; //set center align
// style in our style
// flag
}
}
if (((flags & OPTION_OK) > 0 ? true : false) //is ok button specified?
|| ((flags & OPTION_OK_CANCEL_APPLY) == 0 ? true : false))
{ //OR no buttons specified?
m_okButton.setVisible(true);
m_dialogProperties |= OPTION_OK;
}
else
{
m_okButton.setVisible(false);
}
if (((flags & OPTION_CANCEL) > 0 ? true : false) //is cancel button
// specified?
|| ((flags & OPTION_OK_CANCEL_APPLY) == 0 ? true : false))
{ //OR no buttons specified?
m_cancelButton.setVisible(true);
m_dialogProperties |= OPTION_CANCEL;
}
else
{
m_cancelButton.setVisible(false);
}
if (((flags & OPTION_APPLY) > 0 ? true : false) //is apply button
// specified?
|| ((flags & OPTION_OK_CANCEL_APPLY) == 0 ? true : false))
{ //OR no buttons specified?
m_applyButton.setVisible(true);
m_dialogProperties |= OPTION_APPLY;
}
else
{
m_applyButton.setVisible(false);
}
if ((flags & BEHAVE_DIRTY_ALWAYS) > 0 ? true : false)
{
m_applyButton.setEnabled(true);
m_dialogProperties |= BEHAVE_DIRTY_ALWAYS;
}
else
{
if (m_bIsDirty)
m_applyButton.setEnabled(true);
else
m_applyButton.setEnabled(false);
}
if ((flags & DISPLAY_CENTER_DIALOG) > 0 ? true : false)
{
centerDialog();
}
if (shouldPack) pack();
}
/**
* Initializes the body pane. The implementation should create, layout and
* set up the event handling functions for the body pane's UI components. No
* default implementation is provided for this method.
*
* @return a container that holds all the body pane's UI components.
*/
abstract protected Container initBodyPane();
/**
* Updates the data model. The implementation should be able to update the
* data model in both directions. i.e. FROM the UI components TO the data
* model and vice versa. No default implementation is provided for this
* method.
*
* @param toModel
* a flag which determines the direction of the data flow. If
* <code>
* true</code>, then the update should be FROM UI
* components TO the data model.
*/
abstract protected void updateData(boolean toModel);
/**
* Initializes the dialog components. It is usually called in the class
* constructor to create the various UI components and add them to the
* content pane.
*/
protected void initComponents()
{
getContentPane().setLayout(new BorderLayout());
getContentPane().add(initBodyPane(), BorderLayout.CENTER);
getContentPane().add(initOptionsPane(), BorderLayout.SOUTH);
}
/**
* Initializes the options pane. This method creates and set up the event
* handling function of the option buttons.
*
* @return a container which holds all the option buttons.
*/
protected Container initOptionsPane()
{
m_optionsPane = new JPanel();
//setting up ok button
m_okButton = new JButton();
m_okButton.setText("OK");
m_okButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt)
{
m_returnCode = OPTION_OK;
onOK(evt);
}
});
//set the OK button as the default button
addComponentListener(new ComponentListener()
{
public void componentHidden(ComponentEvent evt)
{
}
public void componentResized(ComponentEvent evt)
{
}
public void componentMoved(ComponentEvent evt)
{
}
public void componentShown(ComponentEvent evt)
{
getRootPane().setDefaultButton(m_okButton);
}
});
m_optionsPane.add(m_okButton);
//setting up cancel button
m_cancelButton = new JButton();
m_cancelButton.setText("Cancel");
m_cancelButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt)
{
m_returnCode = OPTION_CANCEL;
onCancel(evt);
}
});
m_optionsPane.add(m_cancelButton);
//setting up apply button
m_applyButton = new JButton();
m_applyButton.setText("Apply");
m_applyButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt)
{
m_returnCode = OPTION_APPLY;
onApply(evt);
}
});
m_optionsPane.add(m_applyButton);
return m_optionsPane;
}
/**
* Triggers the WINDOW_CLOSING event on this dialog.
*/
protected void fireWindowClosing()
{
dispatchEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING));
}
/**
* Sets the dirty state. If BEHAVE_DIRTY_ALWAYS is not set, then enables the
* apply button as well. This method should be called at least once when the
* user inputs data in the dialog.
*/
public void setDirty()
{
m_bIsDirty = true;
if (!isDirtyAlways() && m_applyButton != null) m_applyButton.setEnabled(true);
}
/**
* Event handler for the OK button clicked event. The default implementation
* simply calls <code>updateData(true)</code> to update the data model (if
* its in the DIRTY state and BEHAVE_DIRTY_ALWAYS is not set) before firing
* the WINDOW_CLOSING event. The user may override this method to provide
* more sophisticated handling of the event.
*
* @Param evt
* the event that triggered this action.
*/
protected void onOK(ActionEvent evt)
{
if (isDirtyAlways() || m_bIsDirty) updateData(true);
fireWindowClosing();
}
/**
* Event handler for the apply button clicked event. The default
* implementation simply calls <code>updateData(true)</code> to update the
* data model and disables the apply button if BEHAVE_DIRTY_ALWAYS is not
* set. The user may override this method to provide more sophisticated
* handling of the event.
*
* @Param evt
* the event that triggered this action.
*/
protected void onApply(ActionEvent evt)
{
updateData(true);
m_bIsDirty = false;
if (!isDirtyAlways()) m_applyButton.setEnabled(false);
}
/**
* Event handler for the cancel button clicked event. The default
* implementation simply fires the WINDOW_CLOSING event. The user may
* override this method to provide more sophisticated handling of the event.
*
* @Param evt
* the event that triggered this action.
*/
protected void onCancel(ActionEvent evt)
{
fireWindowClosing();
}
/**
* Returns the container for the body pane.
*
* @return the container for the body pane.
*/
protected Container getBodyPane()
{
return m_bodyPane;
}
/**
* Returns the container for the options pane.
*
* @return the container for the options pane.
*/
protected JPanel getOptionsPane()
{
return m_optionsPane;
}
/**
* Convenience method to check for the BEHAVE_DIRTY_ALWAYS setting.
*
* @return true if flag is set. false otherwise.
*/
private boolean isDirtyAlways()
{
return ((m_dialogProperties & BEHAVE_DIRTY_ALWAYS) > 0 ? true : false);
}
}