001: /*=============================================================================
002: * Copyright Texas Instruments, Inc., 2002. All Rights Reserved.
003: *
004: * This program is free software; you can redistribute it and/or modify
005: * it under the terms of the GNU General Public License as published by
006: * the Free Software Foundation; either version 2 of the License, or
007: * (at your option) any later version.
008: *
009: * This program is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012: * GNU General Public License for more details.
013: *
014: * You should have received a copy of the GNU General Public License
015: * along with this program; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: */
018:
019: package ti.chimera.plugin;
020:
021: import ti.exceptions.ProgrammingErrorException;
022: import ti.chimera.*;
023: import ti.chimera.registry.*;
024: import ti.chimera.service.*;
025:
026: import java.util.*;
027: import java.lang.ref.*;
028: import javax.swing.*;
029: import javax.swing.event.*;
030: import java.awt.*;
031: import java.awt.event.*;
032:
033: /**
034: * This plugin provides an implementation of the window manager mode. It
035: * provides an implementation of the {@link WindowMode} service for the
036: * window manager to use to activate/deactivate this mode. The rest of
037: * this plugin realizes the implementation of displaying dialogs, tool-
038: * bars, menubar entries, etc., on behalf of the window manager plugin.
039: * <p>
040: * This plugin is only half of the equasion as far as window management.
041: * The other half is the plugin that implements the "window manager"
042: * service, which is the "front end" that the rest of the system uses.
043: * <center><img src="WindowManagerArchitecture.png"></center>
044: * The devision is labor is that the this plugin responds to data written
045: * into the registry.
046: *
047: * @author Rob Clark
048: * @version 0.1
049: */
050: public class WindowModePlugin extends AbstractModePlugin {
051: /**
052: * Used to track WindowModeDialogImplementations so we can dispose 'em
053: * when leaving this mode
054: */
055: private WeakHashMap dialogImplementationTable = new WeakHashMap();
056: private NodeSubscriber haveMenuBarSubscriber;
057:
058: /**
059: * The position-map maps a window to the original outer edge position
060: * of that window. This is used so that windows can be moved back to
061: * their original position when whatever is encroaching on the windows
062: * recedes.
063: */
064: private Map xPositionMap = new WeakHashMap();
065: private Map yPositionMap = new WeakHashMap();
066: private Map widthPositionMap = new WeakHashMap();
067: private Map heightPositionMap = new WeakHashMap();
068:
069: /*=======================================================================*/
070: /**
071: * Class Constructor.
072: *
073: * @param main the main application
074: */
075: public WindowModePlugin(Main main) {
076: super (main, "Window Mode");
077:
078: try {
079: registry
080: .link(
081: new PersistentNode(MRJ ? Boolean.TRUE
082: : Boolean.FALSE,
083: NodeContract.BOOLEAN_CONTRACT,
084: "should each dialog have it's own menubar?"),
085: "/Preferences/Window Manager/Window Mode/Dialogs have Menu Bar");
086: } catch (RegistryException e) {
087: throw new ProgrammingErrorException(e);
088: }
089:
090: registerServiceFactory(new ServiceFactory() {
091:
092: public Service createService() {
093: return new AbstractWindowMode("window mode") {
094:
095: /**
096: * Called after mainWindow is created, but before subscribes in start()
097: */
098: protected void startHook() {
099: mainWindow
100: .setBounds(getDefaultMainWindowBounds());
101: xPositionMap.clear();
102: yPositionMap.clear();
103: widthPositionMap.clear();
104: heightPositionMap.clear();
105: }
106:
107: /**
108: * Called after unsubscribes in stop()
109: */
110: protected void stopHook() {
111: for (Iterator itr = getDialogImplementations()
112: .iterator(); itr.hasNext();)
113: ((WindowModeDialogImplementation) (itr
114: .next())).dispose();
115: }
116:
117: /**
118: * Called to realize a dialog
119: */
120: protected void createDialog(String name) {
121: new WindowModeDialogImplementation(name);
122: }
123:
124: };
125: }
126:
127: });
128: }
129:
130: /*=======================================================================*/
131: /**
132: * Get an collection of all dialog-implementations
133: */
134: private Collection getDialogImplementations() {
135: return dialogImplementationTable.keySet();
136: }
137:
138: /*=======================================================================*/
139: /**
140: * Get the appropriate default main-window size for this mode
141: */
142: protected Rectangle getDefaultMainWindowBounds() {
143: Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
144: Insets i = getScreenInsets();
145: Rectangle r = new Rectangle();
146:
147: r.x = i.left;
148: r.y = i.top;
149: r.width = d.width - i.left - i.right;
150: r.height = 0;
151:
152: return r;
153: }
154:
155: /*=======================================================================*/
156: /**
157: * Create the main-window, in which the menubar, toolbar, etc. are
158: * displayed.
159: *
160: * @return the main-window
161: * @see #diposeMainWindow
162: */
163: protected Component createMainWindow() {
164: final JWindow mainWindow = new JWindow() {
165: public void setBounds(int x, int y, int width, int height) {
166: super .setBounds(x, y, width, height);
167: try {
168: fixBounds();
169: } catch (NullPointerException e) {
170: /* XXX ignore... ugly hack to work around details of how java
171: * implements inner-classes... if we access dialogUtilityTable
172: * prior to this constructor returning, then the access method
173: * generated by the compiler will throw a NPE.
174: */
175: }
176: }
177: };
178: mainWindow.addWindowListener(new WindowAdapter() {
179: public void windowClosing(WindowEvent evt) {
180: main.exit(0);
181: }
182: });
183:
184: // if each window has a menubar, then we don't want a global-one
185: registry
186: .subscribeToValue(
187: "/Preferences/Window Manager/Window Mode/Dialogs have Menu Bar",
188: null,
189: haveMenuBarSubscriber = new SwingNodeSubscriber(
190: new NodeSubscriber() {
191:
192: public void publish(Node node,
193: Object value) {
194: if (((Boolean) value)
195: .booleanValue())
196: mainWindow.getRootPane()
197: .setJMenuBar(null);
198: else
199: mainWindow
200: .getRootPane()
201: .setJMenuBar(
202: new ti.chimera.MenuBar(
203: getMain(),
204: "/MenuBar"));
205: }
206:
207: }));
208:
209: return mainWindow;
210: }
211:
212: /*=======================================================================*/
213: /**
214: * Dispose of the main-window created by {@link #createMainWindow}.
215: *
216: * @param mainWindow the main-window to dispose
217: * @see #createMainWindow
218: */
219: protected void disposeMainWindow(Component mainWindow) {
220: ((JWindow) mainWindow).dispose();
221: registry.unsubscribeFromValue(haveMenuBarSubscriber);
222: }
223:
224: /*=======================================================================*/
225: /**
226: * The dialog implementation realizes the display of a dialog created
227: * by the window manager. It handles subscribing to the children of
228: * <code>/Dialogs/<TITLE></code> to receive state change notification
229: * of the dialog it is realizing. Also, it subscribes to the deletion
230: * of <code>/Dialogs/<TITLE></code> to detect that the dialog has
231: * been closed.
232: */
233: private class WindowModeDialogImplementation extends JFrame
234: implements DialogImplementation {
235: private DialogUtility util;
236: private NodeSubscriber haveMenuBarSubscriber;
237: private boolean windowVisible = false;
238: private Rectangle autoResizedBounds = null;
239:
240: /**
241: * Class Constructor
242: *
243: * @param title the unique title of this dialog
244: */
245: WindowModeDialogImplementation(final String title) {
246: super (title);
247:
248: util = new DialogUtility(this , title);
249:
250: setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
251: addWindowListener(new WindowAdapter() {
252:
253: private long lastToFrontTimeMs = 0;
254:
255: public void windowClosing(WindowEvent evt) {
256: util.triggerDispose();
257: }
258:
259: public void windowActivated(WindowEvent evt) {
260: // XXX hack... mainWindow.toFront() seems to be triggering more
261: // window-activated events, causing a feedback loop:
262: if ((mainWindow != null)
263: && ((System.currentTimeMillis() - lastToFrontTimeMs) > 100)) {
264: ((JWindow) mainWindow).toFront();
265: lastToFrontTimeMs = System.currentTimeMillis();
266: }
267: }
268:
269: });
270:
271: registry
272: .subscribeToValue(
273: "/Preferences/Window Manager/Window Mode/Dialogs have Menu Bar",
274: null,
275: haveMenuBarSubscriber = new SwingNodeSubscriber(
276: new NodeSubscriber() {
277:
278: public void publish(Node node,
279: Object value) {
280: if (((Boolean) value)
281: .booleanValue())
282: setJMenuBar(new ti.chimera.MenuBar(
283: getMain(),
284: "/MenuBar"));
285: else
286: setJMenuBar(null);
287: }
288:
289: }));
290:
291: addComponentListener(new ComponentAdapter() {
292:
293: javax.swing.Timer fixBoundsTimer;
294:
295: public void componentResized(ComponentEvent evt) {
296: // XXX how do we tell reliably if resize was user initiated?
297: if ((autoResizedBounds == null)
298: || !autoResizedBounds.equals(getBounds()))
299: resetStoredPositions();
300:
301: if (fixBoundsTimer != null) {
302: fixBoundsTimer.restart();
303: } else {
304: fixBoundsTimer = new javax.swing.Timer(100,
305: new ActionListener() {
306: public void actionPerformed(
307: ActionEvent evt) {
308: fixBoundsTimer = null;
309: fixBounds();
310: util.boundsUpdated();
311: }
312: });
313: fixBoundsTimer.setRepeats(false);
314: fixBoundsTimer.start();
315: }
316: }
317:
318: public void componentMoved(ComponentEvent evt) {
319: componentResized(evt);
320: }
321: });
322:
323: dialogImplementationTable.put(this , Boolean.TRUE);
324: }
325:
326: public void setVisible(boolean b) {
327: windowVisible = b;
328: refreshVisibility();
329: if (windowVisible) {
330: repaint();
331: // SwingUtilities.invokeLater( new Runnable() {
332: // public void run()
333: // {
334: // repaint();
335: // }
336: // } );
337: }
338: }
339:
340: public void refreshVisibility() {
341: super .setVisible(userInterfaceVisible && windowVisible);
342: }
343:
344: public void dispose() {
345: try {
346: if (getJMenuBar() != null)
347: getJMenuBar().setVisible(false);
348: util.dispose();
349: registry.unsubscribeFromValue(haveMenuBarSubscriber);
350: super .dispose();
351: } catch (WindowManager.DialogNotClosableException e) {
352: // ignore
353: }
354: }
355:
356: /*
357: public void toFront()
358: {
359: //removeNotify();
360: //addNotify();
361: //super.setVisible(true);
362: super.toFront();
363: super.requestFocus();
364: }
365: */
366:
367: public void center() {
368: Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
369: Insets i = getScreenInsets();
370: Rectangle b = getBounds();
371:
372: b.x = (d.width + i.left - i.right - b.width) / 2;
373: b.y = (d.height + i.top - i.bottom - b.height) / 2;
374:
375: setBounds(b);
376: }
377:
378: private void resetStoredPositions() {
379: xPositionMap.remove(this );
380: yPositionMap.remove(this );
381: }
382:
383: public void fixBounds() {
384: boolean hasChanged = false;
385: Rectangle mwr = mainWindow.getBounds();
386: Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
387: Insets i = getScreenInsets();
388: Rectangle r = getBounds();
389: Rectangle orig = new Rectangle(r);
390:
391: // restore original positions:
392: if (xPositionMap.get(this ) != null) {
393: r.x = ((Integer) (xPositionMap.remove(this )))
394: .intValue();
395: hasChanged = true;
396: }
397:
398: if (yPositionMap.get(this ) != null) {
399: r.y = ((Integer) (yPositionMap.remove(this )))
400: .intValue();
401: hasChanged = true;
402: }
403:
404: if (widthPositionMap.get(this ) != null) {
405: r.width = ((Integer) (widthPositionMap.remove(this )))
406: .intValue();
407: hasChanged = true;
408: }
409:
410: if (heightPositionMap.get(this ) != null) {
411: r.height = ((Integer) (heightPositionMap.remove(this )))
412: .intValue();
413: hasChanged = true;
414: }
415:
416: // now, if necessary, adjust positions until window position is legal:
417: if (r.x < i.left) {
418: xPositionMap.put(this , new Integer(r.x));
419: r.x = i.left;
420: hasChanged = true;
421: }
422:
423: if (r.y < (mwr.y + mwr.height)) {
424: yPositionMap.put(this , new Integer(r.y));
425: r.y = mwr.y + mwr.height;
426: hasChanged = true;
427: }
428:
429: if ((r.x + r.width) > (d.width - i.right)) {
430: widthPositionMap.put(this , new Integer(r.width));
431: r.width = d.width - i.right - r.x;
432: hasChanged = true;
433: }
434:
435: if ((r.y + r.height) > (d.height - i.bottom)) {
436: heightPositionMap.put(this , new Integer(r.height));
437: r.height = d.height - i.bottom - r.y;
438: hasChanged = true;
439: }
440:
441: if (hasChanged) {
442: autoResizedBounds = r;
443: setBounds(r);
444: }
445: }
446: }
447: }
448:
449: /*
450: * Local Variables:
451: * tab-width: 2
452: * indent-tabs-mode: nil
453: * mode: java
454: * c-indentation-style: java
455: * c-basic-offset: 2
456: * eval: (c-set-offset 'substatement-open '0)
457: * eval: (c-set-offset 'case-label '+)
458: * eval: (c-set-offset 'inclass '+)
459: * eval: (c-set-offset 'inline-open '0)
460: * End:
461: */
|