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: View.java,v 1.66 2005/12/03 14:34:34 jesper Exp $
023: package net.infonode.docking;
024:
025: import net.infonode.docking.drag.DockingWindowDragSource;
026: import net.infonode.docking.drag.DockingWindowDragger;
027: import net.infonode.docking.drag.DockingWindowDraggerProvider;
028: import net.infonode.docking.drop.InteriorDropInfo;
029: import net.infonode.docking.internal.ReadContext;
030: import net.infonode.docking.internal.ViewTitleBar;
031: import net.infonode.docking.internal.WriteContext;
032: import net.infonode.docking.internalutil.DropAction;
033: import net.infonode.docking.model.ViewItem;
034: import net.infonode.docking.model.ViewWriter;
035: import net.infonode.docking.properties.ViewProperties;
036: import net.infonode.docking.properties.ViewTitleBarProperties;
037: import net.infonode.gui.ComponentUtil;
038: import net.infonode.gui.panel.SimplePanel;
039: import net.infonode.properties.base.Property;
040: import net.infonode.properties.propertymap.PropertyMap;
041: import net.infonode.properties.propertymap.PropertyMapWeakListenerManager;
042: import net.infonode.properties.util.PropertyChangeListener;
043: import net.infonode.tabbedpanel.TabbedPanel;
044: import net.infonode.util.ChangeNotifyList;
045: import net.infonode.util.Direction;
046: import net.infonode.util.StreamUtil;
047:
048: import javax.swing.*;
049: import javax.swing.border.EmptyBorder;
050: import java.awt.*;
051: import java.awt.event.HierarchyEvent;
052: import java.awt.event.HierarchyListener;
053: import java.awt.event.MouseAdapter;
054: import java.awt.event.MouseEvent;
055: import java.io.*;
056: import java.lang.ref.WeakReference;
057: import java.util.List;
058:
059: /**
060: * <p>
061: * A view is a docking window containing a component.
062: * </p>
063: *
064: * <p>
065: * A view can also contain a title bar that can be shown on either side of the view component.
066: * The title bar is made visible by setting the visible property in the ViewTitleBarProperties
067: * in the ViewProperties for this view. The title bar automatically inherits the view's title
068: * and icon but it's possible to specify a specific title and icon for the title bar in the
069: * ViewTitleBarProperties in the ViewProperties for this view.
070: * </p>
071: *
072: * @author $Author: jesper $
073: * @version $Revision: 1.66 $
074: * @see net.infonode.docking.properties.ViewProperties
075: * @see net.infonode.docking.properties.ViewTitleBarProperties
076: */
077: public class View extends DockingWindow {
078: private Component lastFocusedComponent;
079: private HierarchyListener focusComponentListener = new HierarchyListener() {
080: public void hierarchyChanged(HierarchyEvent e) {
081: checkLastFocusedComponent();
082: }
083: };
084: private SimplePanel contentPanel = new SimplePanel();
085: private ViewProperties rootProperties = new ViewProperties();
086: private WeakReference lastRootWindow;
087: private PropertyChangeListener listener = new PropertyChangeListener() {
088: public void propertyChanged(Property property,
089: Object valueContainer, Object oldValue, Object newValue) {
090: fireTitleChanged();
091: }
092: };
093:
094: private PropertyChangeListener titleBarPropertiesListener = new PropertyChangeListener() {
095: public void propertyChanged(Property property,
096: Object valueContainer, Object oldValue, Object newValue) {
097: updateTitleBar(property, valueContainer);
098: }
099: };
100:
101: private ViewTitleBar titleBar;
102: private boolean isfocused = false;
103: private List customTitleBarComponents;
104:
105: private WindowTab ghostTab;
106:
107: private DropAction titleBarDropAction = new DropAction() {
108: public boolean showTitle() {
109: return false;
110: }
111:
112: public void execute(DockingWindow window, MouseEvent mouseEvent) {
113: removeGhostTab();
114:
115: ((AbstractTabWindow) getWindowParent()).addTab(window);
116: }
117:
118: public void clear(DockingWindow window, DropAction newDropAction) {
119: if (newDropAction != this )
120: removeGhostTab();
121: }
122:
123: private void removeGhostTab() {
124: if (ghostTab != null) {
125: TabbedPanel tp = ((AbstractTabWindow) getWindowParent())
126: .getTabbedPanel();
127: tp.removeTab(ghostTab);
128: ghostTab = null;
129:
130: if (tp.getProperties().getEnsureSelectedTabVisible()
131: && tp.getSelectedTab() != null) {
132: tp.scrollTabToVisibleArea(tp.getSelectedTab());
133: }
134: }
135: }
136:
137: };
138:
139: /**
140: * Constructor.
141: *
142: * @param title the title of the view
143: * @param icon the icon for the view
144: * @param component the component to place inside the view
145: */
146: public View(String title, Icon icon, Component component) {
147: super (new ViewItem());
148: rootProperties.setTitle(title);
149: rootProperties.setIcon(icon);
150: getViewProperties().addSuperObject(rootProperties);
151: super .setComponent(contentPanel);
152: contentPanel.setComponent(component);
153:
154: PropertyMapWeakListenerManager.addWeakPropertyChangeListener(
155: getViewProperties().getMap(), ViewProperties.TITLE,
156: listener);
157: PropertyMapWeakListenerManager.addWeakPropertyChangeListener(
158: getViewProperties().getMap(), ViewProperties.ICON,
159: listener);
160:
161: PropertyMapWeakListenerManager.addWeakPropertyChangeListener(
162: getViewProperties().getViewTitleBarProperties()
163: .getMap(), ViewTitleBarProperties.VISIBLE,
164: titleBarPropertiesListener);
165: PropertyMapWeakListenerManager.addWeakPropertyChangeListener(
166: getViewProperties().getViewTitleBarProperties()
167: .getMap(),
168: ViewTitleBarProperties.CONTENT_TITLE_BAR_GAP,
169: titleBarPropertiesListener);
170: PropertyMapWeakListenerManager.addWeakPropertyChangeListener(
171: getViewProperties().getViewTitleBarProperties()
172: .getMap(), ViewTitleBarProperties.ORIENTATION,
173: titleBarPropertiesListener);
174:
175: updateTitleBar(null, null);
176:
177: init();
178: }
179:
180: /**
181: * <p>
182: * Returns a list containing the custom window tab components. Changes to the list will be propagated to the tab.
183: * </p>
184: * <p>
185: * The custom tab components will be shown after the window title when the window tab is highlighted. The
186: * components are shown in the same order as they appear in the list. The custom tab components container layout is
187: * rotated with the tab direction.
188: * </p>
189: *
190: * @return a list containing the custom tab components, list elements are of type {@link JComponent}
191: * @since IDW 1.3.0
192: */
193: public java.util.List getCustomTabComponents() {
194: return getTab().getCustomTabComponentsList();
195: }
196:
197: /**
198: * <p>
199: * Returns a list containing the custom view title bar components. Changes to the list will be propagated to the title bar.
200: * </p>
201: * <p>
202: * The custom title bar components will be shown after the view title in the title bar but before the close, minimize and restore
203: * buttons. The components are shown in the same order as they appear in the list. The custom title bar components container
204: * layout is rotated with the title bar direction.
205: * </p>
206: * <p>
207: * <strong>Note:</strong> The components are only shon if the title bar is visible, see {@link ViewTitleBarProperties}.
208: * </p>
209: *
210: * @return a list containing the custom title bar components, list elements are of type {@link JComponent}
211: * @since IDW 1.4.0
212: */
213: public List getCustomTitleBarComponents() {
214: if (customTitleBarComponents == null)
215: customTitleBarComponents = new ChangeNotifyList() {
216: protected void changed() {
217: if (titleBar != null)
218: titleBar.updateCustomBarComponents(this );
219: }
220: };
221:
222: return customTitleBarComponents;
223: }
224:
225: /**
226: * Gets the component inside the view.
227: *
228: * @return the component inside the view
229: * @since IDW 1.1.0
230: */
231: public Component getComponent() {
232: return contentPanel.getComponent(0);
233: }
234:
235: /**
236: * Sets the component inside the view.
237: *
238: * @param component the component to place inside the view
239: * @since IDW 1.1.0
240: */
241: public void setComponent(Component component) {
242: contentPanel.setComponent(component);
243: }
244:
245: /**
246: * Returns the property values for this view.
247: *
248: * @return the property values for this view
249: */
250: public ViewProperties getViewProperties() {
251: return ((ViewItem) getWindowItem()).getViewProperties();
252: }
253:
254: protected void update() {
255: // TODO:
256: }
257:
258: public DockingWindow getChildWindow(int index) {
259: return null;
260: }
261:
262: public int getChildWindowCount() {
263: return 0;
264: }
265:
266: void setLastFocusedComponent(Component component) {
267: if (component != lastFocusedComponent) {
268: if (lastFocusedComponent != null)
269: lastFocusedComponent
270: .removeHierarchyListener(focusComponentListener);
271:
272: // System.out.println("Focus: " + this + ", " + component.getClass() + " " + System.identityHashCode(component));
273: lastFocusedComponent = component;
274:
275: if (lastFocusedComponent != null)
276: lastFocusedComponent
277: .addHierarchyListener(focusComponentListener);
278: }
279: }
280:
281: Component getFocusComponent() {
282: checkLastFocusedComponent();
283: return lastFocusedComponent;
284: }
285:
286: public boolean isFocusCycleRoot() {
287: return true;
288: }
289:
290: /**
291: * Restores focus to the last focused child component or, if no child component has had focus,
292: * the first focusable component inside the view.
293: *
294: * @since IDW 1.1.0
295: */
296: public void restoreFocus() {
297: makeVisible();
298: checkLastFocusedComponent();
299:
300: if (lastFocusedComponent == null) {
301: ComponentUtil.smartRequestFocus(contentPanel);
302: } else {
303: // System.out.println("Restore: " + this + ", " + lastFocusedComponent.getClass() + " " + System.identityHashCode(lastFocusedComponent));
304: lastFocusedComponent.requestFocusInWindow();
305: }
306: }
307:
308: public Icon getIcon() {
309: return getViewProperties().getIcon();
310: }
311:
312: protected void doReplace(DockingWindow oldWindow,
313: DockingWindow newWindow) {
314: throw new RuntimeException(View.class
315: + ".replaceChildWindow called!");
316: }
317:
318: protected void doRemoveWindow(DockingWindow window) {
319: throw new RuntimeException(View.class
320: + ".removeChildWindow called!");
321: }
322:
323: protected void write(ObjectOutputStream out, WriteContext context)
324: throws IOException {
325: ByteArrayOutputStream baos = new ByteArrayOutputStream();
326: ObjectOutputStream oos = new ObjectOutputStream(baos);
327:
328: try {
329: context.getViewSerializer().writeView(this , oos);
330: getWindowItem().writeSettings(oos, context);
331: } finally {
332: oos.close();
333: }
334:
335: out.writeInt(baos.size());
336: baos.writeTo(out);
337: }
338:
339: static View read(ObjectInputStream in, ReadContext context)
340: throws IOException {
341: int size = in.readInt();
342: byte[] viewData = new byte[size];
343: StreamUtil.readAll(in, viewData);
344: ObjectInputStream viewIn = new ObjectInputStream(
345: new ByteArrayInputStream(viewData));
346: View view = context.getViewSerializer().readView(viewIn);
347:
348: if (view != null)
349: view.getWindowItem().readSettings(viewIn, context);
350:
351: return view;
352: }
353:
354: protected DropAction doAcceptDrop(Point p, DockingWindow window) {
355: if (getWindowParent() instanceof TabWindow
356: && titleBar != null
357: && titleBar.contains(SwingUtilities.convertPoint(this ,
358: p, titleBar))) {
359: return acceptInteriorDrop(p, window);
360: }
361:
362: return getWindowParent() instanceof TabWindow
363: && getWindowParent().getChildWindowCount() == 1 ? null
364: : super .doAcceptDrop(p, window);
365: }
366:
367: protected DropAction acceptInteriorDrop(Point p,
368: DockingWindow window) {
369: if (getWindowParent() instanceof TabWindow && titleBar != null
370: && window.getWindowParent() != getWindowParent()) {
371: Point p2 = SwingUtilities.convertPoint(this , p, titleBar);
372: if (titleBar.contains(p2)) {
373: if (!getInteriorDropFilter().acceptDrop(
374: new InteriorDropInfo(window, this , p)))
375: return null;
376:
377: addGhostTab(window);
378: Component c = getWindowParent() instanceof TabWindow ? getWindowParent()
379: : this ;
380: getRootWindow()
381: .setDragRectangle(
382: SwingUtilities.convertRectangle(c,
383: new Rectangle(0, 0, c
384: .getWidth(), c
385: .getHeight()),
386: getRootWindow()));
387:
388: return titleBarDropAction;
389: }
390: }
391:
392: return null;
393: }
394:
395: private void addGhostTab(DockingWindow window) {
396: if (ghostTab == null) {
397: ghostTab = ((AbstractTabWindow) getWindowParent())
398: .createGhostTab(window);
399: ((AbstractTabWindow) getWindowParent()).getTabbedPanel()
400: .addTab(ghostTab);
401: ((AbstractTabWindow) getWindowParent()).getTabbedPanel()
402: .scrollTabToVisibleArea(ghostTab);
403: }
404: }
405:
406: public String toString() {
407: return getTitle();
408: }
409:
410: void setRootWindow(RootWindow newRoot) {
411: if (newRoot == null)
412: return;
413:
414: RootWindow last = lastRootWindow == null ? null
415: : (RootWindow) lastRootWindow.get();
416:
417: if (last == newRoot)
418: return;
419:
420: if (last != null)
421: last.removeView(this );
422:
423: lastRootWindow = new WeakReference(newRoot);
424: newRoot.addView(this );
425: }
426:
427: protected void setFocused(boolean focused) {
428: super .setFocused(focused);
429: if (isfocused != focused) {
430: isfocused = focused;
431:
432: if (focused)
433: getViewProperties().getViewTitleBarProperties()
434: .getNormalProperties().addSuperObject(
435: getViewProperties()
436: .getViewTitleBarProperties()
437: .getFocusedProperties());
438: else
439: getViewProperties().getViewTitleBarProperties()
440: .getNormalProperties().removeSuperObject(
441: getViewProperties()
442: .getViewTitleBarProperties()
443: .getFocusedProperties());
444: }
445: }
446:
447: protected void rootChanged(final RootWindow oldRoot,
448: final RootWindow newRoot) {
449: super .rootChanged(oldRoot, newRoot);
450: setRootWindow(newRoot);
451:
452: // TODO: eliminate root window = null because triggers property updates.
453: if (oldRoot != getRootWindow()) {
454: if (oldRoot != null)
455: rootProperties.removeSuperObject(oldRoot
456: .getRootWindowProperties().getViewProperties());
457:
458: if (getRootWindow() != null) {
459: rootProperties.addSuperObject(getRootWindow()
460: .getRootWindowProperties().getViewProperties());
461: }
462: }
463: }
464:
465: protected PropertyMap getPropertyObject() {
466: return getViewProperties().getMap();
467: }
468:
469: protected PropertyMap createPropertyObject() {
470: return new ViewProperties().getMap();
471: }
472:
473: protected boolean needsTitleWindow() {
474: return getViewProperties().getAlwaysShowTitle();
475: }
476:
477: private void checkLastFocusedComponent() {
478: if (lastFocusedComponent != null
479: && !SwingUtilities.isDescendingFrom(
480: lastFocusedComponent, this )) {
481: lastFocusedComponent
482: .removeHierarchyListener(focusComponentListener);
483: lastFocusedComponent = null;
484: }
485: }
486:
487: void removeWindowComponent(DockingWindow window) {
488: }
489:
490: void restoreWindowComponent(DockingWindow window) {
491: }
492:
493: private void updateTitleBar(Property property, Object valueContainer) {
494: boolean changed = valueContainer == null;
495:
496: ViewTitleBarProperties titleBarProperties = getViewProperties()
497: .getViewTitleBarProperties();
498: //System.out.println("Updating title bar " + changed + " " + property);
499:
500: if (changed || property == ViewTitleBarProperties.VISIBLE) {
501: if (titleBarProperties.getVisible()) {
502: if (titleBar == null) {
503: titleBar = new ViewTitleBar(this );
504: new DockingWindowDragSource(titleBar,
505: new DockingWindowDraggerProvider() {
506: public DockingWindowDragger getDragger(
507: MouseEvent mouseEvent) {
508: return getWindowProperties()
509: .getDragEnabled() ? startDrag(getRootWindow())
510: : null;
511: }
512: });
513: titleBar.addMouseListener(new MouseAdapter() {
514: public void mousePressed(MouseEvent e) {
515: fireTabWindowMouseButtonEvent(e);
516: checkPopupMenu(e);
517: }
518:
519: public void mouseClicked(MouseEvent e) {
520: fireTabWindowMouseButtonEvent(e);
521: }
522:
523: public void mouseReleased(MouseEvent e) {
524: fireTabWindowMouseButtonEvent(e);
525: checkPopupMenu(e);
526: }
527:
528: private void checkPopupMenu(MouseEvent e) {
529: if (e.isPopupTrigger()) {
530: showPopupMenu(e);
531: }
532: }
533: });
534:
535: if (customTitleBarComponents != null)
536: titleBar
537: .updateCustomBarComponents(customTitleBarComponents);
538: changed = true;
539: }
540: } else {
541: if (titleBar != null) {
542: remove(titleBar);
543: titleBar.dispose();
544: titleBar = null;
545:
546: changed = true;
547: }
548: }
549: }
550:
551: if (changed || property == ViewTitleBarProperties.ORIENTATION) {
552: if (titleBar != null) {
553: remove(titleBar);
554: add(titleBar, ComponentUtil
555: .getBorderLayoutOrientation(titleBarProperties
556: .getOrientation()));
557:
558: changed = true;
559: }
560: }
561:
562: if (changed
563: || property == ViewTitleBarProperties.CONTENT_TITLE_BAR_GAP) {
564: if (titleBar != null) {
565: Direction orientation = titleBarProperties
566: .getOrientation();
567: int contentBarGap = titleBarProperties
568: .getContentTitleBarGap();
569: contentPanel
570: .setBorder(new EmptyBorder(
571: orientation == Direction.UP ? contentBarGap
572: : 0,
573: orientation == Direction.LEFT ? contentBarGap
574: : 0,
575: orientation == Direction.DOWN ? contentBarGap
576: : 0,
577: orientation == Direction.RIGHT ? contentBarGap
578: : 0));
579: } else {
580: contentPanel.setBorder(null);
581: }
582: }
583: }
584:
585: protected void updateButtonVisibility() {
586: super .updateButtonVisibility();
587:
588: if (titleBar != null)
589: titleBar.updateViewButtons(null);
590: }
591:
592: protected void write(ObjectOutputStream out, WriteContext context,
593: ViewWriter viewWriter) throws IOException {
594: out.writeInt(WindowIds.VIEW);
595: viewWriter.writeView(this, out, context);
596: }
597: }
|