package extensions.awt; import java.awt.*; import java.util.*; import extensions.util.*; // Allow ButtonAction and MenuItemAction of other // FrameExtended and DialogExtended to show this // FrameExtended instance. class FrameExtendedShowAdapter implements ActionProtocol { private FrameExtended fe; FrameExtendedShowAdapter(FrameExtended fe) { this.fe = fe; } public final void action (Component forward, Event evt, Object what) { fe.show(); } } // Allow for "Quit" MenuItemAction and ButtonAction instances. class FrameExtendedDestroyAdapter implements ActionProtocol, Runnable { private FrameExtended fe; FrameExtendedDestroyAdapter(FrameExtended fe) { this.fe = fe; } public final void action (Component forward, Event evt, Object what) { if (fe.destroyMayBlock) (new Thread(this,"FrameExtendedDestroyAdapter")).start(); else run(); } public final void run() { fe.destroy(); } } /* Isolates destroy() in separate thread from toolkit callback. */ class FrameExtendedDestroyThread extends Thread { FrameExtended fe; FrameExtendedDestroyThread(FrameExtended fe) { this.fe = fe; start(); } public final void run() { fe.destroy(); } } /* Present a picklist of frames to switch to. */ class FrameExtendedPickFrameAdapter extends Action { public void run() { FrameExtended.pickFrame((FrameExtended)forward); } } /* Cascade all frames appearing on the screen. */ class FrameExtendedCascadeFramesAdapter implements ActionProtocol { public void action(Component forward, Event evt, Object what) { FrameExtended.cascadeFrames((FrameExtended)forward); } } /* Destroy all frames. */ class FrameExtendedDestroyFramesAdapter extends Action { public void run() { FrameExtended.destroyFrames(); } } /** * 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 frames 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 FrameExtended#handleEvent * @see FrameExtended#action * @see FrameExtended#destroyMayBlock * @see FrameExtended#destroy * @see extensions.awt.ActionProtocol * @see java.awt.Frame * @version 1.0.2 * @author John Webster Small */ public class FrameExtended extends Frame { static Instances instances = new Instances(); static Stack frameStack = new Stack(); static final int CASCADE = 20; static void pickFrame(FrameExtended currentFE) { Hashtable efd = new Hashtable(); Enumeration frames = instances.instances(); FrameExtended fe; while (frames.hasMoreElements()) { fe = (FrameExtended) frames.nextElement(); efd.put(((fe == currentFE)?" *":" ")+fe.getTitle(),fe); } PickListDialog pld = new PickListDialog (currentFE,"Pick ",efd); pld.show(); frames = pld.getSelections().elements(); if (pld.getButtonClickedIdx() == 0) { if (frames.hasMoreElements()) { fe = (FrameExtended) frames.nextElement(); fe.toFront(); } } } static void cascadeFrames(FrameExtended currentFE) { Enumeration frames = instances.sibblings(currentFE); int i; FrameExtended fe; Dimension d; Insets dd; for (i = CASCADE; frames.hasMoreElements(); i+=CASCADE) { fe = (FrameExtended)frames.nextElement(); d = fe.preferredSize(); dd = fe.insets(); fe.reshape (i,i,d.width+dd.left+dd.right,d.height+dd.top+dd.bottom); fe.toFront(); } d = currentFE.preferredSize(); dd = currentFE.insets(); currentFE.reshape (i,i,d.width+dd.left+dd.right,d.height+dd.top+dd.bottom); currentFE.toFront(); } static void destroyFrames() { Enumeration frames = instances.instances(); while (frames.hasMoreElements()) ((FrameExtended) frames.nextElement()).destroy(); } /** 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; /** Indicate whether or not the JVM should exit * upon window destroy. */ public boolean exitOnDestroy = false; /** Indicate whether or not the JVM should exit * upon all window instances having been destroyed. */ public static boolean exitOnDestroyAll = false; /** Indicate exit status either by setting this * value or overriding exitStatus(). * @see #exitStatus */ protected int exit_status = 0; /** Backtrack through parents until enclosing frame * is found. This function is used primarily to launch * dialogs. * @param c component for which it frame parent is desired * @return returns c if it is an instance of frame or * backtracks across parents until enclosing frame is * found returning that frame. If the component is not * a frame or is not contained in a frame ultimately * null is returned. */ public static Frame getParentFrame(Component c) { do { if (c instanceof Frame) break; } while ((c = c.getParent()) != null); return (Frame) c; } /** Set title of frame, numbering the windows. * @param title title of frame prefix with * number ID of this frame */ public void setTitle(String title) { super.setTitle(String.valueOf(instances.getID(this))+" "+title); } /** Construct a FrameExtended with no title. */ public FrameExtended() { instances.getID(this); } /** Construct a FrameExtended with title. * @param title title of frame */ public FrameExtended(String title) { super(); instances.getID(this); setTitle(title); } /** * Construct and show an Exception/Error frame. * Never call with exit_status non zero in an applet! * @param t exception/error * @param exit_status if zero dispose upon closing, * otherwise call System.exit(exit_status); * @param stackTrace display a stack trace */ FrameExtended(Throwable t, int exit_status, boolean stackTrace) { super(); instances.getID(this); setTitle("Exception/Error"); if (exit_status != 0) { this.exit_status = exit_status; exitOnDestroy = true; } add("Center",new Label (t.getClass().getName()+": "+t.getMessage())); pack(); show(); new DialogExtended(this,t,stackTrace); destroy(); } /** * Construct and show an Exception/Error frame. * Never call with exit_status non zero in an applet! * @param t exception/error * @param exit_status if zero dispose upon closing, * otherwise call System.exit(exit_status); */ FrameExtended(Throwable t, int exit_status) { this(t,exit_status,false); } /** * Construct and show an Exception/Error frame. * @param t exception/error */ public FrameExtended(Throwable t) { this(t,0); } /** 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 frame. 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 frame's * handleEvent() or action() methods had to be overridden * to process the menu item 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 frame 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 DialogExtended */ 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 FrameExtendedDestroyThread(this); else destroy(); return true; } return super.handleEvent(evt); } /** * Provide show adapter for this FrameExtended * instance. Use to construct ButtonAction, * MenuItemAction, and other action trigger instances. * @see java.awt.Frame#show */ public ActionProtocol showAdapter() { return new FrameExtendedShowAdapter(this); } /** * If the FrameExtended's exitOnDestroy is true, * this function returns exit_status. Override this * method to indicate a status other than exit_status, * i.e. System.exit(exitStatus()). * * @return appropriate status code * @see #exit_status */ public int exitStatus() { return exit_status; } /** @see java.awt.Window#dispose */ public void dispose() { instances.dispose(this); frameStack.removeElement(this); super.dispose(); } protected void finalize() throws Throwable { if (isShowing()) hide(); instances.dispose(this); frameStack.removeElement(this); super.finalize(); } /** * 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 #disposeOnDestroy * @see #handleEvent * @see #exitStatus * @see #exit_status */ public void destroy() { if (isShowing()) hide(); if (exitOnDestroy) System.exit(exitStatus()); if (disposeOnDestroy) dispose(); if (!frameStack.empty()) ((FrameExtended)frameStack.pop()).toFront(); else if (instances.size() > 0) ((FrameExtended)instances.instances().nextElement()).toFront(); else if (exitOnDestroyAll) System.exit(0); } /** Causes the focus to shift to the overloaded * requestFocus() indicated component. * @see java.awt.Window#toFront */ public void toFront() { if (!isShowing()) show(); super.toFront(); requestFocus(); } /** Causes the focus to shift to the overloaded * requestFocus() indicated component whenever * the frame is enabled. * @see java.awt.Component#enable */ public void enable(boolean cond) { super.enable(cond); if (cond) requestFocus(); } /** Causes the focus to shift to the overloaded * requestFocus() indicated component whenever * the frame is resized. * @see java.awt.Component#resize */ public void resize(int width, int height) { super.resize(width,height); requestFocus(); } /** Causes the focus to shift to the overloaded * requestFocus() indicated component whenever * the frame is resized. * @see java.awt.Component#resize */ public void resize(Dimension d) { super.resize(d); requestFocus(); } /** Causes the focus to shift to the overloaded * requestFocus() indicated component whenever * the frame is reshaped. * @see java.awt.Component#reshape */ public void reshape(int x, int y, int width, int height) { super.reshape(x,y,width,height); requestFocus(); } /** * Provide destroy adapter for this FrameExtended * instance. Use to construct "Quit" MenuItemAction * and ButtonAction instances. * @see #action * @see #destroy */ public ActionProtocol destroyAdapter() { return new FrameExtendedDestroyAdapter(this); } /** Provide a pick frame adapter to use to construct * "List Frames" MenuItemAction. */ public static ActionProtocol pickFrameAdapter() { return new FrameExtendedPickFrameAdapter(); } /** Provide a cascade frames adapter to use to construct * "Cascade Frames" MenuItemAction. */ public static ActionProtocol cascadeFramesAdapter() { return new FrameExtendedCascadeFramesAdapter(); } /** Provide a destroy frames adapter to use to construct * "Exit All" MenuItemAction. */ public static ActionProtocol destroyFramesAdapter() { return new FrameExtendedDestroyFramesAdapter(); } }