KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > swing > tabcontrol > plaf > DefaultTabbedContainerUI


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.swing.tabcontrol.plaf;
21
22 import java.awt.AlphaComposite JavaDoc;
23 import java.awt.BorderLayout JavaDoc;
24 import java.awt.Component JavaDoc;
25 import java.awt.Composite JavaDoc;
26 import java.awt.Container JavaDoc;
27 import java.awt.Dimension JavaDoc;
28 import java.awt.Graphics JavaDoc;
29 import java.awt.Graphics2D JavaDoc;
30 import java.awt.GraphicsEnvironment JavaDoc;
31 import java.awt.Image JavaDoc;
32 import java.awt.Insets JavaDoc;
33 import java.awt.LayoutManager JavaDoc;
34 import java.awt.Point JavaDoc;
35 import java.awt.Polygon JavaDoc;
36 import java.awt.Rectangle JavaDoc;
37 import java.awt.event.ActionEvent JavaDoc;
38 import java.awt.event.ActionListener JavaDoc;
39 import java.awt.event.ComponentAdapter JavaDoc;
40 import java.awt.event.ComponentEvent JavaDoc;
41 import java.awt.event.ComponentListener JavaDoc;
42 import java.awt.event.HierarchyEvent JavaDoc;
43 import java.awt.event.HierarchyListener JavaDoc;
44 import java.awt.event.MouseEvent JavaDoc;
45 import java.awt.event.MouseListener JavaDoc;
46 import java.awt.geom.AffineTransform JavaDoc;
47 import java.awt.image.BufferedImage JavaDoc;
48 import java.beans.PropertyChangeEvent JavaDoc;
49 import java.beans.PropertyChangeListener JavaDoc;
50 import java.util.HashSet JavaDoc;
51 import java.util.Iterator JavaDoc;
52 import java.util.List JavaDoc;
53 import java.util.Set JavaDoc;
54 import javax.swing.JComponent JavaDoc;
55 import javax.swing.JPanel JavaDoc;
56 import javax.swing.SingleSelectionModel JavaDoc;
57 import javax.swing.SwingUtilities JavaDoc;
58 import javax.swing.Timer JavaDoc;
59 import javax.swing.UIManager JavaDoc;
60 import javax.swing.border.Border JavaDoc;
61 import javax.swing.event.ChangeEvent JavaDoc;
62 import javax.swing.event.ChangeListener JavaDoc;
63 import javax.swing.event.ListDataEvent JavaDoc;
64 import javax.swing.plaf.ComponentUI JavaDoc;
65 import org.netbeans.swing.tabcontrol.TabData;
66 import org.netbeans.swing.tabcontrol.TabDisplayer;
67 import org.netbeans.swing.tabcontrol.TabbedContainer;
68 import org.netbeans.swing.tabcontrol.TabbedContainerUI;
69 import org.netbeans.swing.tabcontrol.WinsysInfoForTabbed;
70 import org.netbeans.swing.tabcontrol.event.ArrayDiff;
71 import org.netbeans.swing.tabcontrol.event.ComplexListDataEvent;
72 import org.netbeans.swing.tabcontrol.event.ComplexListDataListener;
73 import org.netbeans.swing.tabcontrol.event.TabActionEvent;
74 import org.netbeans.swing.tabcontrol.event.VeryComplexListDataEvent;
75
76 /**
77  * Default UI implementation for tabbed containers. Manages installing the tab
78  * contentDisplayer and content contentDisplayer components, and the
79  * relationship between the data models and selection models of the UI.
80  * <p>
81  * Note that there is typically little reasons to subclass this - to affect the display
82  * or behavior of the tabs, implement {@link org.netbeans.swing.tabcontrol.TabDisplayerUI},
83  * the UI delegate for the embedded component which displays the tabs.
84  *
85  * @author Tim Boudreau
86  */

87 public class DefaultTabbedContainerUI extends TabbedContainerUI {
88     /**
89      * Action listener which receives actions from the tab displayer and propagates them through
90      * action listeners on the tab displayer, to allow clients to consume the events.
91      */

92     private ActionListener JavaDoc actionListener = null;
93     /**
94      * Flag to ensure listeners attached, since ComponentShown notifications are not always
95      * reliable.
96      */

97     /** UIManager key for the border of the tab displayer in editor ui type. */
98     public static final String JavaDoc KEY_EDITOR_CONTENT_BORDER = "TabbedContainer.editor.contentBorder"; //NOI18N
99
/** UIManager key for the border of the tab displayer in editor ui type */
100     public static final String JavaDoc KEY_EDITOR_TABS_BORDER = "TabbedContainer.editor.tabsBorder"; //NOI18N
101
/** UIManager key for the border of the entire tabbed container in editor ui type*/
102     public static final String JavaDoc KEY_EDITOR_OUTER_BORDER = "TabbedContainer.editor.outerBorder"; //NOI18N
103

104     /** UIManager key for the border of the tab displayer in view ui type. */
105     public static final String JavaDoc KEY_VIEW_CONTENT_BORDER = "TabbedContainer.view.contentBorder"; //NOI18N
106
/** UIManager key for the border of the tab displayer in view ui type. */
107     public static final String JavaDoc KEY_VIEW_TABS_BORDER = "TabbedContainer.view.tabsBorder"; //NOI18N
108
/** UIManager key for the border of the entire tabbed container in view ui type.*/
109     public static final String JavaDoc KEY_VIEW_OUTER_BORDER = "TabbedContainer.view.outerBorder"; //NOI18N
110

111     /** UIManager key for the border of the tab displayer in sliding ui type. */
112     public static final String JavaDoc KEY_SLIDING_CONTENT_BORDER = "TabbedContainer.sliding.contentBorder"; //NOI18N
113
/** UIManager key for the border of the tab displayer in sliding ui type */
114     public static final String JavaDoc KEY_SLIDING_TABS_BORDER = "TabbedContainer.sliding.tabsBorder"; //NOI18N
115
/** UIManager key for the border of the entire tabbed container in sliding ui type*/
116     public static final String JavaDoc KEY_SLIDING_OUTER_BORDER = "TabbedContainer.sliding.outerBorder"; //NOI18N
117

118     /** UIManager key for the border of the tab displayer in toolbar ui type. */
119     public static final String JavaDoc KEY_TOOLBAR_CONTENT_BORDER = "TabbedContainer.toolbar.contentBorder"; //NOI18N
120
/** UIManager key for the border of the tab displayer in toolbar ui type */
121     public static final String JavaDoc KEY_TOOLBAR_TABS_BORDER = "TabbedContainer.toolbar.tabsBorder"; //NOI18N
122
/** UIManager key for the border of the entire tabbed container in toolbar ui type*/
123     public static final String JavaDoc KEY_TOOLBAR_OUTER_BORDER = "TabbedContainer.toolbar.outerBorder"; //NOI18N
124

125     /** Component listener which listens on the container, and attaches/detaches listeners.
126      * <strong>do not alter the value in this field. To provide a different implementation,
127      * override the appropriate creation method.</strong>
128      **/

129     protected ComponentListener JavaDoc componentListener = null;
130     /** Change listener which tracks changes in the selection model and changes the displayed
131      * component to reflect the selected tab
132      * <strong>do not alter the value in this field. To provide a different implementation,
133      * override the appropriate creation method.</strong>
134      */

135     protected ChangeListener JavaDoc selectionListener = null;
136     /** Listener on the data model, which handles updating the contained components to keep them
137      * in sync with the contents of the data model.
138      * <strong>do not alter the value in this field. To provide a different implementation,
139      * override the appropriate creation method.</strong>
140      * @see TabbedContainer#setContentPolicy
141      */

142     protected ComplexListDataListener modelListener = null;
143     /**
144      * Layout manager which will handle layout of the tabbed container.
145      * <strong>do not alter the value in this field. To provide a different implementation,
146      * override the appropriate creation method.</strong>
147      */

148     protected LayoutManager JavaDoc contentDisplayerLayout = null;
149     /** Property change listener which detects changes on the tabbed container, such as its active state, which
150      * should be propagated to the tab displayer.
151      * <strong>do not alter the value in this field. To provide a different implementation,
152      * override the appropriate creation method.</strong>
153      */

154     protected PropertyChangeListener JavaDoc propertyChangeListener = null;
155     /**
156      * FxProvider which will provide transition effects when tabs are changed. By default, only used for
157      * TabbedContainer.TYPE_SLIDE tabs.
158      * <strong>do not alter the value in this field. To provide a different implementation,
159      * override the appropriate creation method.</strong>
160      */

161     protected FxProvider slideEffectManager = null;
162
163     /**
164      * The component which displays the selected component in the tabbed container.
165      * <strong>do not alter the value in this field. To provide a different implementation,
166      * override the appropriate creation method.</strong>
167      */

168     protected JComponent JavaDoc contentDisplayer = null;
169     /**
170      * The Displayer for the tabs. Normally an instance of <code>TabDisplayer</code>. To get the actual
171      * GUI component that is showing tabs, call its <code>getComponent()</code> method.
172      * <strong>do not alter the value in this field. To provide a different implementation,
173      * override the appropriate creation method.</strong>
174      */

175     protected TabDisplayer tabDisplayer = null;
176     
177     private HierarchyListener JavaDoc hierarchyListener = null;
178
179     /**
180      * Creates a new instance of DefaultTabbedContainerUI
181      */

182     public DefaultTabbedContainerUI(TabbedContainer c) {
183         super(c);
184     }
185
186     public static ComponentUI JavaDoc createUI(JComponent JavaDoc c) {
187         return new DefaultTabbedContainerUI((TabbedContainer) c);
188     }
189
190     /** This method is final. Subclasses which need to provide additional initialization should override
191      * <code>install()</code>
192      *
193      * @param c A JComponent, which must == the displayer field initialized in the constructor
194      */

195     public final void installUI(JComponent JavaDoc c) {
196         assert c == container;
197         container.setLayout(createLayout());
198         contentDisplayer = createContentDisplayer();
199         tabDisplayer = createTabDisplayer();
200         selectionListener = createSelectionListener();
201         modelListener = createModelListener();
202         componentListener = createComponentListener();
203         propertyChangeListener = createPropertyChangeListener();
204         contentDisplayerLayout = createContentDisplayerLayout();
205         slideEffectManager = createFxProvider();
206         actionListener = createDisplayerActionListener();
207         container.setLayout(createLayout());
208         hierarchyListener = new ContainerHierarchyListener();
209         forward = new ForwardingMouseListener(container);
210         installContentDisplayer();
211         installTabDisplayer();
212         installBorders();
213         installListeners();
214         install();
215         //#41681, #44278, etc. FOCUS related. the UI needs to be the first listener to be notified
216
// that the selection has changed. Otherwise strange focus effects kick in, eg. when the winsys snapshot gets undated beforehand..
217
tabDisplayer.getSelectionModel().addChangeListener(selectionListener);
218
219     }
220     
221     
222     /** Used by unit tests */
223     TabDisplayer getTabDisplayer() {
224         return tabDisplayer;
225     }
226
227     private MouseListener JavaDoc forward = null;
228
229     /** This method is final. Subclasses which need to provide additional initialization should override
230      * <code>uninstall()</code>
231      *
232      * @param c A JComponent, which must == the displayer field initialized in the constructor
233      */

234     public final void uninstallUI(JComponent JavaDoc c) {
235         assert c == container;
236         tabDisplayer.getSelectionModel().removeChangeListener(selectionListener);
237         uninstall();
238         uninstallListeners();
239         uninstallDisplayers();
240
241         container = null;
242         contentDisplayer = null;
243         tabDisplayer = null;
244         selectionListener = null;
245         modelListener = null;
246         componentListener = null;
247         propertyChangeListener = null;
248         contentDisplayerLayout = null;
249         actionListener = null;
250         forward = null;
251     }
252
253     /** Subclasses may override this method to do anything they need to do on installUI(). It will
254      * be called after listeners, the displayer, etc. (all pseudo-final protected fields) have been
255      * initialized.
256      */

257     protected void install() {
258
259     }
260
261     /** Subclasses may override this method to do anything they need to do on uninstallUI(). It will
262      * be called before the protected fields of the instance have been nulled.
263      */

264     protected void uninstall() {
265
266     }
267     
268     protected boolean uichange() {
269         installBorders();
270         return false;
271     }
272
273     /**
274      * Installs the content displayer component and its layout manager
275      */

276     protected void installContentDisplayer() {
277         contentDisplayer.setLayout(contentDisplayerLayout);
278         container.add(contentDisplayer, BorderLayout.CENTER);
279     }
280
281     /**
282      * Installs the tab displayer component into the container. By default, installs it using the
283      * constraint <code>BorderLayout.NORTH</code>.
284      */

285     protected void installTabDisplayer() {
286         container.add(tabDisplayer, BorderLayout.NORTH);
287         tabDisplayer.registerShortcuts(container);
288     }
289
290     /**
291      * Installs borders on the container, content displayer and tab displayer
292      */

293     protected void installBorders() {
294         String JavaDoc tabsKey;
295         String JavaDoc contentKey;
296         String JavaDoc outerKey;
297         switch (container.getType()) {
298             case TabbedContainer.TYPE_EDITOR :
299                 tabsKey = KEY_EDITOR_TABS_BORDER;
300                 contentKey = KEY_EDITOR_CONTENT_BORDER;
301                 outerKey = KEY_EDITOR_OUTER_BORDER;
302                 break;
303             case TabbedContainer.TYPE_VIEW :
304                 tabsKey = KEY_VIEW_TABS_BORDER;
305                 contentKey = KEY_VIEW_CONTENT_BORDER;
306                 outerKey = KEY_VIEW_OUTER_BORDER;
307                 break;
308             case TabbedContainer.TYPE_SLIDING :
309                 tabsKey = KEY_SLIDING_TABS_BORDER;
310                 contentKey = KEY_SLIDING_CONTENT_BORDER;
311                 outerKey = KEY_SLIDING_OUTER_BORDER;
312                 break;
313             case TabbedContainer.TYPE_TOOLBAR :
314                 tabsKey = KEY_TOOLBAR_TABS_BORDER;
315                 contentKey = KEY_TOOLBAR_CONTENT_BORDER;
316                 outerKey = KEY_TOOLBAR_OUTER_BORDER;
317                 break;
318             default :
319                 throw new IllegalStateException JavaDoc ("Unknown type: "
320                     + container.getType());
321         }
322         try {
323             Border JavaDoc b = (Border JavaDoc) UIManager.get (contentKey);
324             contentDisplayer.setBorder(b);
325             b = (Border JavaDoc) UIManager.get (tabsKey);
326             tabDisplayer.setBorder(b);
327             b = (Border JavaDoc) UIManager.get (outerKey);
328             container.setBorder(b);
329         } catch (ClassCastException JavaDoc cce) {
330             System.err.println ("Expected a border from UIManager for "
331                 + tabsKey + "," + contentKey + "," + outerKey);
332         }
333     }
334
335     /**
336      * Installs a component listener on the component. Listeners on the data
337      * model and selection model are installed when the component is shown, as
338      * detected by the component listener.
339      */

340     protected void installListeners() {
341         container.addComponentListener(componentListener);
342         container.addHierarchyListener (hierarchyListener);
343         //Allow mouse events to be forwarded as if they came from the
344
//container
345
tabDisplayer.addMouseListener (forward);
346         contentDisplayer.addMouseListener (forward);
347     }
348
349     /**
350      * Begin listening to the model for changes in the selection, which should
351      * cause us to update the displayed component in the content
352      * contentDisplayer. Listening starts when the component is first shown, and
353      * stops when it is hidden; if you override <code>createComponentListener()</code>,
354      * you will need to call this method when the component is shown.
355      */

356     protected void attachModelAndSelectionListeners() {
357         container.getModel().addComplexListDataListener(modelListener);
358         container.addPropertyChangeListener(propertyChangeListener);
359         tabDisplayer.setActive (container.isActive());
360         tabDisplayer.addActionListener (actionListener);
361     }
362
363     /**
364      * Stop listening to the model for changes in the selection, which should
365      * cause us to update the displayed component in the content
366      * contentDisplayer, and changes in the data model which can affect the
367      * displayed component. Listening starts when the component is first shown,
368      * and stops when it is hidden; if you override <code>createComponentListener()</code>,
369      * you will need to call this method when the component is hidden.
370      */

371     protected void detachModelAndSelectionListeners() {
372         container.getModel().removeComplexListDataListener(modelListener);
373         container.removePropertyChangeListener(propertyChangeListener);
374         tabDisplayer.removeActionListener (actionListener);
375     }
376
377     /**
378      * Uninstalls the component listener installed in <code>installListeners()</code>
379      */

380     protected void uninstallListeners() {
381         container.removeComponentListener(componentListener);
382         container.removeHierarchyListener (hierarchyListener);
383         componentListener = null;
384         propertyChangeListener = null;
385         tabDisplayer.removeMouseListener (forward);
386         contentDisplayer.removeMouseListener (forward);
387     }
388
389     /**
390      * Uninstalls and nulls references to the content contentDisplayer and tab
391      * contentDisplayer, and removes all components from the content
392      * contentDisplayer.
393      */

394     protected void uninstallDisplayers() {
395         container.remove(contentDisplayer);
396         container.remove(tabDisplayer);
397         tabDisplayer.unregisterShortcuts(container);
398         contentDisplayer.removeAll();
399         contentDisplayer = null;
400         tabDisplayer = null;
401     }
402
403     /**
404      * Create the component which will display the tabs.
405      */

406     protected TabDisplayer createTabDisplayer() {
407         TabDisplayer result = null;
408         WinsysInfoForTabbed winsysInfo = container.getWinsysInfo();
409         if (winsysInfo != null) {
410             result = new TabDisplayer(
411                     container.getModel(), container.getType(), winsysInfo);
412         } else {
413             result = new TabDisplayer(
414                     container.getModel(), container.getType(), container.getLocationInformer());
415         }
416         result.setName("Tab Displayer"); //NOI18N
417
return result;
418     }
419
420     /**
421      * Create the component which will contain the content (the components which
422      * correspond to tabs). The default implementation simply returns a
423      * vanilla, unadorned <code>JPanel</code>.
424      */

425     protected JPanel JavaDoc createContentDisplayer() {
426         JPanel JavaDoc result = new JPanel JavaDoc();
427         result.setName ("Content displayer"); //NOI18N
428
return result;
429     }
430
431     /**
432      * Create an FxProvider instance which will provide transition effects when tabs are selected.
433      * By default creates a no-op instance for all displayer types except TYPE_SLIDING.
434      *
435      * @return An instance of FxProvider
436      */

437     protected FxProvider createFxProvider() {
438         if (NO_EFFECTS || (tabDisplayer.getType() != TabDisplayer.TYPE_SLIDING && !EFFECTS_EVERYWHERE)) {
439             return new NoOpFxProvider();
440         } else {
441             if (ADD_TO_GLASSPANE) {
442                 return new LiveComponentSlideFxProvider();
443             } else {
444                 return new ImageSlideFxProvider();
445             }
446         }
447     }
448
449     /**
450      * Creates the content contentDisplayer's layout manager, responsible for
451      * ensuring that the correct component is on top and is the only one
452      * showing
453      */

454     protected LayoutManager JavaDoc createContentDisplayerLayout() {
455         return new StackLayout();
456     }
457
458     /**
459      * Create the layout manager that will manage the layout of the
460      * TabbedContainer. A TabbedContainer contains two components - the tabs
461      * contentDisplayer, and the component contentDisplayer.
462      * <p/>
463      * The layout manager determines the position of the tabs relative to the
464      * contentDisplayer component which displays the tab contents.
465      * <p/>
466      * The default implementation uses BorderLayout. If you override this, you
467      * should probably override <code>installDisplayer()</code> as well.
468      */

469     protected LayoutManager JavaDoc createLayout() {
470         if (container.getType() == TabbedContainer.TYPE_SLIDING) {
471             return new SlidingTabsLayout();
472         } else if (container.getType() == TabbedContainer.TYPE_TOOLBAR) {
473             return new ToolbarTabsLayout();
474         } else {
475             return new BorderLayout JavaDoc();
476         }
477     }
478
479     /**
480      * Create a component listener responsible for initializing the
481      * contentDisplayer component when the tabbed container is shown
482      */

483     protected ComponentListener JavaDoc createComponentListener() {
484         return new ContainerComponentListener();
485     }
486
487     /**
488      * Create a property change listener which will update the tab displayer in
489      * accordance with property changes on the container. Currently the only
490      * property change of interest is calls to <code>TabbedContainer.setActive()</code>,
491      * which simply cause the active state to be set on the displayer.
492      */

493     protected PropertyChangeListener JavaDoc createPropertyChangeListener() {
494         return new ContainerPropertyChangeListener();
495     }
496
497     /** Creates an action listener to catch actions performed on the tab displayer
498      * and forward them to listeners on the container for consumption.
499      *
500      * @return An action listener
501      */

502     private ActionListener JavaDoc createDisplayerActionListener() {
503         return new DisplayerActionListener();
504     }
505
506     /**
507      * Ensures that the component the selection model says is selected is the
508      * one that is showing.
509      */

510     protected void ensureSelectedComponentIsShowing() {
511         int i = tabDisplayer.getSelectionModel().getSelectedIndex();
512         if (i != -1) {
513             TabData td = container.getModel().getTab(i);
514             showComponent(toComp(td));
515         }
516     }
517     
518     /** Convenience method for fetching a component from a TabData object
519      * via the container's ComponentConverter */

520     protected final Component JavaDoc toComp (TabData data) {
521         return container.getComponentConverter().getComponent(data);
522     }
523
524     /**
525      * Shows the passed component. <strong>This method does not communicate with
526      * the data model in any way shape or form, it just moves the passed
527      * component to the front. It should only be called in response to an event
528      * from the data model or selection model.
529      * <p/>
530      * If you override <code>createContentDisplayerLayoutModel()</code> to
531      * provide your own layout manager to arrange the displayed component, you
532      * need to override this to tell the layout (or do whatever is needed) to
533      * change the component that is shown.
534      *
535      * @param c The component to be shown
536      * @return The previously showing component, or null if no change was made
537      */

538     protected Component JavaDoc showComponent(Component JavaDoc c) {
539         if (contentDisplayerLayout instanceof StackLayout) {
540             StackLayout stack = ((StackLayout) contentDisplayerLayout);
541             Component JavaDoc last = stack.getVisibleComponent();
542             stack.showComponent(c, contentDisplayer);
543             if (c != null) {
544                 Integer JavaDoc offset = (Integer JavaDoc)((JComponent JavaDoc)c).getClientProperty("MultiViewBorderHack.topOffset");
545                 contentDisplayer.putClientProperty("MultiViewBorderHack.topOffset", offset);
546             } else {
547                 contentDisplayer.putClientProperty("MultiViewBorderHack.topOffset", null);
548             }
549             if (last != c) {
550                 maybeRemoveLastComponent(last);
551                 return last;
552             }
553         }
554         return null;
555     }
556
557     /**
558      * Shows a component in the control, using the <code>FxProvider</code> created in
559      * <code>createFxProvider()</code> to manage showing it. Equivalent to calling <code>showComponent</code>,
560      * but there may be a delay while the effect is performed. If no <code>FxProvider</code> is installed,
561      * this will simply delegate to <code>showComponent</code>; if not, the <code>FxProvider</code> is expected
562      * to do that when its effect is completed.
563      *
564      * @param c The component to be shown.
565      */

566     protected final void showComponentWithFxProvider (Component JavaDoc c) {
567         if (slideEffectManager == null || !container.isShowing() || (!(c instanceof JComponent JavaDoc))) {
568             Component JavaDoc last = showComponent (c);
569             maybeRemoveLastComponent (last);
570         } else {
571             slideEffectManager.start((JComponent JavaDoc) c, container.getRootPane(),
572                 tabDisplayer.getClientProperty(TabDisplayer.PROP_ORIENTATION));
573         }
574     }
575
576     /**
577      * Removes the passed component from the AWT hierarchy if the container's content policy is
578      * CONTENT_POLICY_ADD_ONLY_SELECTED.
579      *
580      * @param c The component that should be removed
581      */

582     private final void maybeRemoveLastComponent (Component JavaDoc c) {
583         if (c != null && container.getContentPolicy() == TabbedContainer.CONTENT_POLICY_ADD_ONLY_SELECTED) {
584             contentDisplayer.remove (c);
585         }
586     }
587
588     /**
589      * Fills contentDisplayer container with components retrieved from model.
590      */

591     protected void initDisplayer() {
592         if (container.getContentPolicy() == TabbedContainer.CONTENT_POLICY_ADD_ALL) {
593             List JavaDoc tabs = container.getModel().getTabs();
594             Component JavaDoc curC = null;
595             for (Iterator JavaDoc iter = tabs.iterator(); iter.hasNext();) {
596                 curC = toComp ((TabData) iter.next());
597                 // string parameter is needed for StackLayout to kick in correctly
598
contentDisplayer.add(curC, "");
599             }
600         } else {
601             int i = tabDisplayer.getSelectionModel().getSelectedIndex();
602             if (i != -1) {
603                 TabData td = container.getModel().getTab(i);
604                 contentDisplayer.add(toComp(td), "");
605             }
606         }
607         updateActiveState();
608     }
609
610     /**
611      * Create a listener for the TabDataModel. This listener is responsible for
612      * keeping the state of the contained components in the displayer in sync
613      * with the contents of the data model. Note that it is not necessary for
614      * this listener to adjust the selection - DefaultTabSelectionModel handles
615      * cases such as removing the selected component appropriately, so if such a
616      * model change happens, a selection change will be immediately forthcoming
617      * to handle it.
618      * <p/>
619      * Note that it is important that this listener be added to the data model
620      * <i>after</i> the DefaultSelectionModel has added its listener. It is
621      * important to create the displayer component before adding this listener.
622      * Some support for privilged listeners may be added to DefaultTabDataModel
623      * in the future to avoid this issue entirely.
624      */

625     protected ComplexListDataListener createModelListener() {
626         return new ModelListener();
627     }
628
629     /**
630      * Create a ChangeListener which will listen to the selection model of the
631      * tab displayer, and update the displayed component in the displayer when
632      * the selection model changes.
633      */

634     protected ChangeListener JavaDoc createSelectionListener() {
635         return new SelectionListener();
636     }
637
638     /** Sets the active state of the displayer to match that of the container */
639     private void updateActiveState() {
640         //#45630 - more of a hack than a fix.
641
//apparently uninstallUI() was called before the the ContainerPropertyChangeListener instance was removed in
642
//ContainerHierarchyListener's hierarchyChanged method.
643
// for such case the property change should be a noop.
644
TabDisplayer displ = tabDisplayer;
645         TabbedContainer cont = container;
646         if (displ != null && cont != null) {
647             displ.setActive(cont.isActive());
648         }
649         
650     }
651
652     public Rectangle JavaDoc getTabRect(int tab, Rectangle JavaDoc r) {
653         if (r == null) {
654             r = new Rectangle JavaDoc();
655         }
656         tabDisplayer.getTabRect(tab, r);
657         Point JavaDoc p = tabDisplayer.getLocation();
658         r.x += p.x;
659         r.y += p.y;
660         return r;
661     }
662     
663     protected void requestAttention (int tab) {
664         tabDisplayer.requestAttention (tab);
665     }
666     
667     protected void cancelRequestAttention (int tab) {
668         tabDisplayer.cancelRequestAttention(tab);
669     }
670     
671     public void setShowCloseButton (boolean val) {
672         tabDisplayer.setShowCloseButton(val);
673     }
674     
675     public boolean isShowCloseButton () {
676         return tabDisplayer.isShowCloseButton();
677     }
678
679     /**
680      * Scroll pane-like border, good general border around windows. Used if no
681      * border is provided via UIDefaults.
682      */

683     private static final class DefaultWindowBorder implements Border JavaDoc {
684         private static final Insets JavaDoc insets = new Insets JavaDoc(1, 1, 2, 2);
685
686         public void paintBorder(Component JavaDoc c, Graphics JavaDoc g, int x, int y, int w,
687                                 int h) {
688             g.translate(x, y);
689
690             g.setColor(UIManager.getColor("controlShadow")); //NOI18N
691
g.drawRect(0, 0, w - 2, h - 2);
692             g.setColor(UIManager.getColor("controlHighlight")); //NOI18N
693
g.drawLine(w - 1, 1, w - 1, h - 1);
694             g.drawLine(1, h - 1, w - 1, h - 1);
695
696             g.translate(-x, -y);
697         }
698
699         public Insets JavaDoc getBorderInsets(Component JavaDoc c) {
700             return insets;
701         }
702
703         public boolean isBorderOpaque() {
704             return true;
705         }
706     } // end of DefaultWindowBorder
707

708     protected class ContainerPropertyChangeListener implements PropertyChangeListener JavaDoc {
709         
710         public void propertyChange(PropertyChangeEvent JavaDoc evt) {
711             if (TabbedContainer.PROP_ACTIVE.equals(evt.getPropertyName())) {
712                 updateActiveState();
713             }
714         }
715     }
716     
717     /** Checks the position of the tabbed container relative to its parent
718      * window, and potentially updates its orientation client property.
719      *
720      * @see TabDisplayer#PROP_ORIENTATION
721      */

722     protected final void updateOrientation() {
723         if (!container.isDisplayable()) {
724             return;
725         }
726         if (Boolean.FALSE.equals(container.getClientProperty (TabbedContainer.PROP_MANAGE_TAB_POSITION))) {
727             //The client has specified that it does not want automatic management
728
//of the displayer orientation
729
return;
730         }
731         Object JavaDoc currOrientation = tabDisplayer.getClientProperty(TabDisplayer.PROP_ORIENTATION);
732         Container JavaDoc window = container.getTopLevelAncestor();
733
734         Rectangle JavaDoc containerBounds = container.getBounds();
735         containerBounds = SwingUtilities.convertRectangle(container, containerBounds, window);
736
737         boolean longestIsVertical = containerBounds.width < containerBounds.height;
738
739         int distanceToLeft = containerBounds.x;
740         int distanceToTop = containerBounds.y;
741         int distanceToRight = window.getWidth() - (containerBounds.x + containerBounds.width);
742         int distanceToBottom = window.getHeight() - (containerBounds.y + containerBounds.height);
743
744         Object JavaDoc orientation;
745         if (!longestIsVertical) {
746             if (distanceToBottom > distanceToTop) {
747                 orientation = TabDisplayer.ORIENTATION_NORTH;
748             } else {
749                 orientation = TabDisplayer.ORIENTATION_SOUTH;
750             }
751         } else {
752             if (distanceToLeft > distanceToRight) {
753                 orientation = TabDisplayer.ORIENTATION_EAST;
754             } else {
755                 orientation = TabDisplayer.ORIENTATION_WEST;
756             }
757         }
758
759         if (currOrientation != orientation) {
760             tabDisplayer.putClientProperty(
761                 TabDisplayer.PROP_ORIENTATION, orientation);
762             container.validate();
763         }
764     }
765
766     public int tabForCoordinate(Point JavaDoc p) {
767         p = SwingUtilities.convertPoint(container, p, tabDisplayer);
768         return tabDisplayer.tabForCoordinate(p);
769     }
770
771     public void makeTabVisible(int tab) {
772         tabDisplayer.makeTabVisible (tab);
773     }
774
775     public SingleSelectionModel JavaDoc getSelectionModel() {
776         return tabDisplayer.getSelectionModel();
777     }
778
779     public Image JavaDoc createImageOfTab(int idx) {
780         return tabDisplayer.getUI().createImageOfTab(idx);
781     }
782
783     public Polygon JavaDoc getExactTabIndication(int idx) {
784         Polygon JavaDoc result = tabDisplayer.getUI().getExactTabIndication(idx);
785         scratchPoint.setLocation(0,0);
786         Point JavaDoc p = SwingUtilities.convertPoint(tabDisplayer, scratchPoint, container);
787         result.translate (-p.x, -p.y);
788         return appendContentBoundsTo(result);
789     }
790
791     private Point JavaDoc scratchPoint = new Point JavaDoc();
792     public Polygon JavaDoc getInsertTabIndication(int idx) {
793         Polygon JavaDoc result = tabDisplayer.getUI().getInsertTabIndication(idx);
794         scratchPoint.setLocation(0,0);
795         Point JavaDoc p = SwingUtilities.convertPoint(tabDisplayer, scratchPoint, container);
796         result.translate (-p.x, -p.y);
797         return appendContentBoundsTo(result);
798     }
799
800     private Polygon JavaDoc appendContentBoundsTo (Polygon JavaDoc p) {
801         int width = contentDisplayer.getWidth();
802         int height = contentDisplayer.getHeight();
803
804         int[] xpoints = new int[p.npoints + 4];
805         int[] ypoints = new int[xpoints.length];
806
807         //XXX not handling this correctly for non-top orientations
808

809         int pos = 0;
810         Object JavaDoc orientation = tabDisplayer.getClientProperty (TabDisplayer.PROP_ORIENTATION);
811
812         int tabsHeight = tabDisplayer.getHeight();
813         if (orientation == null || orientation == TabDisplayer.ORIENTATION_NORTH) {
814
815             xpoints[pos] = 0;
816             ypoints[pos] = tabsHeight;
817
818             pos++;
819
820             xpoints[pos] = p.xpoints[p.npoints-1];
821             ypoints[pos] = tabsHeight;
822             pos++;
823
824             for (int i=0; i < p.npoints-2; i++) {
825                 xpoints [pos] = p.xpoints[i];
826                 ypoints [pos] = p.ypoints[i];
827                 pos++;
828             }
829
830             xpoints[pos] = xpoints[pos-1];
831             ypoints[pos] = tabsHeight;
832
833             pos++;
834
835             xpoints[pos] = width - 1;
836             ypoints[pos] = tabsHeight;
837
838             pos++;
839
840             xpoints[pos] = width - 1;
841             ypoints[pos] = height -1;
842
843             pos++;
844
845             xpoints[pos] = 0;
846             ypoints[pos] = height - 1;
847         } else if (orientation == TabDisplayer.ORIENTATION_SOUTH) {
848             int yxlate = contentDisplayer.getHeight() * 2;
849
850             xpoints[pos] = 0;
851             ypoints[pos] = 0;
852
853             pos++;
854
855             xpoints[pos] = container.getWidth();
856             ypoints[pos] = 0;
857
858             pos++;
859
860             xpoints[pos] = container.getWidth();
861             ypoints[pos] = container.getHeight() - tabsHeight;
862
863             pos++;
864
865             int upperRight = 0;
866             //Search backward for the upper right corner - we only know
867
//the location of the left upper corner
868
int highestFound = Integer.MIN_VALUE;
869             for (int i = p.npoints-2; i >= 0; i--) {
870                 if (highestFound < p.ypoints[i]) {
871                     upperRight = i;
872                     highestFound = p.ypoints[i];
873                 } else if (highestFound == p.ypoints[i]) {
874                     break;
875                 }
876             }
877
878             int curr = upperRight-1;
879             for (int i=p.npoints-1; i >= 0; i--) {
880                 xpoints[pos] = p.xpoints[curr];
881                 if (ypoints[pos] == highestFound) {
882                     ypoints[pos] = Math.min (tabDisplayer.getLocation().y, p.ypoints[curr] + yxlate);
883                 } else {
884                     ypoints[pos] = p.ypoints[curr] + yxlate;
885                 }
886                 pos++;
887                 curr++;
888                 if (curr == p.npoints-1) {
889                     curr = 0;
890                 }
891             }
892
893             xpoints[pos] = 0;
894             ypoints[pos] = container.getHeight() - tabsHeight;
895         } else {
896             //Punt on side tabs for now
897
xpoints = p.xpoints;
898             ypoints = p.ypoints;
899         }
900
901         Polygon JavaDoc result = new EqualPolygon (xpoints, ypoints, xpoints.length);
902         return result;
903     }
904
905     public Rectangle JavaDoc getContentArea() {
906         return contentDisplayer.getBounds();
907     }
908
909     public Rectangle JavaDoc getTabsArea() {
910         return tabDisplayer.getBounds();
911     }
912
913     public int dropIndexOfPoint(Point JavaDoc p) {
914         Point JavaDoc p2 = SwingUtilities.convertPoint(container, p, tabDisplayer);
915         return tabDisplayer.getUI().dropIndexOfPoint (p2);
916     }
917
918     public Rectangle JavaDoc getTabsArea(Rectangle JavaDoc dest) {
919         return tabDisplayer.getBounds();
920     }
921
922     /**
923      * A ComponentListener which listens for show/hide to add and remove the
924      * selection and model listeners
925      */

926     protected class ContainerComponentListener extends ComponentAdapter JavaDoc {
927         public ContainerComponentListener() {
928         }
929         
930         public void componentMoved (ComponentEvent JavaDoc e) {
931             if (container.getType() == TabbedContainer.TYPE_SLIDING) {
932                 updateOrientation();
933             }
934         }
935         
936         public void componentResized (ComponentEvent JavaDoc e) {
937             if (container.getType() == TabbedContainer.TYPE_SLIDING) {
938                 updateOrientation();
939             }
940         }
941     }
942     
943     private boolean bug4924561knownShowing = false;
944     /**
945      * Calls <code>initDisplayer()</code>, then <code>attachModelAndSelectionListeners</code>,
946      * then <code>ensureSelectedComponentIsShowing</code>
947      */

948     private class ContainerHierarchyListener implements HierarchyListener JavaDoc {
949         public ContainerHierarchyListener() {
950         }
951         
952         public void hierarchyChanged(HierarchyEvent JavaDoc e) {
953             if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0) {
954                 boolean showing = container.isShowing();
955                 if (showing != bug4924561knownShowing) {
956                     if (container.isShowing()) {
957                         initDisplayer();
958                         attachModelAndSelectionListeners();
959                         ensureSelectedComponentIsShowing();
960                         if (container.getType() == TabbedContainer.TYPE_SLIDING) {
961                             updateOrientation();
962                         }
963                     } else {
964                         detachModelAndSelectionListeners();
965                         if (container.getType() == TabbedContainer.TYPE_SLIDING) {
966                             updateOrientation();
967                         }
968                     }
969                 }
970                 bug4924561knownShowing = showing;
971             }
972         }
973     }
974     
975     private class ToolbarTabsLayout implements LayoutManager JavaDoc {
976         
977         public void layoutContainer(Container JavaDoc container) {
978             Dimension JavaDoc tabSize = tabDisplayer.getPreferredSize();
979             Insets JavaDoc ins = container.getInsets();
980             int w = container.getWidth() - (ins.left + ins.right);
981             tabDisplayer.setBounds (ins.left, ins.top,
982                 w,
983                 tabSize.height);
984             contentDisplayer.setBounds(ins.left,
985                 ins.top + tabSize.height, w,
986                 container.getHeight() - (ins.top + ins.bottom + tabSize.height));
987         }
988         
989         public Dimension JavaDoc minimumLayoutSize(Container JavaDoc container) {
990             Dimension JavaDoc tabSize = tabDisplayer.getMinimumSize();
991             Dimension JavaDoc contentSize = contentDisplayer.getMinimumSize();
992             Insets JavaDoc ins = container.getInsets();
993             Dimension JavaDoc result = new Dimension JavaDoc(ins.left + ins.top, ins.right + ins.bottom);
994             result.width += Math.max (tabSize.width, contentSize.width);
995             result.height += tabSize.height + contentSize.height;
996             return result;
997         }
998         
999         public Dimension JavaDoc preferredLayoutSize(Container JavaDoc container) {
1000            Dimension JavaDoc tabSize = tabDisplayer.getPreferredSize();
1001            Dimension JavaDoc contentSize = contentDisplayer.getPreferredSize();
1002            Insets JavaDoc ins = container.getInsets();
1003            Dimension JavaDoc result = new Dimension JavaDoc(ins.left + ins.top, ins.right + ins.bottom);
1004            result.width += Math.max (tabSize.width, contentSize.width);
1005            result.height += tabSize.height + contentSize.height;
1006            return result;
1007        }
1008        
1009        public void removeLayoutComponent(Component JavaDoc component) {
1010            //do nothing
1011
}
1012        
1013        public void addLayoutComponent(String JavaDoc str, Component JavaDoc component) {
1014            //do nothing
1015
}
1016    }
1017
1018    /**
1019     * A ChangeListener which updates the component displayed in the content
1020     * displayer with the selected component in the tab displayer's selection
1021     * model
1022     */

1023    protected class SelectionListener implements ChangeListener JavaDoc {
1024        public SelectionListener() {
1025        }
1026
1027        public void stateChanged(ChangeEvent JavaDoc e) {
1028            if (container.isShowing()) {
1029                int idx = tabDisplayer.getSelectionModel().getSelectedIndex();
1030                if (idx != -1) {
1031                    Component JavaDoc c = toComp(container.getModel().getTab(idx));
1032                    c.setBounds(0, 0, contentDisplayer.getWidth(),
1033                                contentDisplayer.getHeight());
1034                    showComponentWithFxProvider(c);
1035                } else {
1036                    showComponent (null);
1037                }
1038            }
1039        }
1040    }
1041
1042    /**
1043     * This class does the heavy lifting of keeping the content of the content
1044     * displayer up-to-date with the contents of the data model.
1045     */

1046    protected class ModelListener implements ComplexListDataListener {
1047        public ModelListener() {
1048        }
1049
1050        /**
1051         * DefaultTabDataModel will always call this method with an instance of
1052         * ComplexListDataEvent.
1053         */

1054        public void contentsChanged(ListDataEvent JavaDoc e) {
1055            //Only need to reread components on setTab (does winsys even use it?)
1056
if (e instanceof ComplexListDataEvent) {
1057                ComplexListDataEvent clde = (ComplexListDataEvent) e;
1058                int index = clde.getIndex0();
1059                if (clde.isUserObjectChanged() && index != -1) {
1060                    Component JavaDoc comp = contentDisplayer.getComponent(
1061                            index);
1062                    Component JavaDoc nue = toComp(tabDisplayer.getModel().getTab(index));
1063                    contentDisplayer.remove(comp);
1064                    
1065                    boolean add =
1066                        container.getContentPolicy() ==
1067                        TabbedContainer.CONTENT_POLICY_ADD_ALL ||
1068                        index ==
1069                        container.getSelectionModel().getSelectedIndex();
1070                    
1071                    if (add) {
1072                        contentDisplayer.add(nue, index);
1073                    }
1074                }
1075                if (clde.isTextChanged()) {
1076                    maybeMakeSelectedTabVisible(clde);
1077                }
1078            }
1079        }
1080
1081        /**
1082         * This method is called to scroll the selected tab into view if its
1083         * title changes (it may be scrolled offscreen). NetBeans' editor uses
1084         * this to ensure that the user can see what file they're editing when
1085         * the user starts typing (this triggers a * being appended to the tab
1086         * title, thus triggering this call).
1087         */

1088        private void maybeMakeSelectedTabVisible(ComplexListDataEvent clde) {
1089            if (!container.isShowing() || container.getWidth() < 10) {
1090                //Java module fires icon changes from badging before the
1091
//main window has been validated for the first time
1092
return;
1093            }
1094            if (tabDisplayer.getType() == TabDisplayer.TYPE_EDITOR) {
1095                int idx = tabDisplayer.getSelectionModel().getSelectedIndex();
1096                //If more than one tab changed, it's probably not an event we want.
1097
//Only do this if there is only one.
1098
if ((clde.getIndex0() == clde.getIndex1())
1099                        && clde.getIndex0() == idx) {
1100                    (tabDisplayer).makeTabVisible(idx);
1101                }
1102            }
1103        }
1104
1105
1106        public void intervalAdded(ListDataEvent JavaDoc e) {
1107            if (container.getContentPolicy() == TabbedContainer.CONTENT_POLICY_ADD_ALL) {
1108                Component JavaDoc curC = null;
1109                for (int i = e.getIndex0(); i <= e.getIndex1(); i++) {
1110                    curC = toComp(container.getModel().getTab(i));
1111                    contentDisplayer.add(curC, "");
1112                }
1113            }
1114        }
1115
1116        public void intervalRemoved(ListDataEvent JavaDoc e) {
1117            // we know that it must be complex data event
1118
ComplexListDataEvent clde = (ComplexListDataEvent) e;
1119            TabData[] removedTabs = clde.getAffectedItems();
1120            Component JavaDoc curComp;
1121            for (int i = 0; i < removedTabs.length; i++) {
1122                curComp = toComp(removedTabs[i]);
1123                contentDisplayer.remove(curComp);
1124            }
1125        }
1126
1127        public void indicesAdded(ComplexListDataEvent e) {
1128            Component JavaDoc curC = null;
1129            if (container.getContentPolicy() == TabbedContainer.CONTENT_POLICY_ADD_ALL) {
1130                int[] indices = e.getIndices();
1131                for (int i = 0; i < indices.length; i++) {
1132                    curC = toComp(container.getModel().getTab(indices[i]));
1133                    contentDisplayer.add(curC, "");
1134                }
1135            }
1136        }
1137
1138        public void indicesRemoved(ComplexListDataEvent e) {
1139            int[] indices = e.getIndices();
1140            TabData[] removedTabs = e.getAffectedItems();
1141            Component JavaDoc curComp;
1142            for (int i = 0; i < indices.length; i++) {
1143                curComp = toComp(removedTabs[i]);
1144                // TBD - add assertion curComp.getParent() != contentDisplayer
1145
contentDisplayer.remove(curComp);
1146            }
1147        }
1148
1149        public void indicesChanged(ComplexListDataEvent e) {
1150            //XXX - if we keep contentPolicies, this could be simplified for
1151
//the non ADD_ALL policies
1152
if (e instanceof VeryComplexListDataEvent) {
1153                ArrayDiff dif = ((VeryComplexListDataEvent) e).getDiff();
1154
1155                //Get the deleted and added indices
1156
Set JavaDoc deleted = dif.getDeletedIndices();
1157                Set JavaDoc added = dif.getAddedIndices();
1158
1159                //Get the TabData array from before the change
1160
TabData[] old = dif.getOldData();
1161                //Get the TabData array from after the change
1162
TabData[] nue = dif.getNewData();
1163
1164                //Now we need to fetch the set of components we should end up
1165
//displaying. We need to do this because TabData.equals is only
1166
//true if the text *and* the component match. So if the winsys
1167
//called setTabs just to change the title of a tab, we would
1168
//end up removing and re-adding the component for no reason.
1169
Set JavaDoc<Component JavaDoc> components = new HashSet JavaDoc<Component JavaDoc>();
1170                if (container.getContentPolicy() == TabbedContainer.CONTENT_POLICY_ADD_ALL) {
1171                    for (int i = 0; i < nue.length; i++) {
1172                        components.add(toComp(nue[i]));
1173                    }
1174                }
1175                boolean changed = false;
1176
1177                synchronized (contentDisplayer.getTreeLock()) {
1178                    //See if we've got anything to delete
1179
if (!deleted.isEmpty()) {
1180                        Iterator JavaDoc i = deleted.iterator();
1181                        while (i.hasNext()) {
1182                            //Get the index into the old array of a deleted tab
1183
Integer JavaDoc idx = (Integer JavaDoc) i.next();
1184                            //Find the TabData object for it
1185
TabData del = old[idx.intValue()];
1186                            //Make sure its component is not one we'll be adding
1187
if (!components.contains(toComp(del))) {
1188                                //remove it
1189
contentDisplayer.remove(toComp(del));
1190                                changed = true;
1191                            }
1192                        }
1193                    }
1194                    
1195                    if (container.getContentPolicy() == TabbedContainer.CONTENT_POLICY_ADD_ALL) {
1196                    
1197                        //See if we've got anything to add
1198
if (!added.isEmpty()) {
1199                            Iterator JavaDoc i = added.iterator();
1200                            while (i.hasNext()) {
1201                                //Get the index into the new array of the added tab
1202
Integer JavaDoc idx = (Integer JavaDoc) i.next();
1203                                //Find the TabData object that was added
1204
TabData add = nue[idx.intValue()];
1205                                //Make sure it's not already showing so we don't do
1206
//extra work
1207
if (!contentDisplayer.isAncestorOf(
1208                                        toComp(add))) {
1209                                    contentDisplayer.add(toComp(add), "");
1210                                    changed = true;
1211                                }
1212                            }
1213                        }
1214                    }
1215                }
1216                //repaint
1217
if (changed) {
1218                    contentDisplayer.revalidate();
1219                    contentDisplayer.repaint();
1220                }
1221            }
1222        }
1223    }
1224
1225    /** An action listener which listens on action events from the tab displayer (select, close, etc.)
1226     * and propagates them to the tabbed container's action posting mechanism, so listeners on it also
1227     * have an opportunity to veto undesired actions, or handle actions themselves.
1228     */

1229    private class DisplayerActionListener implements ActionListener JavaDoc {
1230        public void actionPerformed (ActionEvent JavaDoc ae) {
1231            TabActionEvent tae = (TabActionEvent) ae;
1232            if (!shouldPerformAction(tae.getActionCommand(), tae.getTabIndex(), tae.getMouseEvent())) {
1233                tae.consume();
1234            }
1235        }
1236    }
1237
1238    private class SlidingTabsLayout implements LayoutManager JavaDoc {
1239        
1240        public void addLayoutComponent(String JavaDoc name, Component JavaDoc comp) {
1241            //do nothing
1242
}
1243        
1244        public void layoutContainer(Container JavaDoc parent) {
1245            JComponent JavaDoc c = tabDisplayer;
1246            
1247            Object JavaDoc orientation = c.getClientProperty (
1248                TabDisplayer.PROP_ORIENTATION);
1249            
1250            Dimension JavaDoc d = tabDisplayer.getPreferredSize();
1251            Insets JavaDoc ins = container.getInsets();
1252            int width = parent.getWidth() - (ins.left + ins.right);
1253            int height = parent.getHeight() - (ins.top + ins.bottom);
1254            
1255            if (orientation == TabDisplayer.ORIENTATION_NORTH) {
1256                c.setBounds (ins.left, ins.top,
1257                    width, d.height);
1258                
1259                contentDisplayer.setBounds (ins.left, ins.top + d.height,
1260                    width,
1261                    parent.getHeight() - (d.height + ins.top + ins.bottom));
1262                
1263            } else if (orientation == TabDisplayer.ORIENTATION_SOUTH) {
1264                contentDisplayer.setBounds (ins.top, ins.left, width,
1265                    parent.getHeight() - (d.height + ins.top + ins.bottom));
1266                
1267                c.setBounds (ins.left, parent.getHeight() - (d.height + ins.top + ins.bottom),
1268                    width, d.height);
1269            } else if (orientation == TabDisplayer.ORIENTATION_EAST) {
1270                contentDisplayer.setBounds (ins.left, ins.top, width - d.width,
1271                    height);
1272                
1273                c.setBounds (parent.getWidth() - (ins.right + d.width), ins.top,
1274                    d.width, height);
1275                
1276            } else if (orientation == TabDisplayer.ORIENTATION_WEST) {
1277                c.setBounds (ins.left, ins.top, d.width, height);
1278                
1279                contentDisplayer.setBounds (ins.left + d.width, ins.top,
1280                    width - d.width, height);
1281                
1282            } else {
1283                throw new IllegalArgumentException JavaDoc ("Unknown orientation: " + orientation);
1284            }
1285        }
1286        
1287        public Dimension JavaDoc minimumLayoutSize(Container JavaDoc parent) {
1288            JComponent JavaDoc c = tabDisplayer;
1289            
1290            Object JavaDoc orientation = c.getClientProperty (
1291                TabDisplayer.PROP_ORIENTATION);
1292            
1293            Dimension JavaDoc tabSize = tabDisplayer.getPreferredSize();
1294            Insets JavaDoc ins = container.getInsets();
1295            
1296            Dimension JavaDoc result = new Dimension JavaDoc();
1297            
1298            Dimension JavaDoc contentSize = contentDisplayer.getPreferredSize();
1299            if (tabDisplayer.getSelectionModel().getSelectedIndex() == -1) {
1300                contentSize.width = 0;
1301                contentSize.height = 0;
1302            }
1303            
1304            if (orientation == TabDisplayer.ORIENTATION_NORTH || orientation == TabDisplayer.ORIENTATION_SOUTH) {
1305                result.height = ins.top + ins.bottom + contentSize.height + tabSize.height;
1306                result.width = ins.left + ins.right + Math.max (contentSize.width, tabSize.width);
1307            } else {
1308                result.width = ins.left + ins.right + contentSize.width + tabSize.width;
1309                result.height = ins.top + ins.bottom + Math.max (contentSize.height, tabSize.height);
1310            }
1311            return result;
1312        }
1313        
1314        public Dimension JavaDoc preferredLayoutSize(Container JavaDoc parent) {
1315            return minimumLayoutSize(parent);
1316        }
1317        
1318        public void removeLayoutComponent(Component JavaDoc comp) {
1319            //do nothing
1320
}
1321    }
1322    
1323    /** A FxProvider which simply calls finish() from its start
1324     * method, providing no effects whatsoever */

1325    private final class NoOpFxProvider extends FxProvider {
1326        
1327        public void cleanup() {
1328            //Do nothing
1329
}
1330        
1331        protected void doFinish() {
1332            showComponent (comp);
1333            
1334        }
1335        
1336        protected void doStart() {
1337            finish();
1338        }
1339    }
1340    
1341    private final class ImageSlideFxProvider extends FxProvider implements ActionListener JavaDoc {
1342        private Timer JavaDoc timer = null;
1343        private Component JavaDoc prevGlassPane = null;
1344        private Dimension JavaDoc d = null;
1345        
1346        protected void doStart() {
1347            if (timer == null) {
1348                timer = new Timer JavaDoc (TIMER, this);
1349                timer.setRepeats(true);
1350            }
1351
1352            prevGlassPane = root.getGlassPane();
1353
1354            if (prevGlassPane.isVisible() && prevGlassPane.isShowing()) {
1355                //Probably a drag and drop operation - don't interfere
1356
doFinish();
1357                return;
1358            }
1359
1360            initSize();
1361            img = createImageOfComponent();
1362            
1363
1364            ImageScalingGlassPane cp = getCustomGlassPane();
1365            root.setGlassPane (cp);
1366            cp.setIncrement (0.1f);
1367            cp.setBounds (root.getBounds());
1368            cp.setVisible(true);
1369            cp.revalidate();
1370            timer.start();
1371        }
1372        
1373        public void cleanup() {
1374            timer.stop();
1375            root.setGlassPane(prevGlassPane);
1376            prevGlassPane.setVisible(false);
1377            if (img != null) {
1378                img.flush();
1379            }
1380            img = null;
1381        }
1382        
1383        protected void doFinish() {
1384            showComponent (comp);
1385        }
1386        
1387        private void initSize() {
1388            d = comp.getPreferredSize();
1389            
1390            Dimension JavaDoc d2 = contentDisplayer.getSize();
1391            
1392            d.width = Math.max (d2.width, d.width);
1393            d.height = Math.max (d2.height, d.height);
1394
1395            
1396            boolean flip = orientation == TabDisplayer.ORIENTATION_EAST ||
1397                orientation == TabDisplayer.ORIENTATION_WEST;
1398            
1399            if (d.width == 0 || d.height == 0) {
1400                if (flip) {
1401                    d.width = root.getWidth();
1402                    d.height = tabDisplayer.getHeight();
1403                } else {
1404                    d.width = tabDisplayer.getWidth();
1405                    d.height = root.getHeight();
1406                }
1407            } else {
1408                if (flip) {
1409                    d.height = Math.max (d.height, tabDisplayer.getHeight());
1410                } else {
1411                    d.width = Math.max (d.width, tabDisplayer.getWidth());
1412                }
1413            }
1414        }
1415
1416        private BufferedImage JavaDoc img = null;
1417        private BufferedImage JavaDoc createImageOfComponent() {
1418            if (USE_SWINGPAINTING) {
1419                return null;
1420            }
1421            if (d.width == 0 || d.height == 0) {
1422                //Avoid problems in native graphics engine scaling if we should
1423
//end up with crazy values
1424
finish();
1425            }
1426
1427            BufferedImage JavaDoc img =
1428                GraphicsEnvironment.getLocalGraphicsEnvironment().
1429                    getDefaultScreenDevice().getDefaultConfiguration().
1430                    createCompatibleImage(d.width, d.height);
1431            
1432            Graphics2D JavaDoc g2d = img.createGraphics();
1433            JComponent JavaDoc c = tabDisplayer;
1434            
1435            c.setBounds (0, 0, d.width, d.height);
1436            comp.paint (g2d);
1437
1438            return img;
1439        }
1440        
1441        public void actionPerformed(java.awt.event.ActionEvent JavaDoc e) {
1442            float inc = customGlassPane.getIncrement();
1443            if (inc >= 1.0f) {
1444                finish();
1445            } else {
1446                customGlassPane.setIncrement (inc + INCREMENT);
1447            }
1448        }
1449        
1450        private ImageScalingGlassPane customGlassPane = null;
1451        private ImageScalingGlassPane getCustomGlassPane() {
1452            if (customGlassPane == null) {
1453                customGlassPane = new ImageScalingGlassPane();
1454                customGlassPane.setOpaque(false);
1455            }
1456            return customGlassPane;
1457        }
1458        
1459        private class ImageScalingGlassPane extends JPanel JavaDoc {
1460            private float inc = 0f;
1461            private Rectangle JavaDoc rect = new Rectangle JavaDoc();
1462            private Rectangle JavaDoc r2 = new Rectangle JavaDoc();
1463            private boolean changed = true;
1464            
1465            private void setIncrement (float inc) {
1466                this.inc = inc;
1467                changed = true;
1468                if (isShowing()) {
1469                    Rectangle JavaDoc r = getImageBounds();
1470                    if (SYNCHRONOUS_PAINTING) {
1471                        paintImmediately (r.x, r.y, r.width, r.height);
1472                    } else {
1473                        repaint(r.x, r.y, r.width, r.height);
1474                    }
1475                }
1476            }
1477            
1478            private float getIncrement () {
1479                return inc;
1480            }
1481            
1482            private Rectangle JavaDoc getImageBounds() {
1483                if (!changed) {
1484                    return rect;
1485                }
1486                Component JavaDoc c = tabDisplayer;
1487                r2.setBounds (0, 0, c.getWidth(), c.getHeight());
1488                
1489                Rectangle JavaDoc dispBounds = SwingUtilities.convertRectangle(c, r2,
1490                    this);
1491                
1492                if (orientation == TabDisplayer.ORIENTATION_WEST) {
1493                    rect.x = dispBounds.x + dispBounds.width;
1494                    rect.y = dispBounds.y;
1495                    rect.width = Math.round (inc * d.width);
1496                    rect.height = dispBounds.height;
1497                } else if (orientation == TabDisplayer.ORIENTATION_EAST) {
1498                    rect.width = Math.round (inc * d.width);
1499                    rect.height = dispBounds.height;
1500                    rect.x = dispBounds.x - rect.width;
1501                    rect.y = dispBounds.y;
1502                } else if (orientation == TabDisplayer.ORIENTATION_SOUTH) {
1503                    rect.width = dispBounds.width;
1504                    rect.height = Math.round(inc * d.height);
1505                    rect.x = dispBounds.x;
1506                    rect.y = dispBounds.y - rect.height;
1507                } else if (orientation == TabDisplayer.ORIENTATION_NORTH) {
1508                    rect.x = dispBounds.x;
1509                    rect.y = dispBounds.y + dispBounds.height;
1510                    rect.width = dispBounds.width;
1511                    rect.height = Math.round(inc * d.height);
1512                }
1513                changed = false;
1514                return rect;
1515            }
1516            
1517            public void paint(Graphics JavaDoc g) {
1518                try {
1519                    if (USE_SWINGPAINTING) {
1520                        SwingUtilities.paintComponent(g, comp, this, getImageBounds());
1521                    } else {
1522                        Graphics2D JavaDoc g2d = (Graphics2D JavaDoc) g;
1523                        Composite JavaDoc comp = null;
1524                        if (true) {
1525                            comp = g2d.getComposite();
1526                            g2d.setComposite (AlphaComposite.getInstance(AlphaComposite.SRC_OVER, Math.min(0.99f, inc)));
1527                        }
1528                        Rectangle JavaDoc r = getImageBounds();
1529                        if (NO_SCALE) {
1530                            AffineTransform JavaDoc at = AffineTransform.getTranslateInstance(r.x, r.y);
1531                            g2d.drawRenderedImage(img, at);
1532                        } else {
1533                            g2d.drawImage (img, r.x, r.y, r.x + r.width,
1534                                r.y + r.height, 0, 0, d.width, d.height,
1535                                getBackground(), null);
1536                        }
1537                        if (comp != null) {
1538                            g2d.setComposite(comp);
1539                        }
1540                    }
1541                } catch (Exception JavaDoc e) {
1542                    //Some problem in Apple's graphics scaling engine
1543
e.printStackTrace();
1544                    finish();
1545                }
1546            }
1547        }
1548    }
1549    
1550
1551    private final class LiveComponentSlideFxProvider extends FxProvider implements ActionListener JavaDoc {
1552        private Timer JavaDoc timer = null;
1553        private Component JavaDoc prevGlassPane = null;
1554        private Dimension JavaDoc d = null;
1555
1556        protected void doStart() {
1557            if (timer == null) {
1558                timer = new Timer JavaDoc (TIMER, this);
1559                timer.setRepeats(true);
1560            }
1561
1562            prevGlassPane = root.getGlassPane();
1563            if (prevGlassPane.isVisible() && prevGlassPane.isShowing()) {
1564                //Probably a drag and drop operation - don't interfere
1565
doFinish();
1566                return;
1567            }
1568
1569            initSize();
1570            LiveComponentResizingGlassPane cp = getCustomGlassPane();
1571            root.setGlassPane (cp);
1572            cp.setIncrement (0.1f);
1573            cp.setBounds (root.getBounds());
1574            cp.setVisible(true);
1575            cp.revalidate();
1576            timer.start();
1577        }
1578        
1579        private void initSize() {
1580            d = comp.getPreferredSize();
1581            
1582            Dimension JavaDoc d2 = contentDisplayer.getSize();
1583            
1584            d.width = Math.max (d2.width, d.width);
1585            d.height = Math.max (d2.height, d.height);
1586            
1587            boolean flip = orientation == TabDisplayer.ORIENTATION_EAST ||
1588                orientation == TabDisplayer.ORIENTATION_WEST;
1589            
1590            if (d.width == 0 || d.height == 0) {
1591                if (flip) {
1592                    d.width = root.getWidth();
1593                    d.height = tabDisplayer.getHeight();
1594                } else {
1595                    d.width = tabDisplayer.getWidth();
1596                    d.height = root.getHeight();
1597                }
1598            } else {
1599                if (flip) {
1600                    d.height = Math.max (d.height, tabDisplayer.getHeight());
1601                } else {
1602                    d.width = Math.max (d.width, tabDisplayer.getWidth());
1603                }
1604            }
1605        }
1606        
1607        public void cleanup() {
1608            timer.stop();
1609            root.setGlassPane(prevGlassPane);
1610            prevGlassPane.setVisible(false);
1611            customGlassPane.remove(comp);
1612        }
1613        
1614        protected void doFinish() {
1615            showComponent (comp);
1616        }
1617        
1618        public void actionPerformed(java.awt.event.ActionEvent JavaDoc e) {
1619            float inc = customGlassPane.getIncrement();
1620            if (inc >= 1.0f) {
1621                finish();
1622            } else {
1623                customGlassPane.setIncrement (inc + INCREMENT);
1624            }
1625        }
1626        
1627        private LiveComponentResizingGlassPane customGlassPane = null;
1628        private LiveComponentResizingGlassPane getCustomGlassPane() {
1629            if (customGlassPane == null) {
1630                customGlassPane = new LiveComponentResizingGlassPane();
1631                customGlassPane.setOpaque(false);
1632            }
1633            return customGlassPane;
1634        }
1635        
1636        private class LiveComponentResizingGlassPane extends JPanel JavaDoc {
1637            private float inc = 0f;
1638            private Rectangle JavaDoc rect = new Rectangle JavaDoc();
1639            private Rectangle JavaDoc r2 = new Rectangle JavaDoc();
1640            private boolean changed = true;
1641            
1642            private void setIncrement (float inc) {
1643                this.inc = inc;
1644                changed = true;
1645                if (isShowing()) {
1646                    if (comp.getParent() != this) {
1647                        add(comp);
1648                        comp.setVisible(true);
1649                    }
1650                }
1651                doLayout();
1652            }
1653            
1654            public void doLayout() {
1655                Rectangle JavaDoc r = getImageBounds();
1656                comp.setBounds (r.x, r.y, r.width, r.height);
1657            }
1658            
1659            private float getIncrement () {
1660                return inc;
1661            }
1662            
1663            private Rectangle JavaDoc getImageBounds() {
1664                if (!changed) {
1665                    return rect;
1666                }
1667                Component JavaDoc c = tabDisplayer;
1668                r2.setBounds (0, 0, c.getWidth(), c.getHeight());
1669                
1670                Rectangle JavaDoc dispBounds = SwingUtilities.convertRectangle(c, r2,
1671                    this);
1672                
1673                if (orientation == TabDisplayer.ORIENTATION_WEST) {
1674                    rect.x = dispBounds.x + dispBounds.width;
1675                    rect.y = dispBounds.y;
1676                    rect.width = Math.round (inc * d.width);
1677                    rect.height = dispBounds.height;
1678                } else if (orientation == TabDisplayer.ORIENTATION_EAST) {
1679                    rect.width = Math.round (inc * d.width);
1680                    rect.height = dispBounds.height;
1681                    rect.x = dispBounds.x - rect.width;
1682                    rect.y = dispBounds.y;
1683                } else if (orientation == TabDisplayer.ORIENTATION_SOUTH) {
1684                    rect.width = dispBounds.width;
1685                    rect.height = Math.round(inc * d.height);
1686                    rect.x = dispBounds.x;
1687                    rect.y = dispBounds.y - rect.height;
1688                } else if (orientation == TabDisplayer.ORIENTATION_NORTH) {
1689                    rect.x = dispBounds.x;
1690                    rect.y = dispBounds.y + dispBounds.height;
1691                    rect.width = dispBounds.width;
1692                    rect.height = Math.round(inc * d.height);
1693                }
1694                changed = false;
1695                return rect;
1696            }
1697        }
1698    }
1699    
1700//*** A bunch of options for testing
1701

1702    /** Sysprop to turn off all sliding effects */
1703    static final boolean NO_EFFECTS = Boolean.getBoolean ("nb.tabcontrol.no.fx"); //NOI18N
1704
/** Sysprop to turn off scaling of the slide image */
1705    static final boolean NO_SCALE = Boolean.getBoolean ("nb.tabcontrol.fx.no.scaling"); //NOI18N
1706
/** Sysprop to turn use SwingUtilities.paintComponent() instead of an image buffer for sliding effects */
1707    static final boolean USE_SWINGPAINTING = Boolean.getBoolean ("nb.tabcontrol.fx.swingpainting"); //NOI18N
1708
/** Sysprop to turn add the component being scaled to the glasspane and alter its size on a
1709     * timer to accomplish growing the component */

1710    static final boolean ADD_TO_GLASSPANE = Boolean.getBoolean ("nb.tabcontrol.fx.use.resizing"); //NOI18N
1711
/** For those who <strong>really</strong> love the sliding effect and want to see it on all
1712     * tab controls of all types */

1713    static final boolean EFFECTS_EVERYWHERE = Boolean.getBoolean ("nb.tabcontrol.fx.everywhere") ||
1714        Boolean.getBoolean("nb.tabcontrol.fx.gratuitous"); //NOI18N
1715

1716    /** Also have the scaled image be partially transparent as it's drawn */
1717    static final boolean USE_ALPHA = Boolean.getBoolean ("nb.tabcontrol.fx.use.alpha") ||
1718        Boolean.getBoolean("nb.tabcontrol.fx.gratuitous"); //NOI18N
1719

1720    static boolean SYNCHRONOUS_PAINTING = Boolean.getBoolean ("nb.tabcontrol.fx.synchronous"); //NOI18N
1721

1722    static float INCREMENT = 0.07f;
1723    
1724    static int TIMER = 25;
1725    static {
1726        boolean gratuitous = Boolean.getBoolean("nb.tabcontrol.fx.gratuitous"); //NOI18N
1727
String JavaDoc s = System.getProperty ("nb.tabcontrol.fx.increment"); //NOI18N
1728
if (s != null) {
1729            try {
1730                INCREMENT = Float.parseFloat(s);
1731            } catch (Exception JavaDoc e) {
1732                System.err.println("Bad float value specified: \"" + s +"\""); //NOI18N
1733
}
1734        } else if (gratuitous) {
1735            INCREMENT = 0.02f;
1736        }
1737        
1738        s = System.getProperty ("nb.tabcontrol.fx.timer"); //NOI18N
1739
if (s != null) {
1740            try {
1741                TIMER = Integer.parseInt (s);
1742            } catch (Exception JavaDoc e) {
1743                System.err.println("Bad integer value specified: \"" + s + "\""); //NOI18N
1744
}
1745        } else if (gratuitous) {
1746            TIMER = 7;
1747        }
1748        if (gratuitous) {
1749            SYNCHRONOUS_PAINTING = true;
1750        }
1751    }
1752    
1753    private static final class ForwardingMouseListener implements MouseListener JavaDoc {
1754        private final Container JavaDoc c;
1755        public ForwardingMouseListener (Container JavaDoc c) {
1756            this.c = c;
1757        }
1758        public void mousePressed (MouseEvent JavaDoc me) {
1759            forward (me);
1760        }
1761        
1762        public void mouseReleased (MouseEvent JavaDoc me) {
1763            forward (me);
1764        }
1765        
1766        public void mouseClicked (MouseEvent JavaDoc me) {
1767            forward (me);
1768        }
1769        
1770        public void mouseEntered (MouseEvent JavaDoc me) {
1771            forward (me);
1772        }
1773        
1774        public void mouseExited (MouseEvent JavaDoc me) {
1775            forward (me);
1776        }
1777        
1778        private void forward (MouseEvent JavaDoc me) {
1779            MouseListener JavaDoc[] ml = c.getMouseListeners();
1780            if (ml.length == 0 || me.isConsumed()) {
1781                return;
1782            }
1783            MouseEvent JavaDoc me2 = SwingUtilities.convertMouseEvent(
1784                (Component JavaDoc) me.getSource(), me, c);
1785            
1786            for (int i=0; i < ml.length; i++) {
1787                switch (me2.getID()) {
1788                    case MouseEvent.MOUSE_ENTERED :
1789                        ml[i].mouseEntered(me2);
1790                        break;
1791                    case MouseEvent.MOUSE_EXITED :
1792                        ml[i].mouseExited(me2);
1793                        break;
1794                    case MouseEvent.MOUSE_PRESSED :
1795                        ml[i].mousePressed(me2);
1796                        break;
1797                    case MouseEvent.MOUSE_RELEASED :
1798                        ml[i].mouseReleased(me2);
1799                        break;
1800                    case MouseEvent.MOUSE_CLICKED :
1801                        ml[i].mouseClicked(me2);
1802                        break;
1803                    default :
1804                        assert false;
1805                }
1806            }
1807        }
1808        
1809    }
1810}
1811
Popular Tags