package extensions.awt; import java.awt.*; import java.io.*; import extensions.lang.*; // Allow ButtonAction and MenuItemAction of other // FrameExtended and DialogExtended to show this // DialogExtended instance. class DialogExtendedShowAdapter extends Action { private DialogExtended de; DialogExtendedShowAdapter(DialogExtended de) { this.de = de; } public final boolean mayBlock() { return de.isModal(); } public final void run() { de.show(); } } // Allow for "Quit" ButtonAction instances. class DialogExtendedDestroyAdapter implements ActionProtocol, Runnable { private DialogExtended de; DialogExtendedDestroyAdapter(DialogExtended de) { this.de = de; } public final void action (Component forward, Event evt, Object what) { if (de.destroyMayBlock) (new Thread(this,"DialogExtendedDestroyAdapter")).start(); else run(); } public final void run() { de.destroy(); } } /* Isolates destroy() in separate thread from toolkit callback. */ class DialogExtendedDestroyThread extends Thread { DialogExtended de; DialogExtendedDestroyThread(DialogExtended de) { this.de = de; start(); } public void run() { de.destroy(); } } /** * Upon receiving a WINDOW_DESTROY message, handleEvent() * calls destroy(). Action events are delegated via * the ActionProtocol interface. Delegation allows * the action event handler to be encapsulated in the * application model instead of the GUI, i.e. handleEvent()'s * or action()'s traditional switch statement. Thus * readibility and maintainability of complex dialogs are * greatly enhanced while capping the proliferation of * derived classes for with different components. Both * destroy() and delegated events can be isolated in * separate threads from the toolkit callback thread, * e.g. blocking handlers won't crash the GUI manager. * * @see extensions.awt.DialogExtended#handleEvent * @see extensions.awt.DialogExtended#action * @see extensions.awt.DialogExtended#destroy * @see extensions.awt.ActionProtocol * @see java.awt.Dialog * @version 1.0.2 * @author John Webster Small */ public class DialogExtended extends Dialog { // Work around for JDK 1.0.2's win32 modal dialog bug. private Semaphore modalBlock; /** A derived class that overrides destroy() may * need to set this value to true in its constructor. * @see #destroy */ protected boolean destroyMayBlock = false; /** Indicate whether or not the window should self * dispose upon window destroy. */ public boolean disposeOnDestroy = true; /** * Construct an invisible DialogExtended with * no title. * @param parent the owner of the dialog * @param modal if true, dialog blocks input to * all other windows * @see java.awt.Component#resize * @see java.awt.Component#show */ public DialogExtended(Frame parent, boolean modal) { super(parent,modal); } /** * Construct an invisible DialogExtended with title. * @param parent the owner of the dialog * @param title the title of the dialog * @param modal if true, dialog blocks input to all * other windows * @see java.awt.Component#resize * @see java.awt.Component#show */ public DialogExtended (Frame parent, String title, boolean modal) { super(parent,title,modal); } /** * Construct and show an Exception/Error dialog. * @param parent parent frame if this dialog * @param t exception/error * @param stackTrace display a stack trace */ public DialogExtended(Frame parent, Throwable t, boolean stackTrace) { super(parent,"Exception/Error",true); add("North",new Label (t.getClass().getName()+": "+t.getMessage())); if (stackTrace) { ByteArrayOutputStream tout = new ByteArrayOutputStream(); t.printStackTrace(new PrintStream(tout)); TextArea ta = new TextArea(tout.toString(0)); ta.setEditable(false); add("Center",ta); } Panel p = new Panel(); p.setLayout(new FlowLayout(FlowLayout.CENTER,15,15)); p.add(new ButtonAction("Ok",destroyAdapter())); add("South",p); pack(); show(); } /** * Construct and show an Exception/Error dialog. * @param parent parent frame if this dialog * @param t exception/error */ public DialogExtended(Frame parent, Throwable t) { this(parent,t,false); } /** Implements a universal observer interface. * @param anySubject event originating object * @param what user defined argument * @return returns true if update acknowledged */ public boolean update(Object anySubject, Object what) { return false; } /** Called if an action event is triggered by any * component within the dialog. Any component * implementing ActionProtocol is notified directly * via their action() method. * For example: *
*
* import java.awt.*;
* import extensions.awt.*;
*
* class ActionHandlerDialog extends DialogExtended
* {
* ActionHandlerDialog(Frame parent)
* {
* super(parent,"Do It!",true);
* add("Center",new ButtonAction("Done",destroyAdapter()));
* pack();
* }
* }
*
* public class MenuAction_Demo extends FrameExtended
* {
* public MenuAction_Demo()
* {
* super("MenuItemAction Demo");
* ActionHandlerDialog ahd = new ActionHandlerDialog(this);
* MenuBar mb = new MenuBar();
* Menu actions = new Menu("Actions");
* actions.add(new MenuItemAction("Do It!",ahd.showAdapter()));
* actions.add(new MenuItemAction("Quit",destroyAdapter()));
* mb.add(actions);
* setMenuBar(mb);
* }
*
* public static void main(String[] args)
* { winMain("MenuAction_Demo",250,200); }
* }
*
*
* Notice that in the above example that neither the dialog's
* handleEvent() or action() methods had to be overridden
* to process the button clicked event! This mechanism is
* known as delegation, allowing the event handler to be
* encapsulated within the application model instead of the
* GUI, i.e. the switch statement of an overridden handeEvent()
* or action() method. Using delegation, the readibility
* and maintainability of a complex dialog is greatly enhanced
* while capping the proliferation of derived classes simply
* to handle different events.
*
* @param evt the event that caused the action
* @param what the action
* @return true if no further processing is required
* @see java.awt.Component#action
* @see ActionProtocol
* @see MenuItemAction
* @see ButtonAction
* @see FrameExtended
*/
public boolean action(Event evt, Object what)
{
if (evt.target instanceof ActionProtocol) {
((ActionProtocol)evt.target).action(((Component)this),evt,what);
return true;
}
return super.action(evt,what);
}
/**
* Process a WINDOW_DESTROY event by redirecting a call to
* destroy().
*
* @param evt the event
* @return if true event has been handled completely
* requiring no further action
* @see #destroy
* @see java.awt.Component#handleEvent
*/
public boolean handleEvent(Event evt)
{
if (evt.id == Event.WINDOW_DESTROY) {
if (destroyMayBlock)
new DialogExtendedDestroyThread(this);
else
destroy();
return true;
}
return super.handleEvent(evt);
}
/** Show Dialog.
* Work around for JDK 1.0.2 Dialog show modal bug under win32.
* Do not call from action() or handleEvent() of another
* frame or dialog if this is a modal dialog. Instead you
* must place the call to this show() in a separate thread
* from the calling action() or handleEvent() method. This
* is not necessary for non win32 platforms or JDK later than
* 1.0.2 when the bug is fixed. However this system is
* transparent and forward compatible when the bug is
* fixed in later versions of JDK. See AWTModalBug.html
* elsewhere.
* @see java.awt.Dialog#show
* @see #showAdapter
* @see extensions.awt.Action
*/
public void show()
{
if (isModal())
modalBlock = new Semaphore(0);
else
modalBlock = new Semaphore(1);
super.show();
modalBlock.pass();
if (isModal())
((Frame)getParent()).requestFocus(); //toFront();
}
/** Overridden for NT bug fixup. */
public Dimension preferredSize()
{
if (!isValid())
validate();
Dimension d = super.preferredSize();
int min = getFontMetrics(getFont()).stringWidth(getTitle())+30;
if (d.width < min)
d.width = min;
if (getLayout() instanceof BorderLayout
&& SystemProperties.jvm_major == 1
&& SystemProperties.jvm_minor.equals("0")
&& SystemProperties.os == SystemProperties.WinNT) {
// fix for JDK 1.0.2 NT GUI threads screw up
d.width += 20;
d.height += 30;
}
return d;
}
/**
* Provide a show adapter for this DialogExtended
* instance. Use to construct ButtonAction and
* MenuItemAction instances.
* @see #show
*/
public ActionProtocol showAdapter()
{ return new DialogExtendedShowAdapter(this); }
/**
* Called automatically from handleEvent() upon
* receiving a WINDOW_DESTROY event. Override to
* process WINDOW_DESTROY events. Besure to
* call super.destroy() as the last statement
* of your overriding method.
* If your destroy() may block you must set
* destroyMayBlock to true in your constructor!
* @see #handleEvent
*/
public void destroy()
{
hide();
if (isModal())
modalBlock.signal();
if (disposeOnDestroy)
dispose();
}
/** Causes the focus to shift to the overloaded
* requestFocus() indicated component.
* @see java.awt.Window#toFront
*/
public void toFront()
{
super.toFront();
requestFocus();
}
/**
* Provide destroy adapter for this DialogExtended
* instance. Use to construct "Quit" ButtonAction
* instances.
* @see #action
* @see #destroy
*/
public ActionProtocol destroyAdapter()
{ return new DialogExtendedDestroyAdapter(this); }
}