KickJava   Java API By Example, From Geeks To Geeks.

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


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 package org.netbeans.swing.tabcontrol.plaf;
20
21 import java.awt.event.ActionEvent JavaDoc;
22 import java.awt.event.ActionListener JavaDoc;
23 import java.util.ArrayList JavaDoc;
24 import java.util.Arrays JavaDoc;
25 import java.util.HashSet JavaDoc;
26 import java.util.Iterator JavaDoc;
27 import java.util.List JavaDoc;
28 import java.util.Set JavaDoc;
29 import javax.swing.Timer JavaDoc;
30 import javax.swing.event.ListDataEvent JavaDoc;
31 import org.netbeans.swing.tabcontrol.TabData;
32 import org.netbeans.swing.tabcontrol.event.ArrayDiff;
33 import org.netbeans.swing.tabcontrol.event.ComplexListDataEvent;
34 import org.netbeans.swing.tabcontrol.event.VeryComplexListDataEvent;
35 import org.openide.util.Utilities;
36
37 /**
38  * Used by BasicTabDisplayerUI and its subclasses.
39  * Tracks and manages the state of tabs, mainly which one currently contains the
40  * mouse, if the mouse is in the close button, if the tab is adjacent to a
41  * selected tab, if it is leftmost, rightmost, active, etc. This class hides
42  * most of the complexity of deciding what mouse events should trigger a repaint
43  * of what areas in an optimized way. It provides setters which a mouse
44  * listener can call to indicate that the mouse has, say, moved into a tab, or
45  * from one tab to another, or the selection has changed, etc.
46  * <p>
47  * Essentially, this class is fed indices of tabs that have various states (selected,
48  * contains mouse, etc.), figures out if this affects one tab, two tabs (a different
49  * tab had the state, such as the mouse moving from one tab to another) or all tabs (activated). It determines
50  * a change type, and consults a <i>repaint policy</i> (an integer bitmask) to decide
51  * if one, both, all or no tabs should be repainted.
52  * <p>
53  * The typical use case is to implement a subclass, and override getState() to
54  * mix in bitmasks for things like whether a tab is clipped, etc. - things the
55  * base implementation can't know about.
56  *
57  * <p>
58  * Subclasses implement the <code>repaintTab()</code> method to do the actual
59  * repainting, and implement <code>getRepaintPolicy()</code>. The repainting will be
60  * called if an event happens that changes the state in a way that the repaint policy
61  * bitmask indicates should cause a repaint.
62  * <p>
63  * BasicTabDisplayerUI implements a mouse listener which will call the appropriate
64  * methods when the mouse enters/exits tabs, etc.
65  * <h4>Details</h4>
66  * <p>
67  * State is composed as an integer bitmask which covers all of the supported
68  * states of a tab that may affect the way they paint. These are also the values that
69  * are passed to the methods of a <code>TabCellRenderer</code> to tell it how to
70  * paint itself. Two other integer
71  * bitmasks are used: The <code>changeType</code>, which indicates whether a
72  * change was from one tab to another tab, one tab to no tab (i.e. selection set
73  * to -1), one tab to the same tab (i.e. the mouse moved out of the tab control,
74  * and so the tab with the mouse in it is now no tab). RepaintPolicy is an integer
75  * bitmask composed of conditions under which the control should repaint one or all
76  * tabs, and determines what types of changes actually trigger repaints.
77  * <p>
78  * Subclasses are expected to override <code>getState()</code> to provide
79  * information about non-mouse-or-focus related states, such as clipping of
80  * scrollable tabs. Predefined states for tabs are: CLIP_RIGHT, CLIP_LEFT,
81  * ARMED, PRESSED, SELECTED, ACTIVE, NOT_ONSCREEN, LEFTMOST, RIGHTMOST,
82  * CLOSE_BUTTON_ARMED, BEFORE_SELECTED, AFTER_SELECTED, MOUSE_IN_TABS_AREA,
83  * MOUSE_PRESSED_IN_CLOSE_BUTTON. Subclasses must handle returning the following
84  * states if they wish to, which are not handled directly in TabState: LEFTMOST,
85  * RIGHTMOST, CLIP_LEFT, CLIP_RIGHT, NOT_ONSCREEN.
86  * <p>
87  * Most of the states are fairly self-explanatory; NOT_ONSCREEN is useful as an
88  * optimization so that no work is done for events that would try to produce a
89  * repaint for something not visible; CLIP_* refers to the case in scrollable
90  * tab UIs, in which a tab may only be partially visible;
91  * MOUSE_PRESSED_IN_CLOSE_BUTTON is distinct because the CLOSE_BUTTON_ARMED
92  * state will be reset if the mouse moves out of the close button area, but UIs
93  * should perform a close action if the mouse was pressed over the close button,
94  * moved away from the close button and then back to it, so this state preserves
95  * the information that the originating location of a mouse press was in the
96  * close button.
97  */

98 public abstract class TabState {
99
100     /**
101      * Bitmask for state of tabs clipped on the right side - that is, partially
102      * displayed
103      */

104     public static final int CLIP_RIGHT = 1;
105     /**
106      * Bitmask for state of tabs clipped on the right side - that is, partially
107      * displayed
108      */

109     public static final int CLIP_LEFT = 2;
110     /**
111      * Bitmask indicating the tab contains the mouse
112      */

113     public static final int ARMED = 4;
114     /**
115      * Bitmask indicating the tab contains the mouse and the mouse button has
116      * been pressed
117      */

118     public static final int PRESSED = 8;
119     /**
120      * Bitmask indicating the tab is selected
121      */

122     public static final int SELECTED = 16;
123     /**
124      * Bitmask indicating the tab is activated
125      */

126     public static final int ACTIVE = 32;
127     /**
128      * State bitmask indicating a tab is not displayed at all and shouldn't be
129      * painted. Implementations may more simply avoid not painting a tab by not
130      * including it in the range returned by getFirstVisibleTab()/getLastVisibleTab().
131      * This state exists so that implementations have the option of returning
132      * the entire range of tabs as visible and determining if one is truly
133      * visble or not in getTabState() - sometimes doing it this way will be less
134      * expensive.
135      */

136     public static final int NOT_ONSCREEN = 64;
137     /**
138      * Bitmask indicating the tab is at the extreme left and
139      * <strong>not</strong> clipped
140      */

141     public static final int LEFTMOST = 128;
142     /**
143      * Bitmask indicating the tab is at the extreme right and
144      * <strong>not</strong> clipped
145      */

146     public static final int RIGHTMOST = 256;
147     /**
148      * Bitmask indicating that the tab contains the mouse and the mouse is in
149      * the close button
150      */

151     public static final int CLOSE_BUTTON_ARMED = 512;
152     /**
153      * Bitmask indicating that the tab's index is that of the selected index
154      * less one
155      */

156     public static final int BEFORE_SELECTED = 1024;
157     /**
158      * Bitmask indicating that the tab's index is that of the selected index
159      * plus one
160      */

161     public static final int AFTER_SELECTED = 2048;
162     /**
163      * Bitmask indicating that the mouse is in the tabs area
164      */

165     public static final int MOUSE_IN_TABS_AREA = 4096;
166
167     /**
168      * Bitmask indicating that the mouse is inside the close button and has
169      * been pressed.
170      */

171     public static final int MOUSE_PRESSED_IN_CLOSE_BUTTON = 8192;
172     
173     /**
174      * Bitmask indicating that the tab is in "attention" mode - blinking or
175      * flashing to get the user's attention.
176      */

177     public static final int ATTENTION = 16384;
178     
179     /**
180      * Bitmask indicating that the tab's index is that of the armed index
181      * less one
182      */

183     public static final int BEFORE_ARMED = 32768;
184     
185     /**
186      * Indicates the last constant defined - renderers that wish to add their
187      * own bitmasks should use multiples of this number
188      */

189     public static int STATE_LAST = MOUSE_PRESSED_IN_CLOSE_BUTTON;
190
191
192     private int pressedIndex = -1;
193     private int containsMouseIndex = -1;
194     private int closeButtonContainsMouseIndex = -1;
195     private int mousePressedInCloseButtonIndex = -1;
196     private boolean mouseInTabsArea = false;
197     private boolean active = false;
198     private int selectedIndex = -1;
199
200     private int prev = -1;
201     private int curr = -1;
202     private int lastChangeType = NO_CHANGE;
203     private int lastAffected = 0;
204     private int lastChange = 0;
205
206     /** Repaint policy bitmask indicating that a tab should be repainted whenever the mouse enters or exits it */
207     public static final int REPAINT_ON_MOUSE_ENTER_TAB = 1;
208     /** Repaint policy bitmask indicating that all tabs should be repainted whenever the mouse enters or leaves the
209      * area in which tabs are painted */

210     public static final int REPAINT_ALL_ON_MOUSE_ENTER_TABS_AREA = 3;
211     /** Repaint policy bitmask indicating that the tab should be repainted when the mouse enters or exits the close
212      * button region */

213     public static final int REPAINT_ON_MOUSE_ENTER_CLOSE_BUTTON = 4;
214     /** Repaint policy bitmask indicating that the tab should be repainted on mouse pressed events */
215     public static final int REPAINT_ON_MOUSE_PRESSED = 8;
216     /** Repaint policy bitmask indicating that the selected tab should be repainted when the activated state changes */
217     public static final int REPAINT_SELECTION_ON_ACTIVATION_CHANGE = 16;
218     /** Repaint policy bitmask indicating that all tabs should be repainted when the activated state changes */
219     public static final int REPAINT_ALL_TABS_ON_ACTIVATION_CHANGE = 32; //includes selection
220
/** Repaint policy bitmask indicating that a tab should be repainted when it becomes selected/unselected */
221     public static final int REPAINT_ON_SELECTION_CHANGE = 64;
222     /** Repaint policy bitmask indicating that all tabs should be repainted whenever the selection changes */
223     public static final int REPAINT_ALL_TABS_ON_SELECTION_CHANGE = 128;
224     /** Repaint policy bitmask indicating that the tab should be repainted when the close button is pressed */
225     public static final int REPAINT_ON_CLOSE_BUTTON_PRESSED = 256;
226
227     /**
228      * Get the state of a given tab. Subclasses are expected to override this
229      * to provide information about states such as clipping which can only be
230      * found out via layout information, such as LEFTMOST, RIGHTMOST, CLIP_LEFT
231      * and CLIP_RIGHT. The state is used by tab renderers to determine how they
232      * should paint themselves.
233      *
234      * @param tab The index of the tab
235      * @return The state
236      */

237     public int getState(int tab) {
238         int result = 0;
239         if (tab == pressedIndex) {
240             result |= PRESSED;
241         }
242         if (tab == containsMouseIndex) {
243             result |= ARMED;
244         }
245         if (tab == closeButtonContainsMouseIndex) {
246             result |= CLOSE_BUTTON_ARMED;
247         }
248         if (tab == mousePressedInCloseButtonIndex) {
249             result |= MOUSE_PRESSED_IN_CLOSE_BUTTON;
250         }
251         if (mouseInTabsArea) {
252             result |= MOUSE_IN_TABS_AREA;
253         }
254         if (active) {
255             result |= ACTIVE;
256         }
257         if (tab == selectedIndex) {
258             result |= SELECTED;
259         }
260         if (tab != 0 && tab == selectedIndex + 1) {
261             result |= AFTER_SELECTED;
262         }
263         if (tab == selectedIndex - 1) {
264             result |= BEFORE_SELECTED;
265         }
266         if (tab == containsMouseIndex - 1) {
267             result |= BEFORE_ARMED;
268         }
269         if (isAlarmTab(tab)) {
270             result |= ATTENTION;
271         }
272         return result;
273     }
274     
275     /** For debugging, enables fetching tab state as a string */
276     String JavaDoc getStateString (int tab) {
277         return stateToString (getState(tab));
278     }
279
280     /**
281      * Clear all mouse position related state information. This should be done
282      * following events in the model that alter the state sufficiently that the
283      * cached information is probably wrong.
284      */

285     public void clearTransientStates() {
286         pressedIndex = -1;
287         containsMouseIndex = -1;
288         closeButtonContainsMouseIndex = -1;
289         mousePressedInCloseButtonIndex = -1;
290         mouseInTabsArea = false;
291         lastChangeType = NO_CHANGE;
292         lastChange = 0;
293         prev = -1;
294         curr = -1;
295     }
296
297     /**
298      * Set the index of the tab over which a mouse button has been pressed.
299      *
300      * @param i The tab which is pressed, or -1 to clear PRESSED from the state
301      * of the previously pressed tab
302      * @return Index of the tab which previously had the state PRESSED, or -1
303      */

304     public final int setPressed(int i) {
305         prev = pressedIndex;
306         pressedIndex = i;
307         curr = i;
308         possibleChange(prev, curr, PRESSED);
309         return prev;
310     }
311
312     /**
313      * Set the index of the tab which currently contains the mouse cursor.
314      *
315      * @param i The tab which contains the mouse cursor, or -1 to clear ARMED
316      * from the state of the tab
317      * @return Index of the tab which previously had the state ARMED, or -1
318      */

319     public final int setContainsMouse(int i) {
320         prev = containsMouseIndex;
321         containsMouseIndex = i;
322         curr = i;
323         possibleChange(prev, curr, ARMED);
324         return prev;
325     }
326
327     /**
328      * Set the index of the tab whose close button contains the mouse cursor.
329      *
330      * @param i The index of the tab whose close button contains the mouse
331      * cursor, or -1 to clear CLOSE_BUTTON_CONTAINS_MOUSE from the
332      * state of the tab which previously had it
333      * @return Index of the tab which formerly had the state
334      * CLOSE_BUTTON_CONTAINS_MOUSE, or -1
335      */

336     public final int setCloseButtonContainsMouse(int i) {
337         prev = closeButtonContainsMouseIndex;
338         closeButtonContainsMouseIndex = i;
339         curr = i;
340         possibleChange(prev, curr, CLOSE_BUTTON_ARMED);
341         return prev;
342     }
343
344     /**
345      * Set the index of the tab in which the mouse button has been pressed in
346      * the close button. This is distinct from the combination of
347      * CLOSE_BUTTON_ARMED and PRESSED, since the user may press the close button
348      * and then drag away from the close button (clearing the CLOSE_BUTTON_ARMED
349      * state) to abort closing a tab.
350      *
351      * @param i The tab in which the mouse was pressed while over the close
352      * button
353      * @return Index of the tab which previously had the state
354      * MOUSE_PRESSED_IN_CLOSE_BUTTON, or -1
355      */

356     public final int setMousePressedInCloseButton(int i) {
357         prev = mousePressedInCloseButtonIndex;
358         mousePressedInCloseButtonIndex = i;
359         curr = i;
360         possibleChange(prev, curr, MOUSE_PRESSED_IN_CLOSE_BUTTON);
361         return prev;
362     }
363
364     /**
365      * Set the index of the tab which is currently selected. Note that users of
366      * this class must ensure that this value stays up to date when changes
367      * occur in the model such as inserting tabs before the selected one.
368      *
369      * @param i The tab index which is selected
370      * @return Index of the tab which previously was selected, or -1 if none
371      */

372     public final int setSelected(int i) {
373         prev = selectedIndex;
374         selectedIndex = i;
375         curr = i;
376         removeAlarmTab(i);
377         possibleChange(prev, curr, SELECTED);
378         return prev;
379     }
380
381     /**
382      * Set the condition for all tabs of the mouse being in the tabs area.
383      *
384      * @param b Whether the mouse is in the tabs area or not
385      * @return The previous state with regard to the mouse being in the tabs
386      * area
387      */

388     public final boolean setMouseInTabsArea(boolean b) {
389         boolean prev = mouseInTabsArea;
390         mouseInTabsArea = b;
391         possibleChange(prev, b, MOUSE_IN_TABS_AREA);
392         return prev;
393     }
394
395     /**
396      * Set the condition for all tabs of the component being activated.
397      *
398      * @param b Whether or not the component is activated
399      * @return The previous state with regard to the component being activated
400      */

401     public final boolean setActive(boolean b) {
402         boolean prev = active;
403         active = b;
404         possibleChange(prev, b, ACTIVE);
405         removeAlarmTab(selectedIndex);
406         return prev;
407     }
408     
409     private boolean isAlarmTab (int tab) {
410         return attentionToggle && alarmTabs.contains(new Integer JavaDoc(tab));
411     }
412     
413     private final HashSet JavaDoc<Integer JavaDoc> alarmTabs = new HashSet JavaDoc<Integer JavaDoc>(6);
414     
415     /** Add a tab to the list of those which should "flash" or otherwise give
416      * some notification to the user to get their attention */

417     public final void addAlarmTab (int alarmTab) {
418         Integer JavaDoc in = new Integer JavaDoc(alarmTab);
419         boolean added = alarmTabs.contains(in);
420         boolean wasEmpty = alarmTabs.isEmpty();
421         if (!added) {
422             alarmTabs.add (new Integer JavaDoc(alarmTab));
423             repaintTab (alarmTab);
424         }
425         if (wasEmpty) {
426             startAlarmTimer();
427             attentionToggle = true;
428             repaintTab (alarmTab);
429         }
430     }
431     
432     /** Remove a tab to the list of those which should "flash" or otherwise give
433      * some notification to the user to get their attention */

434     public final void removeAlarmTab (int alarmTab) {
435         Integer JavaDoc in = new Integer JavaDoc(alarmTab);
436         boolean contained = alarmTabs.contains(in);
437         if (contained) {
438             alarmTabs.remove(in);
439             boolean empty = alarmTabs.isEmpty();
440             boolean wasAttentionToggled = attentionToggle;
441             if (alarmTabs.isEmpty()) {
442                 stopAlarmTimer();
443             }
444             if (wasAttentionToggled) {
445                 repaintTab(alarmTab);
446             }
447         }
448     }
449     
450     private Timer JavaDoc alarmTimer = null;
451     private boolean attentionToggle = false;
452     private final void startAlarmTimer() {
453         if (alarmTimer == null) {
454             ActionListener JavaDoc al = new ActionListener JavaDoc() {
455                 public void actionPerformed (ActionEvent JavaDoc ae) {
456                     attentionToggle = !attentionToggle;
457                     Timer JavaDoc timer = (Timer JavaDoc) ae.getSource();
458                     for (Iterator JavaDoc i=alarmTabs.iterator(); i.hasNext();) {
459                         repaintTab (((Integer JavaDoc) i.next()).intValue());
460                     }
461                 }
462             };
463             alarmTimer = new Timer JavaDoc (700, al);
464             alarmTimer.setRepeats(true);
465         }
466         alarmTimer.start();
467     }
468     
469     private final void stopAlarmTimer() {
470         if (alarmTimer != null && alarmTimer.isRunning()) {
471             alarmTimer.stop();
472             attentionToggle = false;
473             repaintAllTabs(); //XXX optimize
474
}
475     }
476     
477     boolean hasAlarmTabs() {
478         return alarmTabs != null && !alarmTabs.isEmpty();
479     }
480     
481     void pruneAlarmTabs(int max) {
482         if (!hasAlarmTabs()) {
483             return;
484         }
485         for (Iterator JavaDoc i=alarmTabs.iterator(); i.hasNext();) {
486             if (((Integer JavaDoc) i.next()).intValue() >= max) {
487                 i.remove();
488             }
489         }
490         if (alarmTabs.isEmpty()) {
491             stopAlarmTimer();
492         }
493     }
494     
495     int[] getAlarmTabs() {
496         int[] alarms = (int[]) Utilities.toPrimitiveArray((Integer JavaDoc[]) alarmTabs.toArray(new Integer JavaDoc[0]));
497         Arrays.sort(alarms);
498         return alarms;
499     }
500     
501     //Handling of insertions/deletions where we'll need to update the
502
//list of blinking tabs here.
503
void intervalAdded (ListDataEvent JavaDoc evt) {
504         if (!hasAlarmTabs()) return;
505         int start = evt.getIndex0();
506         int end = evt.getIndex1();
507         int[] alarms = (int[]) Utilities.toPrimitiveArray((Integer JavaDoc[]) alarmTabs.toArray(new Integer JavaDoc[0]));
508         boolean changed = false;
509         for (int i=0; i < alarms.length; i++) {
510             if (alarms[i] >= start) {
511                 alarms[i] += (end - start) + 1;
512                 changed = true;
513             }
514         }
515         if (changed) {
516             alarmTabs.clear();
517             for (int i=0; i < alarms.length; i++) {
518                 addAlarmTab(alarms[i]);
519             }
520         }
521     }
522     
523     void intervalRemoved (ListDataEvent JavaDoc evt) {
524         if (!hasAlarmTabs()) return;
525         int start = evt.getIndex0();
526         int end = evt.getIndex1();
527         
528         int[] alarms = (int[]) Utilities.toPrimitiveArray((Integer JavaDoc[]) alarmTabs.toArray(new Integer JavaDoc[0]));
529         Arrays.sort(alarms);
530         
531         if (end == start) {
532             //Faster to handle this case separately
533
boolean changed = true;
534             for (int i=0; i < alarms.length; i++) {
535                 if (alarms[i] > end) {
536                     alarms[i]--;
537                 } else if (alarms[i] == end) {
538                     alarms[i] = -1;
539                 }
540             }
541             if (changed) {
542                 alarmTabs.clear();
543                 boolean added = false;
544                 for (int i=0; i < alarms.length; i++) {
545                     if (alarms[i] != -1) {
546                         addAlarmTab(alarms[i]);
547                         added = true;
548                     }
549                 }
550                 if (!added) {
551                     stopAlarmTimer();
552                 }
553             }
554             return;
555         }
556         
557         boolean changed = false;
558         for (int i=0; i < alarms.length; i++) {
559             if (alarms[i] >= start && alarms[i] <= end) {
560                 alarms[i] = -1;
561                 changed = true;
562             }
563         }
564         for (int i=0; i < alarms.length; i++) {
565             if (alarms[i] > end) {
566                 alarms[i] -= (end - start) + 1;
567                 changed = true;
568             }
569         }
570         if (changed) {
571             alarmTabs.clear();
572             boolean added = false;
573             for (int i=0; i < alarms.length; i++) {
574                 if (alarms[i] != -1) {
575                     addAlarmTab(alarms[i]);
576                     added = true;
577                 }
578             }
579             if (!added) {
580                 stopAlarmTimer();
581             }
582         }
583     }
584     
585     void indicesAdded (ComplexListDataEvent e) {
586         if (!hasAlarmTabs()) return;
587         int[] alarms = (int[]) Utilities.toPrimitiveArray((Integer JavaDoc[]) alarmTabs.toArray(new Integer JavaDoc[0]));
588         java.util.Arrays.sort(alarms);
589
590         int[] indices = e.getIndices();
591         java.util.Arrays.sort(indices);
592         
593         boolean changed = false;
594         for (int i=0; i < indices.length; i++) {
595             for (int j=0; j < alarms.length; j++) {
596                 if (alarms[j] >= indices[i]) {
597                     alarms[j]++;
598                     changed = true;
599                 }
600             }
601         }
602         if (changed) {
603             alarmTabs.clear();
604             for (int i=0; i < alarms.length; i++) {
605                 if (alarms[i] != -1) {
606                     addAlarmTab(alarms[i]);
607                 }
608             }
609         }
610     }
611     
612     void indicesRemoved (ComplexListDataEvent e) {
613         if (!hasAlarmTabs()) return;
614         int[] indices = e.getIndices();
615         java.util.Arrays.sort(indices);
616         
617         int[] alarms = (int[]) Utilities.toPrimitiveArray((Integer JavaDoc[]) alarmTabs.toArray(new Integer JavaDoc[0]));
618         java.util.Arrays.sort(alarms);
619         
620         if (alarms[alarms.length -1] < indices[0]) {
621             //Some tab removed after the last blinking tab, don't care
622
return;
623         }
624
625         boolean changed = false;
626         for (int i=0; i < alarms.length; i++) {
627             //First weed out all deleted alarm tabs
628
for (int j=0; j < indices.length; j++) {
629                 if (alarms[i] == indices[j]) {
630                     alarms[i] = -1;
631                     changed = true;
632                 }
633             }
634         }
635         for (int i=0; i < alarms.length; i++) {
636             //Now decrement those that remain that are affected
637
int alarm = alarms[i];
638             for (int j=0; j < indices.length; j++) {
639                 if (alarm > indices[j]) {
640                     alarms[i]--;
641                     changed = true;
642                 }
643             }
644         }
645         
646         if (changed) {
647             alarmTabs.clear();
648             boolean addedSome = false;
649             for (int i=0; i < alarms.length; i++) {
650                 if (alarms[i] >= 0) {
651                     addAlarmTab(alarms[i]);
652                     addedSome = true;
653                 }
654             }
655             if (!addedSome) {
656                 stopAlarmTimer();
657             }
658         }
659         
660         repaintAllTabs();
661     }
662     
663     void indicesChanged (ComplexListDataEvent e) {
664         if (!hasAlarmTabs()) return;
665         if (e instanceof VeryComplexListDataEvent) { //it always will be
666
VeryComplexListDataEvent ve = (VeryComplexListDataEvent) e;
667             
668             ArrayDiff dif = ((VeryComplexListDataEvent) e).getDiff();
669             
670             List JavaDoc old = Arrays.asList(dif.getOldData());
671             List JavaDoc nue = Arrays.asList(dif.getNewData());
672             
673             int[] alarms = (int[]) Utilities.toPrimitiveArray((Integer JavaDoc[]) alarmTabs.toArray(new Integer JavaDoc[0]));
674             
675             boolean changed = false;
676             for (int i=0; i < alarms.length; i++) {
677                 Object JavaDoc o = old.get(alarms[i]);
678                 int idx = nue.indexOf(o);
679                 changed |= idx != alarms[i];
680                 alarms[i] = nue.indexOf(o);
681             }
682             if (changed) {
683                 alarmTabs.clear();
684                 boolean addedSome = false;
685                 for (int i=0; i < alarms.length; i++) {
686                     if (alarms[i] >= 0) {
687                         addAlarmTab(alarms[i]);
688                         addedSome = true;
689                     }
690                 }
691                 if (!addedSome) {
692                     stopAlarmTimer();
693                 }
694             }
695         }
696     }
697
698     
699     void contentsChanged(ListDataEvent JavaDoc evt) {
700         if (!hasAlarmTabs()) return;
701         //Do nothing, just means some text or icons changed
702
}
703
704     //Change types
705
/** Change type indicating no change happened (i.e. calling setSelected() with the same value it was previously
706      * called with).
707      */

708     public static final int NO_CHANGE = 0;
709     /** Change type indicating a change of state for two tabs */
710     public static final int CHANGE_TAB_TO_TAB = 1;
711     /** Change type indicating a change happened (such as the mouse leaving a tab) such that now no tab has the
712      * state previously held by the affected tab */

713     public static final int CHANGE_TAB_TO_NONE = 2;
714     /** Change type indicating that a state was added that no tab previously had */
715     public static final int CHANGE_NONE_TO_TAB = 3;
716     public static final int CHANGE_TAB_TO_SELF = 4;
717     /** Change type indicating one of the boolean state changes, such as STATE_ACTIVE */
718     public static final int ALL_TABS = Integer.MAX_VALUE;
719
720     protected void possibleChange(boolean prevVal, boolean currVal, int type) {
721         if (prevVal == currVal) {
722             lastChangeType = NO_CHANGE;
723         } else {
724             lastChangeType = ALL_TABS;
725         }
726         if (lastChangeType != NO_CHANGE) {
727             lastAffected = ALL_TABS;
728             change(ALL_TABS, ALL_TABS, type, lastChangeType);
729         }
730     }
731
732     protected void possibleChange(int lastTab, int currTab, int type) {
733         if (lastTab == currTab) {
734             lastChangeType = NO_CHANGE;
735         } else {
736             if (currTab == -1) {
737                 lastChangeType = CHANGE_TAB_TO_NONE;
738             } else if (lastTab == -1) {
739                 lastChangeType = CHANGE_NONE_TO_TAB;
740             } else {
741                 lastChangeType = CHANGE_TAB_TO_TAB;
742             }
743         }
744         if (lastChangeType != NO_CHANGE) {
745             lastAffected = currTab;
746             change(lastTab, currTab, type, lastChangeType);
747         }
748     }
749
750     public String JavaDoc toString() {
751         StringBuffer JavaDoc sb = new StringBuffer JavaDoc(50);
752         sb.append("TabState [lastTab=");
753         sb.append(tabToString(prev));
754         sb.append(" currTab=");
755         sb.append(tabToString(curr));
756         sb.append(" lastAffected=");
757         sb.append(tabToString(lastAffected));
758         sb.append(" lastChangeType=");
759         sb.append(changeToString(lastChangeType));
760         sb.append(" lastChange=");
761         sb.append(stateToString(lastChange));
762         sb.append(" <active=");
763         sb.append(active);
764         sb.append(" sel=");
765         sb.append(tabToString(selectedIndex));
766         sb.append(" mouse=");
767         sb.append(tabToString(containsMouseIndex));
768         sb.append(" inTabs=");
769         sb.append(mouseInTabsArea);
770         sb.append(" pressed=");
771         sb.append(tabToString(pressedIndex));
772         sb.append(" inCloseButton=");
773         sb.append(tabToString(closeButtonContainsMouseIndex));
774         sb.append(" pressedCloseButton=");
775         sb.append(tabToString(mousePressedInCloseButtonIndex));
776         sb.append(">]");
777         return sb.toString();
778     }
779
780     /**
781      * Called when a setter for a tab index has produced a change in a
782      * state-affecting property, such as which tab contains the mouse. Fetches
783      * the repaint policies, and if the change is one that the policy says
784      * should produce a repaint, calls repaintTab for the appropriate tabs.
785      *
786      * @param lastTab The tab previously holding the state which has changed,
787      * or -1
788      * @param currTab The tab currently holding the state which has changed,
789      * or -1
790      * @param type The thing that changed. This will be one of the state
791      * constants.
792      * @param changeType This is one of the defined change types such as
793      * ALL_TABS, TAB_TO_TAB, etc.
794      */

795     protected void change(int lastTab, int currTab, int type, int changeType) {
796         lastChange = type;
797 // System.err.println("Change-type: " + stateToString(type) + " - " + changeToString (changeType) + " from " + tabToString (lastTab) + " to " + tabToString (currTab));
798
if (changeType == CHANGE_TAB_TO_TAB) {
799             maybeRepaint(lastTab, type);
800         } else if (changeType == CHANGE_TAB_TO_NONE) {
801             maybeRepaint (lastTab, type);
802             return;
803         } else if (changeType == ALL_TABS && (getRepaintPolicy(currTab) & REPAINT_ALL_ON_MOUSE_ENTER_TABS_AREA) != 0) {
804             repaintAllTabs();
805             return;
806         }
807         maybeRepaint(currTab, type);
808     }
809
810     protected void maybeRepaint(int tab, int type) {
811         int rpol = getRepaintPolicy (tab);
812         boolean go = false;
813         switch (type) {
814             case ACTIVE:
815                 go = (rpol
816                         & REPAINT_SELECTION_ON_ACTIVATION_CHANGE)
817                         != 0;
818                 if ((rpol
819                         & REPAINT_ALL_TABS_ON_ACTIVATION_CHANGE)
820                         != 0) {
821                     type = ALL_TABS;
822                     go = true;
823                 }
824                 break;
825             case ARMED:
826                 go = (rpol & REPAINT_ON_MOUSE_ENTER_TAB) != 0 ||
827                     tab == closeButtonContainsMouseIndex;
828                 closeButtonContainsMouseIndex = -1;
829                 break;
830             case CLOSE_BUTTON_ARMED:
831                 go = (rpol & REPAINT_ON_MOUSE_ENTER_CLOSE_BUTTON)
832                         != 0;
833                 break;
834             case MOUSE_IN_TABS_AREA:
835                 go = (rpol
836                         & REPAINT_ALL_ON_MOUSE_ENTER_TABS_AREA)
837                         != 0;
838                 break;
839             case MOUSE_PRESSED_IN_CLOSE_BUTTON:
840                 go = (rpol & REPAINT_ON_CLOSE_BUTTON_PRESSED)
841                         != 0;
842                 break;
843             case PRESSED:
844                 go = (rpol & REPAINT_ON_MOUSE_PRESSED) != 0;
845                 break;
846             case SELECTED:
847                 go = (rpol & REPAINT_ON_SELECTION_CHANGE) != 0;
848                 if ((rpol & REPAINT_ALL_TABS_ON_SELECTION_CHANGE)
849                         != 0) {
850                     type = ALL_TABS;
851                     go = true;
852                 }
853                 break;
854             case ATTENTION:
855                 go = true;
856         }
857         if (go) {
858             if (type == ALL_TABS) {
859                 repaintAllTabs();
860             } else {
861                 repaintTab(tab);
862             }
863         }
864     }
865
866     protected abstract void repaintTab(int tab);
867
868     protected abstract void repaintAllTabs();
869
870     static final String JavaDoc changeToString(int change) {
871         switch (change) {
872             case NO_CHANGE:
873                 return "no change"; //NOI18N
874
case CHANGE_TAB_TO_TAB:
875                 return "tab to tab"; //NOI18N
876
case CHANGE_TAB_TO_NONE:
877                 return "tab to none"; //NOI18N
878
case CHANGE_NONE_TO_TAB:
879                 return "none to tab"; //NOI18N
880
case CHANGE_TAB_TO_SELF:
881                 return "tab to self"; //NOI18N
882
case ALL_TABS:
883                 return "all tabs"; //NOI18N
884
default :
885                 return "??? " + change; //NOI18N
886
}
887     }
888
889     static final String JavaDoc tabToString(int tab) {
890         if (tab == ALL_TABS) {
891             return "all tabs"; //NOI18N
892
} else if (tab == -1) {
893             return "none"; //NOI18N
894
} else {
895             return Integer.toString(tab);
896         }
897     }
898
899     /**
900      * Static utility method to get a string representation of a state
901      */

902     static final String JavaDoc stateToString(int st) {
903         String JavaDoc[] states = new String JavaDoc[]{
904             "clip right", "clip left", "armed", "pressed", "selected", "active", "not onscreen", "leftmost", //NOI18N
905
"rightmost", "in closebutton", "before selected", "after selected", "mouse in tabs area", //NOI18N
906
"mouse pressed in close button" //NOI18N
907
}; //NOI18N
908
int[] vals = new int[]{
909             CLIP_RIGHT, CLIP_LEFT, ARMED, PRESSED, SELECTED, ACTIVE,
910             NOT_ONSCREEN, LEFTMOST, RIGHTMOST, CLOSE_BUTTON_ARMED,
911             BEFORE_SELECTED, AFTER_SELECTED, MOUSE_IN_TABS_AREA,
912             MOUSE_PRESSED_IN_CLOSE_BUTTON};
913         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
914         for (int i = 0; i < vals.length; i++) {
915             if ((st & vals[i]) != 0) {
916                 if (sb.length() > 0) {
917                     sb.append(',');
918                 }
919                 sb.append(states[i]);
920             }
921         }
922         if (sb.length() == 0) {
923             sb.append("no flags set"); //NOI18N
924
}
925         sb.append("=");
926         sb.append(st);
927         return sb.toString();
928     }
929
930     static String JavaDoc repaintPolicyToString (int policy) {
931         if (policy == 0) {
932             return "repaint nothing";
933         }
934         String JavaDoc[] names = new String JavaDoc[] {
935             "REPAINT_ON_MOUSE_ENTER_TAB",
936             "REPAINT_ALL_ON_MOUSE_ENTER_TABS_AREA",
937             "REPAINT_ON_MOUSE_ENTER_CLOSE_BUTTON",
938             "REPAINT_ON_MOUSE_PRESSED",
939             "REPAINT_SELECTION_ON_ACTIVATION_CHANGE",
940             "REPAINT_ALL_TABS_ON_ACTIVATION_CHANGE",
941             "REPAINT_ON_SELECTION_CHANGE",
942             "REPAINT_ALL_TABS_ON_SELECTION_CHANGE",
943             "REPAINT_ON_CLOSE_BUTTON_PRESSED",
944         };
945         int[] vals = new int[] {
946             REPAINT_ON_MOUSE_ENTER_TAB,
947             REPAINT_ALL_ON_MOUSE_ENTER_TABS_AREA,
948             REPAINT_ON_MOUSE_ENTER_CLOSE_BUTTON,
949             REPAINT_ON_MOUSE_PRESSED,
950             REPAINT_SELECTION_ON_ACTIVATION_CHANGE,
951             REPAINT_ALL_TABS_ON_ACTIVATION_CHANGE,
952             REPAINT_ON_SELECTION_CHANGE,
953             REPAINT_ALL_TABS_ON_SELECTION_CHANGE,
954             REPAINT_ON_CLOSE_BUTTON_PRESSED,
955         };
956         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
957         for (int i=0; i < vals.length; i++) {
958             if ((policy & vals[i]) != 0) {
959                 sb.append (names[i]);
960                 if (i != vals.length-1) {
961                     sb.append ('+');
962                 }
963             }
964         }
965         return sb.toString();
966     }
967
968     /**
969      * Get the repaint policy that will be used to determine what tabs to repaint, based on state changes.
970      * The default implementation in BasicTabDisplayerUI simply ignores the tab argument and returns a
971      * single policy for all tabs created in <code>BasicTabDisplayerUI.createRepaintPolicy()</code>
972      *
973      * @param tab Index of tab in question
974      * @return Type of repaint policy for given tab
975      */

976     public abstract int getRepaintPolicy(int tab);
977 }
978
Popular Tags