001: package net.xoetrope.awt;
002:
003: import java.applet.Applet;
004: import java.lang.reflect.Method;
005: import java.net.URL;
006:
007: import java.awt.BorderLayout;
008: import java.awt.Component;
009: import java.awt.Container;
010: import java.awt.Dimension;
011: import java.awt.Frame;
012: import java.awt.MenuBar;
013: import java.awt.Toolkit;
014: import java.awt.Window;
015: import java.awt.event.WindowEvent;
016: import java.awt.event.WindowListener;
017:
018: import net.xoetrope.data.XDataSource;
019: import net.xoetrope.debug.DebugLogger;
020: import net.xoetrope.xui.XComponentConstructor;
021: import net.xoetrope.xui.XComponentFactory;
022: import net.xoetrope.xui.XLifeCycleListener;
023: import net.xoetrope.xui.XPage;
024: import net.xoetrope.xui.XPageDisplay;
025: import net.xoetrope.xui.XPageLoader;
026: import net.xoetrope.xui.XPageManager;
027: import net.xoetrope.xui.XProject;
028: import net.xoetrope.xui.XProjectManager;
029: import net.xoetrope.xui.XResourceManager;
030: import net.xoetrope.xui.XTarget;
031: import net.xoetrope.xui.build.BuildProperties;
032: import net.xoetrope.xui.style.XStyleManager;
033:
034: /**
035: * <p>This class is constructed with a window or frame
036: * and can be part of an applet or an application. The class acts as the main
037: * entry point to an XUI application and provides some of the infrastructure
038: * needed to support the application.</p>
039: * <p>The applet can provide support for a frameset or a single page. Page display
040: * functions are also supported to allow the application to display more than a
041: * single page or change the page that is displayed.</p>
042: * <p>By choosing either the AWT or Swing version of the XApplet you choose to
043: * have either an AWT or a Swing application/applet. In general once this choice
044: * has been made you should not mix toolkits.</p>
045: * <p>Copyright (c) Xoetrope Ltd., 1998-2004<br>
046: * License: see license.txt
047: * @version $Revision: 1.29 $
048: */
049: public class XApplet extends Applet implements WindowListener,
050: XPageDisplay {
051: protected XProject project;
052: protected static Window appWindow;
053: protected static Frame clientFrame;
054: protected XPageManager pageMgr;
055: protected boolean bUseWindow;
056: protected XDataSource modelDataSource;
057: protected static Class defaultSourceClass = net.xoetrope.data.XDataSource.class;
058:
059: protected int clientWidth = 800;
060: protected int clientHeight = 600;
061:
062: /**
063: * main method to be invoked as an application. This method is invoked as the
064: * entry point to the 'Application', it is not used if an Applet is being
065: * launched. This method establishes the frame within which the application
066: * runs. If overloading this method remeber to call the setup method.
067: */
068: public static void main(String args[]) {
069: Frame frame = new Frame();
070: XApplet applet = new XApplet();
071: frame.add(applet);
072: applet.setup(frame, args);
073: }
074:
075: /**
076: * A default constructor. Most of the setup work is actually done by the initialize
077: * method and is called by the main method or the init method depending on
078: * whether or not an application of applet is being launched.
079: */
080: public XApplet() {
081: }
082:
083: /**
084: * Setup the applet by setting paths and then initializing the applet. This
085: * method is a stand-in for the main method, processing the commandline
086: * parameters, so that its work is reusable and
087: * does not needed to be redone by derived classes. This method should not be
088: * called directly by user code.
089: * @param frame the owner frame
090: * @param args the command line arguments
091: */
092: protected void setup(Frame frame, String[] args) {
093: setResourceFile(args.length > 0 ? (String) args[0]
094: : "startup.properties");
095:
096: String classPackageName = args.length > 1 ? args[1]
097: : XPage.XUI_AWT_PACKAGE;
098: XResourceManager.setPackageName(classPackageName);
099:
100: XPageLoader pageLoader = getSecondaryClassLoader(classPackageName);
101: if (pageLoader != null)
102: XProjectManager.getPageManager().setSecondaryLoader(
103: pageLoader);
104:
105: String icon = XProjectManager.getCurrentProject()
106: .getStartupParam("Icon");
107: if (icon != null)
108: frame.setIconImage(XProjectManager.getResourceManager()
109: .getImage(icon));
110:
111: initialise(frame);
112: }
113:
114: /**
115: * <p>Construct a new builder and set the default package. XUI sometimes uses
116: * additional class loaders to find the resources needed in a project. By default
117: * XUI uses the XuiBuilder class loader to convert XML files to Java classes.</p>
118: * <p>A custom class loader can be referenced in the startup properties file
119: * using the 'BuilderClass' property. Once this property has been determined
120: * this method will instantiate an instance of that class if necessary.
121: * @param packageName the name of the default widget package e.g. net.xoetrope.awt,
122: * this is normally defined as a result of choosing the appropriate version of
123: * the XApplet class
124: */
125: protected XPageLoader getSecondaryClassLoader(String packageName) {
126: String builderClass = XProjectManager.getCurrentProject()
127: .getStartupParam("BuilderClass");
128: if (builderClass != null) {
129: try {
130: XPageLoader pageLoader = (XPageLoader) Class.forName(
131: builderClass).newInstance();
132: pageLoader.setPackageName(packageName);
133: return pageLoader;
134: } catch (Exception ex) {
135: ex.printStackTrace();
136: DebugLogger.logError("Unable to load builder class: "
137: + ex.getMessage());
138: }
139: }
140:
141: try {
142: XPageLoader pageLoader = (XPageLoader) Class.forName(
143: "net.xoetrope.builder.XuiBuilder").newInstance();
144: pageLoader.setPackageName(packageName);
145: return pageLoader;
146: } catch (Exception ex) {
147: DebugLogger.logError("Unable to load XuiBuilder class:"
148: + ex.getMessage());
149: }
150: return null;
151: }
152:
153: /**
154: * Sets the default datasource class. The default data source will be used to
155: * provide any initial data for the XModel. Normally this data is static data
156: * that will be used to populate things like lists and provide default values.
157: * @param className the name of the datasource class
158: * e.g. net.xoetrope.data.XDataSource.class, this class reads data from an XML file
159: */
160: public static void setDefaultDataSource(String className) {
161: try {
162: defaultSourceClass = Class.forName(className);
163: } catch (ClassNotFoundException ex) {
164: }
165: }
166:
167: /**
168: * Setup the default resource file for the application and load some of the
169: * information in it. The resource file is the startup properties file
170: * @param startFile The name of the file to be loaded, by default startup.properties
171: */
172: protected void setResourceFile(String startFile) {
173: project = XProjectManager.getCurrentProject();
174: project.initialise(startFile);
175:
176: try {
177: String sUseWindow = project.getStartupParam("UseWindow");
178: bUseWindow = sUseWindow.compareTo("true") == 0 ? true
179: : false;
180: String temp = project.getStartupParam("ClientWidth");
181: if (temp != null)
182: clientWidth = Integer.parseInt(temp);
183: temp = project.getStartupParam("ClientHeight");
184: if (temp != null)
185: clientHeight = Integer.parseInt(temp);
186:
187: // Check for subclassing of the datasource/model
188: temp = project.getStartupParam("XDataSourceClass");
189: if (temp != null) {
190: try {
191: defaultSourceClass = Class.forName(temp);
192: } catch (Exception ex) {
193: }
194: }
195: } catch (Exception ex) {
196: }
197: }
198:
199: /**
200: * Invoked when used as an applet. Sets up the startup file and initialises
201: * the application. Reads the applet parameters and calls initialize.
202: */
203: public void init() {
204: setResourceFile(getParameter("StartFile"));
205: String classPackageName = getParameter("StartPackage");
206: XResourceManager.setPackageName(classPackageName);
207: XProjectManager.getPageManager().setSecondaryLoader(
208: getSecondaryClassLoader(classPackageName));
209: initialise(getFrame());
210: }
211:
212: /**
213: * Gets the Frame containing the applet.
214: * @return Frame which is the applet or application's parent
215: */
216: public Frame getFrame() {
217: Container parent;
218: for (parent = getParent(); parent != null; parent = parent
219: .getParent()) {
220: if ((parent != null) && (parent instanceof Frame))
221: return (Frame) parent;
222: }
223: return clientFrame;
224: }
225:
226: /**
227: * <p>Generic function which is called from the constructor if it's an application
228: * or from start if its an applet.</p>
229: * The initialization process proceeds in the following order<br>
230: * <ol>
231: * <li>Register the component factories</li>
232: * <li>Setup the project class</li>
233: * <li>Setup the page manager</li>
234: * <li>Setup the resource manager</li>
235: * <li>Setup the style manager</li>
236: * <li>Size the main window</li>
237: * <li>Set the layout</li>
238: * <li>Add a shutdown hook</li>
239: * <li>Display the main window</li>
240: * </ol>
241: * @param f The Frame which acts as the parent.
242: * @param skipReset Lets the application know whether to skip resetting
243: * the style manager
244: */
245: protected void initialise(Frame f) {
246: register();
247:
248: project = XProjectManager.getCurrentProject();
249:
250: clientFrame = f;
251: XProjectManager.getPageManager();
252: XProjectManager.getResourceManager();
253: try {
254: URL db = getDocumentBase();
255: project.getResourceManager().setDocumentBase(db);
256: } catch (Exception e) {
257: }
258:
259: XStyleManager styleMgr = XProjectManager.getStyleManager();
260:
261: if (f != null)
262: f.setSize(clientWidth, clientHeight);
263: setSize(new Dimension(clientWidth, clientHeight));
264:
265: appWindow = null;
266: if (bUseWindow) {
267: appWindow = new Window(f);
268: appWindow.setSize(clientWidth, clientHeight);
269: appWindow.addWindowListener(this );
270: f.addWindowListener(this );
271: } else {
272: if (f != null)
273: appWindow = f;
274: f.addWindowListener(this );
275: }
276:
277: setLayout(new BorderLayout());
278: XResourceManager.setApplet(this );
279: XResourceManager.setAppFrame(clientFrame);
280: XResourceManager.setAppWindow(appWindow);
281:
282: // Try to create the shutdown hook, this is not supported on all VMss
283: try {
284: Object shutdownHook = Class.forName(
285: "net.xoetrope.xui.build.conditional.ShutdownHook")
286: .newInstance();
287:
288: // Try to register a listener for the startup and shutdown events.
289: String lifeCycleObjectName = project
290: .getStartupParam("LifeCycleListener");
291: if ((lifeCycleObjectName != null)
292: && (lifeCycleObjectName.length() > 0)) {
293: XLifeCycleListener lifeCycleObject = (XLifeCycleListener) Class
294: .forName(lifeCycleObjectName).newInstance();
295: lifeCycleObject.initialize();
296:
297: Class params[] = new Class[1];
298: Object args[] = new Object[1];
299: params[0] = XLifeCycleListener.class;
300: args[0] = lifeCycleObject;
301: Method method = shutdownHook.getClass().getMethod(
302: "addLifeCycleListener", params);
303: method.invoke(shutdownHook, args);
304: }
305: } catch (Exception ex) {
306: ex.printStackTrace();
307: }
308:
309: Dimension screenSize = Toolkit.getDefaultToolkit()
310: .getScreenSize();
311: if (bUseWindow) {
312: Dimension frameSize = appWindow.getSize();
313: appWindow.setLocation(
314: (screenSize.width - frameSize.width) / 2,
315: (screenSize.height - frameSize.height) / 2);
316: appWindow.setVisible(true);
317: f.setLocation(screenSize.width / 2, screenSize.height / 2);
318: f.setSize(0, 0);
319: f.setVisible(true);
320: } else if (f != null) {
321: Dimension frameSize = f.getSize();
322: String center = project.getStartupParam("CenterWin");
323: if ((center != null) && (center.compareTo("true") == 0))
324: f.setLocation((screenSize.width - frameSize.width) / 2,
325: (screenSize.height - frameSize.height) / 2);
326: f.setVisible(true);
327: }
328:
329: setContent(f);
330: if (appWindow != null) {
331: appWindow.invalidate();
332: appWindow.validate();
333: appWindow.repaint();
334: }
335: }
336:
337: /**
338: * Load the componentFactories by reading the factory names from the startup
339: * file. The value of the 'NumComponentFactories' is first read and then
340: * the value of ComponentFactory<X> (i.e. ComponentFactory0, ComponentFactory1)
341: * where <X> is the number, starting at zero, of the factory. Each factory is
342: * then instantiated and will be called upon to construct components in the
343: * order in which the factories were registered.
344: */
345: protected void register() {
346: int numFactories = 0;
347: String numFactoriesStr = XProjectManager.getCurrentProject()
348: .getStartupParam("NumComponentFactories");
349:
350: if (numFactoriesStr != null)
351: numFactories = new Integer(numFactoriesStr).intValue();
352:
353: for (int i = 0; i < numFactories; i++) {
354: String factoryName = XProjectManager.getCurrentProject()
355: .getStartupParam("ComponentFactory" + i);
356: try {
357: if (factoryName != null)
358: XComponentFactory
359: .registerComponentFactory((XComponentConstructor) Class
360: .forName(factoryName).newInstance());
361: } catch (Exception ex) {
362: if (BuildProperties.DEBUG)
363: DebugLogger.logError("Factory not loaded: "
364: + ex.getMessage());
365: }
366: }
367: }
368:
369: /**
370: * Load the content into the model. In the process an instance of the default
371: * model data source class is instantiated and reads data from a file pointed
372: * to by the 'ModelData' startup parameter.
373: * @param f the frame
374: */
375: protected void setContent(Frame f) {
376: boolean sourceExists = false;
377: try {
378: modelDataSource = (XDataSource) defaultSourceClass
379: .newInstance();
380: } catch (Exception ex2) {
381: return;
382: }
383:
384: try {
385: String fileName = project.getStartupParam("ModelData");
386: if (fileName != null) {
387: try {
388: modelDataSource.read(project.getResourceManager()
389: .getBufferedReader(fileName, null));
390: } catch (Exception ex3) {
391: if (BuildProperties.DEBUG)
392: DebugLogger.logError("Could not access file:"
393: + fileName);
394: }
395: }
396: } catch (Exception ex) {
397: if (BuildProperties.DEBUG)
398: DebugLogger.logError("Exception in setContent");
399: }
400: try {
401: if (f != null)
402: f.setTitle(project.getStartupParam("Title"));
403: } catch (Exception ex1) {
404: ex1.printStackTrace();
405: }
406: setHome();
407: }
408:
409: /**
410: * Set the home page using the startup properties. The home page is established
411: * by combining the two startup properties 'StartPackage' and 'StartClass'. By
412: * default 'StartClass' is set to a value of 'home. Both startup parameters
413: * are optional. Loads the first page for the application.
414: */
415: public void setHome() {
416: try {
417: String packageName = project
418: .getStartupParam("StartPackage");
419: String homePage = project.getStartupParam("StartClass");
420: if ((packageName == null) && (homePage == null))
421: homePage = "home";
422: if (packageName != null)
423: packageName += ".";
424:
425: pageMgr = XProjectManager.getPageManager();
426: pageMgr.setPackageName(packageName);
427: pageMgr.setPageDisplay(this );
428:
429: if (homePage != null)
430: pageMgr.showPage(homePage);
431: } catch (Exception ex) {
432: ex.printStackTrace();
433: }
434: }
435:
436: /**
437: * Called when a page has been added or shown via the XPageManager. The page manager
438: * then requests that the applet/application then displays the page in the
439: * appropriate location.
440: * @param page The XPage which has been loaded.
441: * @return the page being displayed
442: */
443: public XPage displayPage(XPage page) {
444: return displayPage(page, null);
445: }
446:
447: /**
448: * <p>Called when a page has been added or shown via the XPageManager. The page manager
449: * then requests that the applet/application then displays the page in the
450: * appropriate location.</p>
451: * <p>
452: * The sequence with which the page transition occurs is as follows:
453: * </p>
454: * <li>Find the appropriate target area</li>
455: * <li>Ask the page to make its components non visible (to avoid flicker during the page update)</li>
456: * <li>Set the page size</li>
457: * <li>Save the current page's data by calling 'saveBoundComponentValues</li>
458: * <li>Mark the current page as deactivated, and call the page's deactivated() method</li>
459: * <li>Remove the old page from the container</li>
460: * <li>Add the new page</li>
461: * <li>Update the new page's bindings by calling updateBindings()</li>
462: * <li>Update the new page's data by calling updateBoundComponentValues()</li>
463: * <li>Layout the container and request it to repaint</li>
464: * <li>Show the new page</li>
465: * <li>Mark the new page as activated, and call its pageActivated method</li>
466: * <ol>
467: * </ol>
468: * @param page The XPage which has been loaded.
469: * @param target the area to update
470: * @return the page being displayed
471: */
472: public XPage displayPage(XPage page, String target) {
473: Container container = bUseWindow ? (Container) appWindow
474: : (Container) this ;
475: Container targetContainer = findTarget(target);
476: page.showComponents(page, false, 0);
477:
478: page.setSize(new Dimension(clientWidth, clientHeight));
479:
480: XPage lastPage = null;
481: try {
482: Component c = targetContainer.getComponent(0);
483: if (!(c instanceof XPage)) // The first component might be a tooltip
484: targetContainer.remove(0);
485: lastPage = (XPage) targetContainer.getComponent(0);
486: } catch (Exception e) {
487: }
488:
489: if (page != lastPage) {
490: if (lastPage != null) {
491: lastPage.saveBoundComponentValues();
492: lastPage.setStatus(XPage.DEACTIVATED);
493:
494: try {
495: lastPage.pageDeactivated();
496: } catch (Exception ex) {
497: if (BuildProperties.DEBUG)
498: DebugLogger
499: .logError("The pageDeactivated method could not be invoked due to an exception");
500: }
501: targetContainer.remove(lastPage);
502: targetContainer.doLayout();
503: }
504:
505: targetContainer.add(page, BorderLayout.CENTER);
506: page.updateBindings();
507: page.updateBoundComponentValues();
508:
509: targetContainer.doLayout();
510: page.doLayout();
511: } else {
512: page.updateBindings();
513: page.updateBoundComponentValues();
514: }
515:
516: targetContainer.doLayout();
517:
518: page.setVisible(true);
519: page.showComponents(page, true, 0);
520: page.repaint();
521:
522: page.doLayout();
523: page.setStatus(XPage.ACTIVATED);
524: try {
525: page.pageActivated();
526: } catch (Exception ex) {
527: if (BuildProperties.DEBUG) {
528: DebugLogger
529: .logError("The pageActivated method could not be invoked due to an exception");
530: ex.printStackTrace();
531: }
532: }
533:
534: return page;
535: }
536:
537: /**
538: * Remove the page from container. The page is simply removed and the container validated.
539: * @param page
540: */
541: public void hidePage(XPage page) {
542: XPage lastPage = null;
543: Container container = bUseWindow ? (Container) appWindow
544: : (Container) this ;
545:
546: try {
547: Component c = container.getComponent(0);
548: if (!(c instanceof XPage)) // The first component might be a tooltip
549: container.remove(0);
550: lastPage = (XPage) container.getComponent(0);
551:
552: if ((lastPage != null) && (page != lastPage)) {
553: container.remove(lastPage);
554:
555: page.setStatus(XPage.DEACTIVATED);
556: page.pageDeactivated();
557:
558: container.doLayout();
559: validate();
560: }
561: } catch (Exception e) {
562: if (BuildProperties.DEBUG)
563: DebugLogger
564: .logError("The page could not be hidden due to an exception: "
565: + e.getMessage());
566: }
567: }
568:
569: /**
570: * Finds the target area for a page display request, this only applies to a
571: * frameset. If there is no frameset the main area or default area is named
572: * 'content' and this area is used if null is used as the target name.
573: * @param target the target area name
574: * @return the container to be updated
575: */
576: public Container findTarget(String target) {
577: Container targetContainer = bUseWindow ? (Container) appWindow
578: : (Container) this ;
579: if (target == null)
580: target = "content";
581:
582: Component[] comps = targetContainer.getComponents();
583: int numChildren = comps.length;
584: for (int i = 0; i < numChildren; i++) {
585: if (comps[i] instanceof XToolTip) {
586: // On rare occassions a tooltip can be left over if it is not hidden
587: // when a page transition or a popup occurs.
588: ((XToolTip) comps[i]).removeToolTip();
589: numChildren--;
590: i--;
591: } else if (comps[i].getName().compareTo(target) == 0)
592: return (XTarget) comps[i];
593: }
594: return targetContainer;
595: }
596:
597: /**
598: * Get the target for a page display request. The target areas are stored in
599: * the order in which they were declared or added.
600: * @param idx the target area
601: * @return the container to be updated
602: */
603: public Container getTarget(int idx) {
604: Container targetContainer = bUseWindow ? (Container) appWindow
605: : (Container) this ;
606: Component[] comps = targetContainer.getComponents();
607: int numChildren = comps.length;
608: return (XTarget) comps[idx];
609: }
610:
611: /**
612: * Get the number of target areas in the container
613: */
614: public int getNumTargets() {
615: Container targetContainer = bUseWindow ? (Container) appWindow
616: : (Container) this ;
617: return targetContainer.getComponentCount();
618: }
619:
620: /**
621: * Add a new frame or target area to a frameset
622: * @param name the frame name
623: * @param constraint the BorderlayoutConstraint
624: * @param preferredWidth the preferred width
625: * @param preferredHeight the preferred height
626: */
627: public void addTarget(String name, Object constraint,
628: int preferredWidth, int preferredHeight) {
629: Container root = bUseWindow ? (Container) appWindow
630: : (Container) this ;
631: XTarget container = new XTarget(name, preferredWidth,
632: preferredHeight);
633: root.add(container, constraint);
634: }
635:
636: /**
637: * Adds a new frame or target area to the frameset
638: * @param frame the new traget frame
639: */
640: public void addTarget(XTarget frame, Object constraint) {
641: Container container = bUseWindow ? (Container) appWindow
642: : (Container) this ;
643: container.add(frame, constraint);
644: }
645:
646: /**
647: * Exit the application
648: */
649: public void windowClosing(WindowEvent e) {
650: System.exit(0);
651: }
652:
653: /**
654: * Get the width of the main window.
655: */
656: public int getClientWidth() {
657: return clientWidth;
658: }
659:
660: /**
661: * Get the height of the main window.
662: */
663: public int getClientHeight() {
664: return clientHeight;
665: }
666:
667: /**
668: * Set the menubar.
669: * @param mb
670: */
671: public static void setMenuBar(MenuBar mb) {
672: clientFrame.setMenuBar(mb);
673: }
674:
675: /**
676: * Get the menubar.
677: */
678: public static MenuBar getMenuBar() {
679: return clientFrame.getMenuBar();
680: }
681:
682: /**
683: * Template method, unused at present
684: */
685: public void windowActivated(WindowEvent e) {
686: }
687:
688: /**
689: * Template method, unused at present
690: */
691: public void windowClosed(WindowEvent e) {
692: }
693:
694: /**
695: * Template method, unused at present
696: */
697: public void windowDeiconified(WindowEvent e) {
698: }
699:
700: /**
701: * Template method, unused at present
702: */
703: public void windowDeactivated(WindowEvent e) {
704: }
705:
706: /**
707: * Template method, unused at present
708: */
709: public void windowIconified(WindowEvent e) {
710: }
711:
712: /**
713: * Template method, unused at present
714: */
715: public void windowOpened(WindowEvent e) {
716: }
717:
718: public URL getDocumentBase() {
719: try {
720: return super .getDocumentBase();
721: } catch (Exception e) {
722: }
723: return null;
724: }
725: }
|