001: /*
002: * Copyright (C) 2004 NNL Technology AB
003: * Visit www.infonode.net for information about InfoNode(R)
004: * products and how to contact NNL Technology AB.
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU General Public License
008: * as published by the Free Software Foundation; either version 2
009: * of the License, or (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
019: * MA 02111-1307, USA.
020: */
021:
022: // $Id: FloatingWindow.java,v 1.51 2007/01/28 21:25:09 jesper Exp $
023: package net.infonode.docking;
024:
025: import net.infonode.docking.drop.ChildDropInfo;
026: import net.infonode.docking.drop.InteriorDropInfo;
027: import net.infonode.docking.internal.ReadContext;
028: import net.infonode.docking.internal.WindowAncestors;
029: import net.infonode.docking.internal.WriteContext;
030: import net.infonode.docking.internalutil.DropAction;
031: import net.infonode.docking.model.FloatingWindowItem;
032: import net.infonode.docking.model.ViewReader;
033: import net.infonode.docking.model.ViewWriter;
034: import net.infonode.docking.properties.DockingWindowProperties;
035: import net.infonode.docking.properties.FloatingWindowProperties;
036: import net.infonode.docking.properties.SplitWindowProperties;
037: import net.infonode.docking.util.DockingUtil;
038: import net.infonode.gui.ComponentUtil;
039: import net.infonode.gui.layout.StretchLayout;
040: import net.infonode.gui.panel.SimplePanel;
041: import net.infonode.gui.shaped.panel.ShapedPanel;
042: import net.infonode.properties.gui.InternalPropertiesUtil;
043: import net.infonode.properties.gui.util.ComponentProperties;
044: import net.infonode.properties.gui.util.ShapedPanelProperties;
045: import net.infonode.properties.propertymap.PropertyMap;
046: import net.infonode.properties.propertymap.PropertyMapTreeListener;
047: import net.infonode.properties.propertymap.PropertyMapWeakListenerManager;
048: import net.infonode.util.Direction;
049:
050: import javax.swing.*;
051: import java.awt.*;
052: import java.awt.event.*;
053: import java.io.IOException;
054: import java.io.ObjectInputStream;
055: import java.io.ObjectOutputStream;
056: import java.util.Map;
057:
058: /**
059: * <p>
060: * A window that is floating on-top of the root window and containing another docking window.
061: * </p>
062: *
063: * <p>
064: * A window can be maximized inside the floating window just as in a root window.
065: * </p>
066: *
067: * <p>
068: * After a floating window has been closed it shouldn't be reused again.
069: * </p>
070: *
071: * <p>
072: * Floating window inherits its component properties and shaped panel properties from the root
073: * window's window area. It is possible to set specific component and shaped panel properties for
074: * a floating window in the {@link net.infonode.docking.properties.FloatingWindowProperties}, see
075: * {@link FloatingWindow#getFloatingWindowProperties()}.
076: * </p>
077: *
078: * <p>
079: * A floating window is created by calling the
080: * {@link net.infonode.docking.RootWindow#createFloatingWindow(Point, Dimension, DockingWindow)}
081: * method or indirectly created by calling the
082: * {@link net.infonode.docking.DockingWindow#undock(Point)} method.
083: * </p>
084: *
085: * <p>
086: * It's possible to add a menu bar to the floating window. Just call:
087: * </p>
088: * <pre>
089: * myFloatingWindow.getRootPane().setJMenuBar(myMenuBar);
090: * </pre>
091: *
092: * <p>
093: * The floating window is placed as the BorderLayout.CENTER component of the content pane of the
094: * root pane. You can add additional components in the other BorderLayout positions. Example, add a
095: * status label at the bottom:
096: * </p>
097: * <pre>
098: * myFloatingWindow.getRootPane().getContentPane().add(myStstusLabel, BroderLayout.SOUTH);
099: * </pre>
100: *
101: * @author $Author: jesper $
102: * @version $Revision: 1.51 $
103: * @since IDW 1.4.0
104: */
105: public class FloatingWindow extends DockingWindow {
106: private DockingWindow window;
107: private Window dialog;
108: private JPanel dragPanel = new SimplePanel();
109: private ShapedPanel shapedPanel;
110: private DockingWindow maximizedWindow;
111: private Runnable titleUpdater;
112:
113: private AWTEventListener awtMouseEventListener;
114:
115: private PropertyMapTreeListener propertiesListener = new PropertyMapTreeListener() {
116: public void propertyValuesChanged(Map changes) {
117: updateFloatingWindow(changes);
118: }
119: };
120:
121: FloatingWindow(RootWindow rootWindow) {
122: super (new FloatingWindowItem());
123:
124: getFloatingWindowProperties().addSuperObject(
125: rootWindow.getRootWindowProperties()
126: .getFloatingWindowProperties());
127:
128: setLayout(new StretchLayout(true, true));
129: shapedPanel = new ShapedPanel();
130: setComponent(shapedPanel);
131:
132: Component c = rootWindow.getTopLevelComponent();
133: dialog = getFloatingWindowProperties().getUseFrame() ? (Window) new JFrame()
134: : (Window) (c instanceof Frame ? new JDialog((Frame) c)
135: : new JDialog((Dialog) c));
136: ((RootPaneContainer) dialog).getContentPane().add(this ,
137: BorderLayout.CENTER);
138:
139: if (dialog instanceof JDialog)
140: ((JDialog) dialog)
141: .setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
142: else
143: ((JFrame) dialog)
144: .setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
145:
146: dialog.addWindowListener(new WindowAdapter() {
147: public void windowClosing(WindowEvent e) {
148: try {
149: if (getWindowProperties().getCloseEnabled())
150: closeWithAbort();
151: } catch (OperationAbortedException e1) {
152: // Ignore
153: }
154: }
155: });
156:
157: JRootPane rp = ((RootPaneContainer) dialog).getRootPane();
158: rp.getLayeredPane().add(dragPanel);
159: rp.getLayeredPane().setLayer(dragPanel,
160: JLayeredPane.DRAG_LAYER.intValue());
161: dragPanel.setVisible(false);
162:
163: dragPanel.addMouseListener(new MouseAdapter() {
164: public void mouseEntered(MouseEvent e) {
165: getRootWindow().setCurrentDragRootPane(getRootPane());
166: }
167:
168: public void mouseExited(MouseEvent e) {
169: if (!dragPanel.contains(e.getPoint())
170: && getRootWindow().getCurrentDragRootPane() == getRootPane())
171: getRootWindow().setCurrentDragRootPane(null);
172: }
173: });
174:
175: if (rootWindow.isHeavyweightSupported()) {
176: try {
177: awtMouseEventListener = new AWTEventListener() {
178: public void eventDispatched(AWTEvent event) {
179: if (event.getID() == MouseEvent.MOUSE_ENTERED) {
180: Component c = (Component) event.getSource();
181: if (ComponentUtil.getTopLevelAncestor(c) == dialog) {
182: getRootWindow().setCurrentDragRootPane(
183: getRootPane());
184: }
185: }
186: }
187: };
188: Toolkit.getDefaultToolkit().addAWTEventListener(
189: awtMouseEventListener,
190: AWTEvent.MOUSE_EVENT_MASK);
191: } catch (SecurityException e) {
192: awtMouseEventListener = null;
193: // Ignore
194: }
195: }
196:
197: PropertyMapWeakListenerManager.addWeakTreeListener(
198: getFloatingWindowProperties().getMap(),
199: propertiesListener);
200: updateFloatingWindow(null);
201: }
202:
203: FloatingWindow(RootWindow rootWindow, DockingWindow window,
204: Point p, Dimension internalSize) {
205: this (rootWindow);
206: setWindow(window);
207: setInternalSize(internalSize);
208: dialog.setLocation(p.x, p.y);
209: }
210:
211: /**
212: * Sets the top level docking window inside this floating window.
213: *
214: * @param newWindow the top level docking window, null for no window i.e. empty floating window
215: */
216: public void setWindow(DockingWindow newWindow) {
217: if (window == newWindow)
218: return;
219:
220: if (window == null) {
221: WindowAncestors ancestors = newWindow.storeAncestors();
222: DockingWindow actualWindow = addWindow(newWindow);
223: doReplace(null, actualWindow);
224: newWindow.notifyListeners(ancestors);
225: } else if (newWindow == null) {
226: removeChildWindow(window);
227: window = null;
228: } else
229: replaceChildWindow(window, newWindow);
230: }
231:
232: /**
233: * Returns the top level docking window inside this floating window.
234: *
235: * @return the top level docking window inside this floating window
236: */
237: public DockingWindow getWindow() {
238: return window;
239: }
240:
241: /**
242: * Sets the maximized window in this floating window.
243: *
244: * @param window the window to maximize, must be a member of the window tree inside this floating window
245: */
246: public void setMaximizedWindow(DockingWindow window) {
247: if (window == maximizedWindow)
248: return;
249:
250: if (window instanceof FloatingWindow || window != null
251: && !(DockingUtil.getFloatingWindowFor(window) == this ))
252: return;
253:
254: internalSetMaximizedWindow(window);
255: }
256:
257: /**
258: * Returns the maximized window in this floating window.
259: *
260: * @return maximized window or null if no window is maximized
261: */
262: public DockingWindow getMaximizedWindow() {
263: return maximizedWindow;
264: }
265:
266: /**
267: * <p>
268: * Returns the property values for this floating window.
269: * </p>
270: *
271: * <p>
272: * Floating window inherits its component properties and shaped panel properties from the root
273: * window's window area. It is possible to set specific component and shaped panel properties for
274: * a floating window in the {@link net.infonode.docking.properties.FloatingWindowProperties}.
275: * </p>
276: *
277: * @return the property values for this floating window
278: */
279: public FloatingWindowProperties getFloatingWindowProperties() {
280: return ((FloatingWindowItem) getWindowItem())
281: .getFloatingWindowProperties();
282: }
283:
284: /**
285: * <p>
286: * Returns the properties for this window.
287: * </p>
288: *
289: * <p>
290: * <strong>Note:</strong> A floating window only uses the close enabled and title provider
291: * properties of the docking window properties.
292: * </p>
293: *
294: * @return the properties for this window
295: */
296: public DockingWindowProperties getWindowProperties() {
297: return super .getWindowProperties();
298: }
299:
300: /**
301: * Floating window cannot be minimized
302: */
303: public void minimize() {
304: }
305:
306: /**
307: * Floating window cannot be minimized
308: *
309: * @param direction
310: */
311: public void minimize(Direction direction) {
312: }
313:
314: public boolean isDockable() {
315: return false;
316: }
317:
318: public boolean isMaximizable() {
319: return false;
320: }
321:
322: public boolean isMinimizable() {
323: return false;
324: }
325:
326: public boolean isRestorable() {
327: return false;
328: }
329:
330: public boolean isUndockable() {
331: return false;
332: }
333:
334: public void close() {
335: PropertyMapWeakListenerManager.removeWeakTreeListener(
336: getFloatingWindowProperties().getMap(),
337: propertiesListener);
338: RootWindow rw = getRootWindow();
339:
340: super .close();
341:
342: dialog.dispose();
343: if (rw != null)
344: rw.removeFloatingWindow(this );
345:
346: try {
347: if (awtMouseEventListener != null)
348: Toolkit.getDefaultToolkit().removeAWTEventListener(
349: awtMouseEventListener);
350: } catch (SecurityException e) {
351: // Ignore
352: }
353: }
354:
355: public Icon getIcon() {
356: return window == null ? null : window.getIcon();
357: }
358:
359: public DockingWindow getChildWindow(int index) {
360: return window;
361: }
362:
363: public int getChildWindowCount() {
364: return window == null ? 0 : 1;
365: }
366:
367: public boolean isUndocked() {
368: return true;
369: }
370:
371: void startDrag() {
372: JRootPane rp = ((RootPaneContainer) dialog).getRootPane();
373: dragPanel.setBounds(0, 0, rp.getWidth(), rp.getHeight());
374: dragPanel.setVisible(true);
375: //ComponentUtil.validate(rp.getLayeredPane());
376: //rp.validate();
377: }
378:
379: void stopDrag() {
380: // JRootPane rp = dialog.getRootPane();
381: dragPanel.setVisible(false);
382: //rp.getLayeredPane().remove(dragPanel);
383: }
384:
385: JPanel getDragPanel() {
386: return dragPanel;
387: }
388:
389: boolean windowContainsPoint(Point p) {
390: return getTopLevelAncestor().contains(
391: SwingUtilities.convertPoint(this , p,
392: getTopLevelAncestor()));
393: }
394:
395: private void internalSetMaximizedWindow(DockingWindow window) {
396: if (window == maximizedWindow)
397: return;
398:
399: DockingWindow focusWindow = null;
400:
401: if (maximizedWindow != null) {
402: DockingWindow oldMaximized = maximizedWindow;
403: maximizedWindow = null;
404:
405: if (oldMaximized.getWindowParent() != null)
406: oldMaximized.getWindowParent().restoreWindowComponent(
407: oldMaximized);
408:
409: if (oldMaximized != this .window)
410: shapedPanel.remove(oldMaximized);
411:
412: focusWindow = oldMaximized;
413: fireWindowRestored(oldMaximized);
414: }
415:
416: maximizedWindow = window;
417:
418: if (maximizedWindow != null) {
419: if (maximizedWindow.getWindowParent() != null)
420: maximizedWindow.getWindowParent()
421: .removeWindowComponent(maximizedWindow);
422:
423: if (maximizedWindow != this .window) {
424: shapedPanel.add(maximizedWindow);
425:
426: if (this .window != null)
427: this .window.setVisible(false);
428: }
429:
430: maximizedWindow.setVisible(true);
431:
432: focusWindow = maximizedWindow;
433: fireWindowMaximized(maximizedWindow);
434: } else if (this .window != null) {
435: this .window.setVisible(true);
436: }
437:
438: if (focusWindow != null)
439: FocusManager.focusWindow(focusWindow);
440: }
441:
442: protected void doReplace(DockingWindow oldWindow,
443: DockingWindow newWindow) {
444: if (oldWindow == window) {
445: if (window != null) {
446: shapedPanel.remove(window);
447: window.setVisible(true);
448: }
449:
450: window = newWindow;
451:
452: if (window != null) {
453: if (maximizedWindow != null)
454: window.setVisible(false);
455:
456: shapedPanel.add(window);
457: doUpdateTitle();
458: shapedPanel.revalidate();
459: }
460: }
461:
462: updateButtonVisibility();
463: }
464:
465: protected void doRemoveWindow(DockingWindow window) {
466: if (window != null) {
467: shapedPanel.remove(window);
468: this .window.setVisible(true);
469: this .window = null;
470: shapedPanel.repaint();
471: }
472: }
473:
474: protected void afterWindowRemoved(DockingWindow window) {
475: if (getFloatingWindowProperties().getAutoCloseEnabled())
476: close();
477: }
478:
479: private void doUpdateTitle() {
480: if (titleUpdater == null) {
481: titleUpdater = new Runnable() {
482: public void run() {
483: if (dialog != null) {
484: if (dialog instanceof Dialog)
485: ((Dialog) dialog)
486: .setTitle(window == null ? ""
487: : window.getTitle());
488: else
489: ((Frame) dialog)
490: .setTitle(window == null ? ""
491: : window.getTitle());
492: }
493:
494: titleUpdater = null;
495: }
496: };
497:
498: SwingUtilities.invokeLater(titleUpdater);
499: }
500: }
501:
502: protected boolean acceptsSplitWith(DockingWindow window) {
503: return false;
504: }
505:
506: protected DropAction doAcceptDrop(Point p, DockingWindow window) {
507: DockingWindow dropWindow = maximizedWindow != null ? maximizedWindow
508: : this .window;
509:
510: if (dropWindow != null) {
511: Point p2 = SwingUtilities.convertPoint(this , p, dropWindow);
512:
513: if (dropWindow.contains(p2)) {
514: return getChildDropFilter().acceptDrop(
515: new ChildDropInfo(window, this , p, dropWindow)) ? dropWindow
516: .acceptDrop(p2, window)
517: : null;
518: }
519: }
520:
521: return super .doAcceptDrop(p, window);
522: }
523:
524: protected DropAction acceptInteriorDrop(Point p,
525: DockingWindow window) {
526: if (this .window != null)
527: return null;
528:
529: getRootWindow().setDragRectangle(null);
530:
531: if (getInteriorDropFilter().acceptDrop(
532: new InteriorDropInfo(window, this , p)))
533: return new DropAction() {
534: public void execute(DockingWindow window,
535: MouseEvent mouseEvent) {
536: setWindow(window);
537: }
538: };
539:
540: return null;
541: }
542:
543: protected void update() {
544: }
545:
546: void removeWindowComponent(DockingWindow window) {
547: // Do nothing
548: }
549:
550: void restoreWindowComponent(DockingWindow window) {
551: // Do nothing
552: }
553:
554: protected void showChildWindow(DockingWindow window) {
555: if (maximizedWindow != null && window == this .window)
556: setMaximizedWindow(null);
557:
558: super .showChildWindow(window);
559: }
560:
561: protected PropertyMap getPropertyObject() {
562: return new SplitWindowProperties().getMap();
563: }
564:
565: protected PropertyMap createPropertyObject() {
566: return new SplitWindowProperties().getMap();
567: }
568:
569: private void updateFloatingWindow(Map map) {
570: FloatingWindowProperties properties = getFloatingWindowProperties();
571: ComponentProperties componentProperties = map == null
572: || map
573: .get(properties.getComponentProperties()
574: .getMap()) != null ? properties
575: .getComponentProperties() : null;
576: ShapedPanelProperties shapedProperties = map == null
577: || map.get(properties.getShapedPanelProperties()
578: .getMap()) != null ? properties
579: .getShapedPanelProperties() : null;
580:
581: if (componentProperties != null)
582: componentProperties.applyTo(shapedPanel);
583:
584: if (shapedProperties != null)
585: InternalPropertiesUtil.applyTo(shapedProperties,
586: shapedPanel);
587: }
588:
589: protected void fireTitleChanged() {
590: super .fireTitleChanged();
591:
592: doUpdateTitle();
593: }
594:
595: private void setInternalSize(Dimension size) {
596: ((RootPaneContainer) dialog).getRootPane().setPreferredSize(
597: size);
598: dialog.pack();
599: ((RootPaneContainer) dialog).getRootPane().setPreferredSize(
600: null);
601: }
602:
603: protected DockingWindow read(ObjectInputStream in,
604: ReadContext context, ViewReader viewReader)
605: throws IOException {
606: dialog.setSize(new Dimension(in.readInt(), in.readInt()));
607: dialog.setLocation(in.readInt(), in.readInt());
608:
609: dialog.setVisible(in.readBoolean());
610: getWindowItem().readSettings(in, context);
611:
612: if (in.readBoolean())
613: setWindow(WindowDecoder.decodeWindow(in, context,
614: viewReader));
615:
616: return this ;
617: }
618:
619: protected void write(ObjectOutputStream out, WriteContext context,
620: ViewWriter viewWriter) throws IOException {
621: out.writeInt(dialog.getWidth());
622: out.writeInt(dialog.getHeight());
623: out.writeInt(dialog.getX());
624: out.writeInt(dialog.getY());
625: out.writeBoolean(dialog.isVisible());
626: getWindowItem().writeSettings(out, context);
627: out.writeBoolean(window != null);
628:
629: if (window != null)
630: window.write(out, context, viewWriter);
631: }
632: }
|