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