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: AbstractTabWindow.java,v 1.72 2007/01/28 21:25:09 jesper Exp $
023: package net.infonode.docking;
024:
025: import net.infonode.docking.drop.InsertTabDropInfo;
026: import net.infonode.docking.internal.ReadContext;
027: import net.infonode.docking.internal.WindowAncestors;
028: import net.infonode.docking.internal.WriteContext;
029: import net.infonode.docking.internalutil.DropAction;
030: import net.infonode.docking.model.AbstractTabWindowItem;
031: import net.infonode.docking.model.ViewReader;
032: import net.infonode.docking.model.ViewWriter;
033: import net.infonode.docking.model.WindowItem;
034: import net.infonode.docking.properties.TabWindowProperties;
035: import net.infonode.docking.properties.WindowTabProperties;
036: import net.infonode.properties.propertymap.PropertyMapManager;
037: import net.infonode.tabbedpanel.*;
038: import net.infonode.util.ChangeNotifyList;
039:
040: import javax.swing.*;
041: import java.awt.*;
042: import java.awt.event.MouseAdapter;
043: import java.awt.event.MouseEvent;
044: import java.io.IOException;
045: import java.io.ObjectInputStream;
046: import java.io.ObjectOutputStream;
047:
048: /**
049: * Abstract base class for windows containing a tabbed panel.
050: *
051: * @author $Author: jesper $
052: * @version $Revision: 1.72 $
053: */
054: abstract public class AbstractTabWindow extends DockingWindow {
055: private static int MINIMUM_SIZE = 7;
056:
057: private DropAction dropAction = new DropAction() {
058: public boolean showTitle() {
059: return false;
060: }
061:
062: public void execute(DockingWindow window, MouseEvent mouseEvent) {
063: if (window.getWindowParent() != AbstractTabWindow.this ) {
064: int index = tabbedPanel.getTabIndex(dragTab);
065: stopDrag();
066:
067: try {
068: window.beforeDrop(AbstractTabWindow.this );
069:
070: if (mouseEvent.isShiftDown())
071: addTabNoSelect(window, index);
072: else
073: addTab(window, index);
074: } catch (OperationAbortedException e) {
075: // Ignore
076: }
077: }
078: }
079:
080: public void clear(DockingWindow window, DropAction newDropAction) {
081: if (newDropAction != this ) {
082: if (window.getWindowParent() == AbstractTabWindow.this ) {
083: WindowTab tab = window.getTab();
084: boolean selected = tab.isSelected();
085: tabbedPanel.removeTab(tab);
086: tabbedPanel.insertTab(tab, draggedTabIndex);
087:
088: if (selected)
089: tab.setSelected(true);
090: } else {
091: stopDrag();
092: }
093: }
094: }
095: };
096:
097: private TabbedPanel tabbedPanel;
098:
099: /**
100: * Temporary drag tab.
101: */
102: private WindowTab dragTab;
103:
104: private int ignoreSelected;
105:
106: private int draggedTabIndex;
107:
108: private java.util.List tabAreaComponents;
109:
110: /**
111: * Returns the properties for this tab window.
112: *
113: * @return the properties for this tab window
114: */
115: abstract public TabWindowProperties getTabWindowProperties();
116:
117: protected AbstractTabWindow(boolean showContent,
118: WindowItem windowItem) {
119: super (windowItem);
120: if (showContent) {
121: TabContentPanel contentPanel = new TabContentPanel() {
122: public Dimension getMinimumSize() {
123: if (getTabWindowProperties()
124: .getRespectChildWindowMinimumSize())
125: return super .getMinimumSize();
126:
127: return new Dimension(0, 0);
128: }
129: };
130: tabbedPanel = new TabbedPanel(contentPanel, true) {
131: public Dimension getMinimumSize() {
132: if (getTabWindowProperties()
133: .getRespectChildWindowMinimumSize())
134: return super .getMinimumSize();
135:
136: return getTabbedPanelMinimumSize(super
137: .getMinimumSize());
138: }
139: };
140: contentPanel.setTabbedPanel(tabbedPanel);
141: } else
142: tabbedPanel = new TabbedPanel(null) {
143: public Dimension getMinimumSize() {
144: if (getTabWindowProperties()
145: .getRespectChildWindowMinimumSize())
146: return super .getMinimumSize();
147:
148: return getTabbedPanelMinimumSize(super
149: .getMinimumSize());
150: }
151: };
152:
153: tabbedPanel
154: .addTabListener(new TabWindowMover(this , tabbedPanel));
155: setComponent(tabbedPanel);
156:
157: getTabbedPanel().addTabListener(new TabAdapter() {
158: public void tabAdded(final TabEvent event) {
159: SwingUtilities.invokeLater(new Runnable() {
160: public void run() {
161: updateButtonVisibility();
162: }
163: });
164: }
165:
166: public void tabRemoved(final TabEvent event) {
167: SwingUtilities.invokeLater(new Runnable() {
168: public void run() {
169: updateButtonVisibility();
170: }
171: });
172: }
173:
174: public void tabSelected(TabStateChangedEvent event) {
175: AbstractTabWindow.this .tabSelected((WindowTab) event
176: .getTab());
177: DockingWindow selectedWindow = getSelectedWindow();
178:
179: if (!getIgnoreSelected() && selectedWindow != null)
180: selectedWindow.fireWindowShown(selectedWindow);
181: }
182:
183: public void tabDeselected(TabStateChangedEvent event) {
184: WindowTab tab = (WindowTab) event.getTab();
185:
186: if (tab != null && !getIgnoreSelected())
187: tab.getWindow().fireWindowHidden(tab.getWindow());
188: }
189:
190: public void tabMoved(TabEvent event) {
191: if (!getIgnoreSelected())
192: fireTitleChanged();
193: }
194: });
195: }
196:
197: protected void initMouseListener() {
198: getTabbedPanel().addMouseListener(new MouseAdapter() {
199: public void mousePressed(MouseEvent e) {
200: if (e.isPopupTrigger()) {
201: showPopupMenu(e);
202: }
203: }
204:
205: public void mouseReleased(MouseEvent e) {
206: mousePressed(e);
207: }
208: });
209: }
210:
211: private Dimension getTabbedPanelMinimumSize(Dimension d) {
212: if (tabbedPanel.getProperties().getTabAreaOrientation()
213: .isHorizontal())
214: return new Dimension(d.width, MINIMUM_SIZE);
215: else
216: return new Dimension(MINIMUM_SIZE, d.height);
217: }
218:
219: /**
220: * <p>
221: * Returns a list containing the custom tab area components. Changes to the
222: * list will be propagated to the tab area.
223: * </p>
224: * <p>
225: * The custom tab area components will between the scroll buttons and the
226: * window buttons in the tab area components panel. The components are shown
227: * in the same order as they appear in the list. The tab area components
228: * container layout is rotated with the tab window tab orientation.
229: * </p>
230: *
231: * @return a list containing the custom tab area components, list elements are
232: * of type {@link JComponent}
233: * @since IDW 1.3.0
234: */
235: public final java.util.List getCustomTabAreaComponents() {
236: if (tabAreaComponents == null)
237: tabAreaComponents = new ChangeNotifyList() {
238: protected void changed() {
239: updateTabAreaComponents();
240: }
241: };
242:
243: return tabAreaComponents;
244: }
245:
246: /**
247: * Returns the currently selected window in the tabbed panel.
248: *
249: * @return the currently selected window in the tabbed panel
250: */
251: public DockingWindow getSelectedWindow() {
252: WindowTab tab = (WindowTab) tabbedPanel.getSelectedTab();
253: return tab == null ? null : tab.getWindow();
254: }
255:
256: /**
257: * Selects the tab with the index.
258: *
259: * @param index the tab index
260: */
261: public void setSelectedTab(int index) {
262: beginIgnoreSelected();
263:
264: try {
265: Tab tab = index == -1 ? null : tabbedPanel.getTabAt(index);
266: Tab oldTab = tabbedPanel.getSelectedTab();
267:
268: if (tab != oldTab) {
269: tabbedPanel.setSelectedTab(tab);
270: fireTitleChanged();
271:
272: if (oldTab != null)
273: ((WindowTab) oldTab).getWindow().fireWindowHidden(
274: ((WindowTab) oldTab).getWindow());
275:
276: if (tab != null)
277: ((WindowTab) tab).getWindow().fireWindowShown(
278: ((WindowTab) tab).getWindow());
279: }
280: } finally {
281: endIgnoreSelected();
282: }
283: }
284:
285: /**
286: * Adds a window tab last in this tab window.
287: *
288: * @param window the window
289: */
290: public void addTab(DockingWindow window) {
291: PropertyMapManager.getInstance().beginBatch();
292:
293: try {
294: addTab(window, tabbedPanel.getTabCount());
295: } finally {
296: PropertyMapManager.getInstance().endBatch();
297: }
298: }
299:
300: /**
301: * Inserts a window tab at an index in this tab window.
302: *
303: * @param window the window
304: * @param index the index where to insert the tab
305: * @return the index of the added tab, this might not be the same as
306: * <tt>index</tt> if the tab already is added to this tab window
307: */
308: public int addTab(DockingWindow window, int index) {
309: PropertyMapManager.getInstance().beginBatch();
310:
311: try {
312: return addTabNoSelect(window, index);
313: } finally {
314: PropertyMapManager.getInstance().endBatch();
315: }
316: }
317:
318: protected int addTabNoSelect(DockingWindow window, int index) {
319: WindowAncestors ancestors = window.storeAncestors();
320: beginOptimize(window.getWindowParent());
321: beginIgnoreSelected();
322:
323: try {
324: Tab beforeTab = index >= tabbedPanel.getTabCount() ? null
325: : tabbedPanel.getTabAt(index);
326: DockingWindow w = window.getContentWindow(this );
327: w.detach();
328: updateTab(w);
329: WindowTab tab = w.getTab();
330: int actualIndex = beforeTab == null ? tabbedPanel
331: .getTabCount() : tabbedPanel.getTabIndex(beforeTab);
332: tabbedPanel.insertTab(tab, actualIndex);
333: addWindow(w);
334: window.notifyListeners(ancestors);
335: return actualIndex;
336: } finally {
337: endIgnoreSelected();
338: endOptimize();
339: }
340: }
341:
342: protected boolean isChildShowingInRootWindow(DockingWindow child) {
343: return super .isChildShowingInRootWindow(child)
344: && child == getSelectedWindow();
345: }
346:
347: protected void showChildWindow(DockingWindow window) {
348: setSelectedTab(getChildWindowIndex(window));
349: super .showChildWindow(window);
350: }
351:
352: protected boolean childInsideTab() {
353: return true;
354: }
355:
356: protected void setTabWindowProperties(TabWindowProperties properties) {
357: getTabbedPanel().getProperties().addSuperObject(
358: properties.getTabbedPanelProperties());
359: }
360:
361: protected void clearFocus(View view) {
362: if (getSelectedWindow() != null) {
363: getSelectedWindow().clearFocus(view);
364: }
365: }
366:
367: protected DockingWindow getPreferredFocusChild() {
368: return getSelectedWindow() == null ? super
369: .getPreferredFocusChild() : getSelectedWindow();
370: }
371:
372: protected void clearChildrenFocus(DockingWindow child, View view) {
373: if (getSelectedWindow() != child)
374: clearFocus(view);
375: }
376:
377: protected int getTabAreaComponentCount() {
378: return 0;
379: }
380:
381: protected void updateTabAreaComponents() {
382: int ls = tabAreaComponents == null ? 0 : tabAreaComponents
383: .size();
384: JComponent[] components = new JComponent[ls
385: + getTabAreaComponentCount()];
386:
387: if (tabAreaComponents != null)
388: tabAreaComponents.toArray(components);
389:
390: getTabAreaComponents(ls, components);
391: getTabbedPanel().setTabAreaComponents(components);
392: }
393:
394: protected void getTabAreaComponents(int index,
395: JComponent[] components) {
396: }
397:
398: protected final boolean getIgnoreSelected() {
399: return ignoreSelected > 0;
400: }
401:
402: protected void tabSelected(WindowTab tab) {
403: if (!getIgnoreSelected() && tab != null) {
404: final RootWindow root = getRootWindow();
405:
406: if (root != null) {
407: // Anticipate the focus movement to avoid flicker
408: tab.setFocused(true);
409: root.addFocusedWindow(tab.getWindow());
410: FocusManager.focusWindow(tab.getWindow());
411: }
412: }
413:
414: if (!getIgnoreSelected())
415: fireTitleChanged();
416: }
417:
418: protected TabbedPanel getTabbedPanel() {
419: return tabbedPanel;
420: }
421:
422: public DockingWindow getChildWindow(int index) {
423: return ((WindowTab) tabbedPanel.getTabAt(index)).getWindow();
424: }
425:
426: protected DockingWindow getLocationWindow() {
427: return tabbedPanel.getTabCount() == 1 ? getChildWindow(0)
428: : this ;
429: }
430:
431: public int getChildWindowCount() {
432: return tabbedPanel.getTabCount();
433: }
434:
435: public Icon getIcon() {
436: DockingWindow window = getSelectedWindow();
437: return window != null ? window.getIcon()
438: : getChildWindowCount() > 0 ? getChildWindow(0)
439: .getIcon() : null;
440: }
441:
442: private void updateTab(DockingWindow window) {
443: window.getTab().setProperties(getTabProperties(window));
444: }
445:
446: private WindowTabProperties getTabProperties(DockingWindow window) {
447: WindowTabProperties properties = new WindowTabProperties(
448: getTabWindowProperties().getTabProperties());
449: properties.addSuperObject(window.getWindowProperties()
450: .getTabProperties());
451: return properties;
452: }
453:
454: protected void doReplace(DockingWindow oldWindow,
455: DockingWindow newWindow) {
456: beginIgnoreSelected();
457:
458: try {
459: Tab tab = oldWindow.getTab();
460: int tabIndex = tabbedPanel.getTabIndex(tab);
461:
462: boolean selected = tab.isSelected();
463: tabbedPanel.removeTab(tab);
464: tabbedPanel.insertTab(newWindow.getTab(), tabIndex);
465:
466: if (selected)
467: tabbedPanel.setSelectedTab(newWindow.getTab());
468:
469: updateTab(newWindow);
470: } finally {
471: endIgnoreSelected();
472: }
473: }
474:
475: protected void doRemoveWindow(DockingWindow window) {
476: beginIgnoreSelected();
477:
478: try {
479: WindowTab tab = window.getTab();
480: tab.unsetProperties();
481: tabbedPanel.removeTab(tab);
482: } finally {
483: endIgnoreSelected();
484: }
485: }
486:
487: private void beginIgnoreSelected() {
488: ignoreSelected++;
489: }
490:
491: private void endIgnoreSelected() {
492: ignoreSelected--;
493: }
494:
495: protected boolean isInsideTabArea(Point p2) {
496: return tabbedPanel.tabAreaContainsPoint(p2);
497: }
498:
499: protected DropAction acceptInteriorDrop(Point p,
500: DockingWindow window) {
501: if (getChildWindowCount() == 1 && window == getChildWindow(0)
502: && dragTab == null)
503: return null;
504:
505: Point p2 = SwingUtilities.convertPoint(this , p, tabbedPanel);
506:
507: if ((getRootWindow().getRootWindowProperties()
508: .getRecursiveTabsEnabled() || window
509: .getChildWindowCount() <= 1)
510: && isInsideTabArea(p2)) {
511: getRootWindow().setDragRectangle(null);
512:
513: if (window.getWindowParent() == this ) {
514: tabbedPanel.moveTab(window.getTab(), p2);
515: } else {
516: if (!getInsertTabDropFilter().acceptDrop(
517: new InsertTabDropInfo(window, this , p)))
518: return null;
519:
520: if (dragTab == null) {
521: dragTab = createGhostTab(window);
522: tabbedPanel.insertTab(dragTab, p2);
523: } else {
524: tabbedPanel.moveTab(dragTab, p2);
525: }
526: }
527:
528: return dropAction;
529: }
530:
531: return null;
532: }
533:
534: WindowTab createGhostTab(DockingWindow window) {
535: WindowTab tab = new WindowTab(window, true);
536: tab.setProperties(getTabProperties(window));
537: return tab;
538: }
539:
540: private void stopDrag() {
541: if (dragTab != null) {
542: tabbedPanel.removeTab(dragTab);
543: dragTab = null;
544: }
545: }
546:
547: protected boolean showsWindowTitle() {
548: return true;
549: }
550:
551: protected DockingWindow oldRead(ObjectInputStream in,
552: ReadContext context) throws IOException {
553: int size = in.readInt();
554: int selectedIndex = in.readInt();
555:
556: while (getChildWindowCount() > 0)
557: removeChildWindow(getChildWindow(0));
558:
559: for (int i = 0; i < size; i++) {
560: DockingWindow window = WindowDecoder.decodeWindow(in,
561: context);
562:
563: if (window != null)
564: addTab(window);
565: else if (i < selectedIndex)
566: selectedIndex--;
567: }
568:
569: super .oldRead(in, context);
570:
571: if (tabbedPanel.getTabCount() > 0) {
572: if (selectedIndex >= 0)
573: setSelectedTab(Math.min(tabbedPanel.getTabCount() - 1,
574: selectedIndex));
575:
576: return this ;
577: } else
578: return null;
579: }
580:
581: protected void write(ObjectOutputStream out, WriteContext context,
582: ViewWriter viewWriter) throws IOException {
583: out.writeInt(getChildWindowCount());
584:
585: for (int i = 0; i < getChildWindowCount(); i++)
586: getChildWindow(i).write(out, context, viewWriter);
587: }
588:
589: protected DockingWindow newRead(ObjectInputStream in,
590: ReadContext context, ViewReader viewReader)
591: throws IOException {
592: int size = in.readInt();
593:
594: while (getChildWindowCount() > 0)
595: removeChildWindow(getChildWindow(0));
596:
597: for (int i = 0; i < size; i++) {
598: DockingWindow window = WindowDecoder.decodeWindow(in,
599: context, viewReader);
600:
601: if (window != null)
602: addTab(window);
603: }
604:
605: updateSelectedTab();
606: return getChildWindowCount() > 0 ? this : null;
607: }
608:
609: protected void updateSelectedTab() {
610: WindowItem selectedItem = ((AbstractTabWindowItem) getWindowItem())
611: .getSelectedItem();
612:
613: for (int i = 0; i < getChildWindowCount(); i++) {
614: if (getChildWindow(i).getWindowItem().hasAncestor(
615: selectedItem)) {
616: setSelectedTab(i);
617: return;
618: }
619: }
620: }
621:
622: void setDraggedTabIndex(int index) {
623: draggedTabIndex = index;
624: }
625:
626: void removeWindowComponent(DockingWindow window) {
627: window.getTab().setContentComponent(null);
628: }
629:
630: void restoreWindowComponent(DockingWindow window) {
631: window.getTab().setContentComponent(window);
632: }
633:
634: }
|