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); } }