KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > swing > tabcontrol > TabbedContainer


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;
21 import javax.accessibility.Accessible JavaDoc;
22 import org.netbeans.swing.tabcontrol.event.TabActionEvent;
23 import org.netbeans.swing.tabcontrol.plaf.DefaultTabbedContainerUI;
24
25 import javax.swing.*;
26 import java.awt.*;
27 import java.awt.event.ActionListener JavaDoc;
28 import java.awt.event.HierarchyEvent JavaDoc;
29 import java.util.ArrayList JavaDoc;
30 import java.util.Collections JavaDoc;
31 import java.util.List JavaDoc;
32 import javax.accessibility.AccessibleRole JavaDoc;
33 import javax.swing.JComponent.AccessibleJComponent;
34 import org.openide.util.NbBundle;
35
36
37 /**
38  * A tabbed container similar to a JTabbedPane. The tabbed container is a
39  * simple container which contains two components - the tabs displayer, and the
40  * content displayer. The tabs displayer is the thing that actually draws the
41  * tabs; the content displayer contains the components that are being shown.
42  * <p>
43  * The first difference from a JTabbedPane is that it is entirely model driven -
44  * the tab contents are in a data model owned by the displayer. It is not
45  * strictly necessary for the contained components to even be installed in the
46  * AWT hierarchy when not displayed.
47  * <p>
48  * Other differences are more flexibility in the way tabs are displayed by
49  * completely separating the implementation and UI for that from that of
50  * displaying the contents.
51  * <p>
52  * Other interesting aspects are the ability of TabDataModel to deliver complex,
53  * granular events in a single pass with no information loss. Generally, great
54  * effort has been gone to to conflate nothing - that is, adding a component
55  * does not equal selecting it does not equal changing focus, et. cetera,
56  * leaving these decisions more in the hands of the user of the control.
57  * <p>
58  * It is possible to implement a subclass which provides the API of JTabbedPane,
59  * making it a drop-in replacement.
60  * <p>
61  * There are several UI styles a <code>TabbedContainer</code> can have. The type
62  * is passed as an argument to the constructor (support for changing these on the
63  * fly may be added in the future, but such a change is a very heavyweight operation,
64  * and is only desirable to enable use of this component in its various permutations
65  * inside GUI designers). The following styles are supported:
66  * <ul>
67  * <li><b>TYPE_VIEW</b> - These are tabs such as the Explorer window has in NetBeans -
68  * all tabs are always displayed, with the available space equally divided between them.</li>
69  * <li><b>TYPE_EDITOR</b> - Scrolling tabs, coupled with control buttons and mouse wheel
70  * support for scrolling the visible tabs, and a popup which displays a list of tabs.</li>
71  * <li><b>TYPE_SLIDING</b> - Tabs which are displayed as buttons, and may provide a
72  * fade or sliding effect when displayed. For this style, a second click on the selected
73  * tab will hide the selected tab (setting the selection model's selected index to -1).</li></ul>
74  * <p>
75  * <h4>Customizing the appearance of tabs</h4>
76  * Tabs are customized by providing a different UI delegate for the tab displayer component,
77  * via UIManager, in the same manner as any standard Swing component; for <code>TYPE_SLIDING</code>
78  * tabs, simply implementing an alternate UI delegate for the buttons used to represent tabs
79  * is all that is needed.
80  *
81  * <h4>Managing user events on tabs</h4>
82  * When a user clicks a tab, the TabbedContainer will fire an action event to all of its listeners.
83  * This action event will always be an instance of <code>TabActionEvent</code>, which can provide
84  * the index of the tab that was pressed, and the command name of the action that was performed.
85  * A client which wants to handle the event itself (for example, the asking a user if they want
86  * to save data, and possibly vetoing the closing of a tab) may veto (or take full responsibility
87  * for performing) the action by consuming the TabActionEvent.
88  *
89  *<h4>Indication of focus and the &quot;activated&quot; state</h4>
90  * The property <code>active</code> is provided to allow a tabbed container to indicate that it
91  * contains the currently focused component. However, no effort is made to track focus on the
92  * part of the tabbed control - there is too much variability possible (for example, if
93  * a component inside a tab opens a modal dialog, is the tab active or not?). In fact, using
94  * keyboard focus at all to manage the activated state of the component turns out to be a potent
95  * source of hard-to-fix, hard-to-reproduce bugs (especially when components are being added
96  * and removed, or hidden and shown or components which do not reliably produce focus events).
97  * What NetBeans does to solve the problem in a reliable way is the following:
98  * <ol>
99  * <li>Use an AWT even listener to track mouse clicks, and when the mouse is clicked,
100  * <ul>
101  * <li>Find the ancestor that is a tabbed container (if any)</li>
102  * <li>Set the activated state appropriately on it and the previously active container</li>
103  * <li>Ensure that keyboard focus moves into that container</li>
104  * </ul>
105  * <li>Block ctrl-tab style keyboard based focus traversal out of tabbed containers</li>
106  * <li>Provide keyboard actions, with menu items, which will change to a different container,
107  * activating it</li>
108  * </ol>
109  * This may seem complicated, and it probably is overkill for a small application (as is this
110  * tabbed control - it wasn't designed for a small application). It's primary advantage is
111  * that it works.
112  *
113  * @see TabDisplayer
114  * @author Tim Boudreau, Dafe Simonek
115  */

116 public class TabbedContainer extends JComponent implements Accessible JavaDoc {
117     /**
118      * UIManager key for the UI Delegate to be used by tabbed containers.
119      */

120     public static final String JavaDoc TABBED_CONTAINER_UI_CLASS_ID = "TabbedContainerUI"; //NOI18N
121

122     /**
123      * Creates a &quot;view&quot; style displayer; typically this will have a
124      * fixed width and a single row of tabs which get smaller as more tabs are
125      * added, as seen in NetBeans&rsquo; Explorer window.
126      */

127     public static final int TYPE_VIEW = 0;
128     /**
129      * Creates a &quot;editor&quot; style displayer; typically this uses a
130      * scrolling tabs UI for the tab displayer. This is the most scalable of the available
131      * UI styles - it can handle a very large number of tabs with minimal overhead, and
132      * the standard UI implementations of it use a cell-renderer model for painting.
133      */

134     public static final int TYPE_EDITOR = 1;
135     
136     /** Creates a &quot;sliding&quot; view, typically with tabs rendered as
137      * buttons along the left, bottom or right edge, with no scrolling behavior for tabs.
138      * Significant about this UI style is that re-clicking the selected tab will
139      * cause the component displayed to be hidden.
140      * <p>
141      * This is the least scalable of the available UI types, and is intended primarily for
142      * use with a small, fixed set of tabs. By default, the position of the tab displayer
143      * will be determined based on the proximity of the container to the edges of its
144      * parent window. This can be turned off by setting the client property
145      * PROP_MANAGE_TAB_POSITION to Boolean.FALSE.
146      */

147     public static final int TYPE_SLIDING = 2;
148     
149     /**
150      * Creates a Toolbar-style displayer (the style used by the NetBeans Form Editor's
151      * Component Inspector and a few other places in NetBeans).
152      */

153     public static final int TYPE_TOOLBAR = 3;
154
155     /**
156      * Property fired when <code>setActive()</code> is called
157      */

158     public static final String JavaDoc PROP_ACTIVE = "active"; //NOI18N
159

160     /** Client property applicable only to TYPE_SLIDING tabs. If set to
161      * Boolean.FALSE, the UI will not automatically try to determine a
162      * correct position for the tab displayer.
163      */

164     public static final String JavaDoc PROP_MANAGE_TAB_POSITION = "manageTabPosition";
165
166     /**
167      * Action command indicating that the action event signifies the user
168      * clicking the Close button on a tab.
169      */

170     public static final String JavaDoc COMMAND_CLOSE = "close"; //NOI18N
171

172     /**
173      * Action command indicating that the action event fired signifies the user
174      * selecting a tab
175      */

176     public static final String JavaDoc COMMAND_SELECT = "select"; //NOI18N
177

178     /** Action command indicating that a popup menu should be shown */
179     public static final String JavaDoc COMMAND_POPUP_REQUEST = "popup"; //NOI18N
180

181     /** Command indicating a maximize (double-click) request */
182     public static final String JavaDoc COMMAND_MAXIMIZE = "maximize"; //NOI18N
183

184     public static final String JavaDoc COMMAND_CLOSE_ALL = "closeAll"; //NOI18N
185

186     public static final String JavaDoc COMMAND_CLOSE_ALL_BUT_THIS = "closeAllButThis"; //NOI18N
187

188     public static final String JavaDoc COMMAND_ENABLE_AUTO_HIDE = "enableAutoHide"; //NOI18N
189

190     public static final String JavaDoc COMMAND_DISABLE_AUTO_HIDE = "disableAutoHide"; //NOI18N
191

192     //XXX support supressing close buttons
193

194     /**
195      * The data model which contains information about the tabs, such as the
196      * corresponding component, the icon and the tooltip. Currently this is
197      * assigned in the constructor and cannot be modified later, though this
198      * could be supported in the future (with substantial effort).
199      *
200      * @see TabData
201      * @see TabDataModel
202      */

203     private TabDataModel model;
204
205     /**
206      * The type of this container, which determines what UI delegate is used for
207      * the tab displayer
208      */

209     private final int type;
210
211     /**
212      * Holds the value of the active property, determining if the displayer
213      * should be painted with the focused or unfocused colors
214      */

215     private boolean active = false;
216
217     /**
218      * Flag used to block the call to updateUI() from the superclass constructor
219      * - at that time, none of our instance fields are set, so the UI can't yet
220      * set up the tab displayer correctly
221      */

222     private boolean initialized = false;
223
224     /**
225      * Utility field holding list of ActionListeners.
226      */

227     private transient List JavaDoc<ActionListener JavaDoc> actionListenerList;
228
229     /**
230      * Content policy in which all components contained in the data model should immediately
231      * be added to the AWT hierarchy at the time they appear in the data model.
232      *
233      * @see #setContentPolicy
234      */

235     public static final int CONTENT_POLICY_ADD_ALL = 1;
236     /**
237      * Content policy by which components contained in the data model are added to the AWT
238      * hierarchy the first time they are shown, and remain their thereafter unless removed
239      * from the data model.
240      *
241      * @see #setContentPolicy
242      */

243     public static final int CONTENT_POLICY_ADD_ON_FIRST_USE = 2;
244     /**
245      * Content policy by which components contained in the data model are added to the AWT
246      * hierarchy the when they are shown, and removed immediately when the user changes tabs.
247      */

248     public static final int CONTENT_POLICY_ADD_ONLY_SELECTED = 3;
249
250     private int contentPolicy = DEFAULT_CONTENT_POLICY ;
251
252     /** The default content policy, currently CONTENT_POLICY_ADD_ALL. To facilitate experimentation with
253      * different settings application-wide, set the system property &quot;nb.tabcontrol.contentpolicy&quot;
254      * to 1, 2 or 3 for ADD_ALL, ADD_ON_FIRST_USE or ADD_ONLY_SELECTED, respectively (note other values
255      * will throw an <code>Error</code>). Do not manipulate this value at runtime, it will likely become
256      * a final field in a future release. It is a protected field only to ensure its inclusion in documentation.
257      *
258      * @see #setContentPolicy
259      */

260     protected static int DEFAULT_CONTENT_POLICY = CONTENT_POLICY_ADD_ALL;
261     
262     /** The component converter which will tranlate TabData's from the model into
263      * components. */

264     private ComponentConverter converter = null;
265     
266     /** Winsys info needed for tab control or null if not available */
267     private WinsysInfoForTabbed winsysInfo = null;
268
269     @Deprecated JavaDoc
270     private LocationInformer locationInformer = null;
271
272     /**
273      * Create a new pane with the default model and tabs displayer
274      */

275     public TabbedContainer() {
276         this(null, TYPE_VIEW);
277     }
278
279     /**
280      * Create a new pane with asociated model and the default tabs displayer
281      */

282     public TabbedContainer(TabDataModel model) {
283         this(model, TYPE_VIEW);
284     }
285     
286     public TabbedContainer(int type) {
287         this (null, type);
288     }
289     
290     /**
291      * Create a new pane with the specified model and displayer type
292      *
293      * @param model The model
294      */

295     public TabbedContainer(TabDataModel model, int type) {
296         this (model, type, (WinsysInfoForTabbed)null);
297     }
298
299     /**
300      * Deprecated, please use constructor with WinsysInfoForTabbed instead.
301      */

302     @Deprecated JavaDoc
303     public TabbedContainer(TabDataModel model, int type, LocationInformer locationInformer) {
304         this (model, type, (WinsysInfoForTabbed)null);
305         this.locationInformer = locationInformer;
306     }
307         
308     /**
309      * Create a new pane with the specified model, displayer type and extra
310      * information from winsys
311      */

312     public TabbedContainer(TabDataModel model, int type, WinsysInfoForTabbed winsysInfo) {
313         switch (type) {
314             case TYPE_VIEW:
315             case TYPE_EDITOR:
316             case TYPE_SLIDING:
317             case TYPE_TOOLBAR:
318                 break;
319             default :
320                 throw new IllegalArgumentException JavaDoc("Unknown UI type: " + type); //NOI18N
321
}
322         if (model == null) {
323             model = new DefaultTabDataModel();
324         }
325         this.model = model;
326         this.type = Boolean.getBoolean("nb.tabcontrol.alltoolbar") ? TYPE_TOOLBAR : type;
327         this.winsysInfo = winsysInfo;
328         initialized = true;
329         updateUI();
330         //A few borders and such will check this
331
//@see org.netbeans.swing.plaf.gtk.AdaptiveMatteBorder
332
putClientProperty ("viewType", new Integer JavaDoc(type)); //NOI18N
333
}
334     
335     
336
337     /**
338      * Overridden as follows: When called by the superclass constructor (before
339      * the <code>type</code> field is set), it will simply return; the
340      * TabbedContainer constructor will call updateUI() explicitly later.
341      * <p>
342      * Will first search UIManager for a matching UI class. If non-null
343      * (by default it is set in the core/swing/plaf library), it will compare
344      * the found class name with the current UI. If they are a match, it
345      * will call TabbedContainerUI.shouldReplaceUI() to decide whether to
346      * actually do anything or not (in most cases it would just replace an
347      * instance of DefaultTabbedContainerUI with another one; but this call
348      * allows DefaultTabbedContainerUI.uichange() to update the tab displayer
349      * as needed).
350      * <p>
351      * If no UIManager UI class is defined, this method will silently use an
352      * instance of DefaultTabbedContainerUI.
353      */

354     public void updateUI() {
355         if (!initialized) {
356             //Block the superclass call to updateUI(), which comes before the
357
//field is set to tell if we are a view tab control or an editor
358
//tab control - the UI won't be able to set up the tab displayer
359
//correctly until this is set.
360
return;
361         }
362         TabbedContainerUI ui = null;
363         String JavaDoc UIClass = (String JavaDoc) UIManager.get(getUIClassID());
364         if (getUI() != null && (getUI().getClass().getName().equals(UIClass) | UIClass == null)) {
365             if (!getUI().shouldReplaceUI()) {
366                 return;
367             }
368         }
369         
370         if (UIClass != null) { //Avoid a stack trace
371
try {
372                 ui = (TabbedContainerUI) UIManager.getUI(this);
373             } catch (Error JavaDoc e) {
374                 //do nothing
375
}
376         }
377         if (ui != null) {
378             setUI(ui);
379         } else {
380             setUI(DefaultTabbedContainerUI.createUI(this));
381         }
382     }
383     
384     /**
385      * Get the type of this displayer - it is either TYPE_EDITOR or TYPE_VIEW.
386      * This property is set in the constructor and is immutable
387      */

388     public final int getType() {
389         return type;
390     }
391
392     /**
393      * Returns <code>TabbedContainer.TABBED_CONTAINER_UI_CLASS_ID</code>
394      */

395     public String JavaDoc getUIClassID() {
396         return TABBED_CONTAINER_UI_CLASS_ID;
397     }
398
399     /** Get the ui delegate for this component */
400     public TabbedContainerUI getUI() {
401         return (TabbedContainerUI) ui;
402     }
403     
404     /**
405      * Set the converter that converts user objects in the data model into
406      * components to display. If set to null (the default), the user object
407      * at the selected index in the data model will be cast as an instance
408      * of JComponent when searching for what to show for a given tab.
409      * <p>
410      * For use cases where a single component is to be displayed for more
411      * than one tab, just reconfigured when the selection changes, simply
412      * supply a ComponentConverter.Fixed with the component that should be
413      * used for all tabs.
414      */

415     public final void setComponentConverter (ComponentConverter cc) {
416         ComponentConverter old = converter;
417         converter = cc;
418         if (old instanceof ComponentConverter.Fixed && cc instanceof ComponentConverter.Fixed) {
419             List JavaDoc<TabData> l = getModel().getTabs();
420             if (!l.isEmpty()) {
421                 TabData[] td = l.toArray (new TabData[0]);
422                 getModel().setTabs (new TabData[0]);
423                 getModel().setTabs(td);
424             }
425         }
426     }
427     
428     /** Get the component converter which is used to fetch a component
429      * corresponding to an element in the data model. If the value has
430      * not been set, it will use ComponentConverter.DEFAULT, which simply
431      * delegates to TabData.getComponent().
432      */

433     public final ComponentConverter getComponentConverter() {
434         if (converter != null) {
435             return converter;
436         }
437         return ComponentConverter.DEFAULT;
438     }
439
440     /** Experimental property - alter the policy by which the components in
441      * the model are added to the container. This may not remain suppported.
442      * If used, it should be called before populating the data model.
443      */

444     public final void setContentPolicy(int i) {
445         switch (i) {
446             case CONTENT_POLICY_ADD_ALL :
447             case CONTENT_POLICY_ADD_ON_FIRST_USE :
448             case CONTENT_POLICY_ADD_ONLY_SELECTED :
449                 break;
450             default :
451                 throw new IllegalArgumentException JavaDoc ("Unknown content policy: "
452                     + i);
453         }
454         
455         if (i != contentPolicy) {
456             int old = contentPolicy;
457             contentPolicy = i;
458             firePropertyChange ("contentPolicy", old, i); //NOI18N
459
}
460     }
461
462     /** Determine the policy by which components are added to the container.
463      * There are various pros and cons to each:
464      * <ul>
465      * <li>CONTENT_POLICY_ADD_ALL - All components in the data model are
466      * automatically added to the container, and whenever the model changes,
467      * components are added and removed as need be. This is less scalable,
468      * but absolutely reliable</li>
469      * <li>CONTENT_POLICY_ADD_ON_FIRST_USE - Components are not added to the
470      * container until the first time they are used, and then they remain in
471      * the AWT hierarchy until their TabData elements are removed from the
472      * model. This is more scalable, and probably has some startup time
473      * benefits</li>
474      * <li>CONTENT_POLICY_ADD_ONLY_SELECTED - The only component that will
475      * ever be in the AWT hierarchy is the one that is being displayed. This
476      * is safest in the case that heavyweight AWT components may be used </li>
477      * </ul>
478      */

479     public int getContentPolicy() {
480         return contentPolicy;
481     }
482     
483     public boolean isValidateRoot() {
484         return true;
485     }
486
487     public boolean isPaintingOrigin() {
488         return true;
489     }
490     
491
492     public void setToolTipTextAt(int index, String JavaDoc toolTip) {
493         //Do this quietly - no notification is needed
494
TabData tabData = getModel().getTab(index);
495         if (tabData != null) {
496             tabData.tip = toolTip;
497         }
498     }
499
500     /**
501      * Get the data model that represents the tabs this component has. All
502      * programmatic manipulation of tabs should be done via the data model.
503      *
504      * @return The model
505      */

506     public final TabDataModel getModel() {
507         return model;
508     }
509
510     /**
511      * Get the selection model. The selection model tracks the index of the
512      * selected component, modifying this index appropriately when tabs are
513      * added or removed.
514      *
515      * @return The model
516      */

517     public final SingleSelectionModel getSelectionModel() {
518         return getUI().getSelectionModel();
519     }
520     
521     /**
522      * Fetch the rectangle of the tab for a given index, in the coordinate space
523      * of this component, by reconfiguring the passed rectangle object
524      */

525     public final Rectangle getTabRect(int index, final Rectangle r) {
526         return getUI().getTabRect(index, r);
527     }
528
529     /** Gets the index of the tab at point p, or -1 if no tab is there */
530     public int tabForCoordinate (Point p) {
531         return getUI().tabForCoordinate(p);
532     }
533
534     /**
535      * Set the &quot;active&quot; state of this tab control - this affects the
536      * way the tabs are displayed, to indicate focus. Note that this method
537      * will <i>never</i> be called automatically in stand-alone use of
538      * TabbedContainer. While one would expect a component gaining keyboard
539      * focus to be a good determinant, it actually turns out to be a potent
540      * source of subtle and hard-to-fix bugs.
541      * <p/>
542      * NetBeans uses an AWTEventListener to track mouse clicks, and allows
543      * components to become activated only via a mouse click or via a keyboard
544      * action or menu item which activates the component. This approach is far
545      * more robust and is the recommended usage pattern.
546      */

547     public final void setActive(boolean active) {
548         if (active != this.active) {
549             this.active = active;
550             firePropertyChange(PROP_ACTIVE, !active, active);
551         }
552     }
553     
554     /**
555      * Cause the tab at the specified index to blink or otherwise suggest that
556      * the user should click it.
557      */

558     public final void requestAttention (int tab) {
559         getUI().requestAttention(tab);
560     }
561     
562     public final void cancelRequestAttention (int tab) {
563         getUI().cancelRequestAttention(tab);
564     }
565     
566     /**
567      * Cause the specified tab to blink or otherwisse suggest that the user should
568      * click it.
569      */

570     public final boolean requestAttention (TabData data) {
571         int idx = getModel().indexOf(data);
572         boolean result = idx >= 0;
573         if (result) {
574             requestAttention (idx);
575         }
576         return result;
577     }
578     
579     public final void cancelRequestAttention (TabData data) {
580         int idx = getModel().indexOf(data);
581         if (idx != -1) {
582             cancelRequestAttention(idx);
583         }
584     }
585
586     /**
587      * Determine if this component thinks it is &quot;active&quot;, which
588      * affects how the tabs are painted - typically used to indicate that
589      * keyboard focus is somewhere within the component
590      */

591     public final boolean isActive() {
592         return active;
593     }
594
595     /**
596      * Register an ActionListener. TabbedContainer and TabDisplayer guarantee
597      * that the type of event fired will always be TabActionEvent. There are
598      * two special things about TabActionEvent: <ol> <li>There are methods on
599      * TabActionEvent to find the index of the tab the event was performed on,
600      * and if present, retrieve the mouse event that triggered it, for clients
601      * that wish to provide different handling for different mouse buttons</li>
602      * <li>TabActionEvents can be consumed. If a listener consumes the event,
603      * the UI will take no action - the selection will not be changed, the tab
604      * will not be closed. Consuming the event means taking responsibility for
605      * doing whatever would normally happen automatically. This is useful for,
606      * for example, showing a dialog and possibly aborting closing a tab if it
607      * contains unsaved data, for instance.</li> </ol> Action events will be
608      * fired <strong>before</strong> any action has been taken to alter the
609      * state of the control to match the action, so that they may be vetoed or
610      * modified by consuming the event.
611      *
612      * @param listener The listener to register.
613      */

614     public final synchronized void addActionListener(ActionListener JavaDoc listener) {
615         if (actionListenerList == null) {
616             actionListenerList = new ArrayList JavaDoc<ActionListener JavaDoc>();
617         }
618         actionListenerList.add(listener);
619     }
620
621     /**
622      * Remove an action listener.
623      *
624      * @param listener The listener to remove.
625      */

626     public final synchronized void removeActionListener(
627             ActionListener JavaDoc listener) {
628         if (actionListenerList != null) {
629             actionListenerList.remove(listener);
630             if (actionListenerList.isEmpty()) {
631                 actionListenerList = null;
632             }
633         }
634     }
635
636     /**
637      * Used by the UI to post action events for selection and close operations.
638      * If the event is consumed, the UI should take no action to change the
639      * selection or close the tab, and will presume that the receiver of the
640      * event is handling performing whatever action is appropriate.
641      *
642      * @param event The event to be fired
643      */

644     protected final void postActionEvent(TabActionEvent event) {
645         List JavaDoc<ActionListener JavaDoc> list;
646         synchronized (this) {
647             if (actionListenerList == null)
648                 return;
649             list = Collections.unmodifiableList(actionListenerList);
650         }
651         for( ActionListener JavaDoc l : list ) {
652             l.actionPerformed(event);
653         }
654     }
655
656     public void setIconAt(int index, Icon icon) {
657         getModel().setIcon(index, icon);
658     }
659
660     public void setTitleAt(int index, String JavaDoc title) {
661         getModel().setText(index, title);
662     }
663
664     /** Create an image of a single tab, suitable for use in drag and drop operations */
665     public Image createImageOfTab(int idx) {
666         return getUI().createImageOfTab (idx);
667     }
668
669     /** Get the number of tabs. Equivalent to <code>getModel().size()</code> */
670     public int getTabCount() {
671         return getModel().size();
672     }
673     
674     /**
675      * Set whether or not close buttons should be shown.
676      * This can be defaulted with the system property
677      * <code>nb.tabs.suppressCloseButton</code>; if the system
678      * property is not set, the default is true.
679      */

680     public final void setShowCloseButton (boolean val) {
681         boolean wasShow = isShowCloseButton();
682         if (val != wasShow) {
683             getUI().setShowCloseButton(val);
684             firePropertyChange ("showCloseButton", wasShow, val);
685         }
686     }
687     
688     /**
689      * Determine whether or not close buttons are being shown.
690      */

691     public final boolean isShowCloseButton () {
692         return getUI().isShowCloseButton();
693     }
694
695     /** Get the index of a component */
696     public int indexOf (Component comp) {
697         int max = getModel().size();
698         TabDataModel mdl = getModel();
699         for (int i=0; i < max; i++) {
700             if (getComponentConverter().getComponent(mdl.getTab(i)) == comp) {
701                 return i;
702             }
703         }
704         return -1;
705     }
706
707     /** The index at which a tab should be inserted if a drop operation
708      * occurs at this point.
709      *
710      * @param location A point anywhere on the TabbedContainer
711      * @return A tab index, or -1
712      */

713     public int dropIndexOfPoint (Point location) {
714         return getUI().dropIndexOfPoint(location);
715     }
716
717     /**
718      * Get a shape appropriate for drawing on the window's glass pane to indicate
719      * where a component should appear in the tab order if it is dropped here.
720      *
721      * @param dragged An object being dragged, or null. The object may be an instance
722      * of <code>TabData</code> or <code>Component</code>, in which case a check
723      * will be done of whether the dragged object is already in the data model,
724      * so that attempts to drop the object over the place it already is in the
725      * model will always return the exact indication of that tab's position.
726      *
727      * @param location A point
728      * @return Drop indication drawing
729      */

730     public Shape getDropIndication(Object JavaDoc dragged, Point location) {
731         int ix;
732         if (dragged instanceof Component) {
733             ix = indexOf((Component)dragged);
734         } else if (dragged instanceof TabData) {
735             ix = getModel().indexOf((TabData) dragged);
736         } else {
737             ix = -1;
738         }
739
740         int over = dropIndexOfPoint(location);
741
742         if(over < 0) { // XXX PENDING The tab is not found, later simulate the last one.
743
Rectangle r = getBounds();
744             r.setLocation(0, 0);
745             return r;
746         }
747         /*
748         if (over == ix || (over == ix + 1 && ix != -1 && over < getModel().size())) { //+1 - dropping on the next tab will put it in the same place
749             return getUI().getExactTabIndication(over);
750         } else {
751             return getUI().getInsertTabIndication(over);
752         }
753         */

754         if (over == ix && ix != -1) {
755             return getUI().getExactTabIndication(over);
756         } else {
757             return getUI().getInsertTabIndication(over);
758         }
759     }
760     
761     @Deprecated JavaDoc
762     public LocationInformer getLocationInformer() {
763         return locationInformer;
764     }
765     
766     public WinsysInfoForTabbed getWinsysInfo() {
767         return winsysInfo;
768     }
769
770     static {
771         //Support for experimenting with different content policies in NetBeans
772
String JavaDoc s = System.getProperty("nb.tabcontrol.contentpolicy"); //NOI18N
773
if (s != null) {
774             try {
775                 DEFAULT_CONTENT_POLICY = Integer.parseInt (s);
776                 switch (DEFAULT_CONTENT_POLICY) {
777                     case CONTENT_POLICY_ADD_ALL :
778                     case CONTENT_POLICY_ADD_ON_FIRST_USE :
779                     case CONTENT_POLICY_ADD_ONLY_SELECTED :
780                         System.err.println("Using custom content policy: " + DEFAULT_CONTENT_POLICY);
781                         break;
782                     default :
783                         throw new Error JavaDoc ("Bad value for default content " +
784                                 "policy: " + s + " only values 1, 2 or 3" +
785                                 "are meaningful"); //NOI18N
786
}
787                 System.err.println ("Default content policy is " + DEFAULT_CONTENT_POLICY);
788             } catch (Exception JavaDoc e) {
789                 System.err.println ("Error parsing default content " +
790                     "policy: \"" + s + "\""); //NOI18N
791
}
792         }
793     }
794
795     public javax.accessibility.AccessibleContext JavaDoc getAccessibleContext() {
796         if( null == accessibleContext ) {
797             accessibleContext = new AccessibleJComponent() {
798                         public AccessibleRole JavaDoc getAccessibleRole() {
799                             return AccessibleRole.PAGE_TAB_LIST;
800                         }
801                     };
802         
803             accessibleContext.setAccessibleName( NbBundle.getMessage(TabbedContainer.class, "ACS_TabbedContainer") );
804             accessibleContext.setAccessibleDescription( NbBundle.getMessage(TabbedContainer.class, "ACSD_TabbedContainer") );
805         }
806         
807         return accessibleContext;
808     }
809     
810 }
811
Popular Tags