All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.netbeans.swing.tabcontrol.plaf.TabState Maven / Gradle / Ivy

There is a newer version: RELEASE230
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.netbeans.swing.tabcontrol.plaf;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import javax.swing.Timer;
import javax.swing.event.ListDataEvent;
import org.netbeans.swing.tabcontrol.event.ArrayDiff;
import org.netbeans.swing.tabcontrol.event.ComplexListDataEvent;
import org.netbeans.swing.tabcontrol.event.VeryComplexListDataEvent;
import org.openide.util.Utilities;

/**
 * Used by BasicTabDisplayerUI and its subclasses.
 * Tracks and manages the state of tabs, mainly which one currently contains the
 * mouse, if the mouse is in the close button, if the tab is adjacent to a
 * selected tab, if it is leftmost, rightmost, active, etc. This class hides
 * most of the complexity of deciding what mouse events should trigger a repaint
 * of what areas in an optimized way.  It provides setters which a mouse
 * listener can call to indicate that the mouse has, say, moved into a tab, or
 * from one tab to another, or the selection has changed, etc.
 * 

* Essentially, this class is fed indices of tabs that have various states (selected, * contains mouse, etc.), figures out if this affects one tab, two tabs (a different * tab had the state, such as the mouse moving from one tab to another) or all tabs (activated). It determines * a change type, and consults a repaint policy (an integer bitmask) to decide * if one, both, all or no tabs should be repainted. *

* The typical use case is to implement a subclass, and override getState() to * mix in bitmasks for things like whether a tab is clipped, etc. - things the * base implementation can't know about. * *

* Subclasses implement the repaintTab() method to do the actual * repainting, and implement getRepaintPolicy(). The repainting will be * called if an event happens that changes the state in a way that the repaint policy * bitmask indicates should cause a repaint. *

* BasicTabDisplayerUI implements a mouse listener which will call the appropriate * methods when the mouse enters/exits tabs, etc. *

* Details *

* State is composed as an integer bitmask which covers all of the supported * states of a tab that may affect the way they paint. These are also the values that * are passed to the methods of a TabCellRenderer to tell it how to * paint itself. Two other integer * bitmasks are used: The changeType, which indicates whether a * change was from one tab to another tab, one tab to no tab (i.e. selection set * to -1), one tab to the same tab (i.e. the mouse moved out of the tab control, * and so the tab with the mouse in it is now no tab). RepaintPolicy is an integer * bitmask composed of conditions under which the control should repaint one or all * tabs, and determines what types of changes actually trigger repaints. *

* Subclasses are expected to override getState() to provide * information about non-mouse-or-focus related states, such as clipping of * scrollable tabs. Predefined states for tabs are: CLIP_RIGHT, CLIP_LEFT, * ARMED, PRESSED, SELECTED, ACTIVE, NOT_ONSCREEN, LEFTMOST, RIGHTMOST, * CLOSE_BUTTON_ARMED, BEFORE_SELECTED, AFTER_SELECTED, MOUSE_IN_TABS_AREA, * MOUSE_PRESSED_IN_CLOSE_BUTTON. Subclasses must handle returning the following * states if they wish to, which are not handled directly in TabState: LEFTMOST, * RIGHTMOST, CLIP_LEFT, CLIP_RIGHT, NOT_ONSCREEN. *

* Most of the states are fairly self-explanatory; NOT_ONSCREEN is useful as an * optimization so that no work is done for events that would try to produce a * repaint for something not visible; CLIP_* refers to the case in scrollable * tab UIs, in which a tab may only be partially visible; * MOUSE_PRESSED_IN_CLOSE_BUTTON is distinct because the CLOSE_BUTTON_ARMED * state will be reset if the mouse moves out of the close button area, but UIs * should perform a close action if the mouse was pressed over the close button, * moved away from the close button and then back to it, so this state preserves * the information that the originating location of a mouse press was in the * close button. */ public abstract class TabState { /** * Bitmask for state of tabs clipped on the right side - that is, partially * displayed */ public static final int CLIP_RIGHT = 1; /** * Bitmask for state of tabs clipped on the right side - that is, partially * displayed */ public static final int CLIP_LEFT = 2; /** * Bitmask indicating the tab contains the mouse */ public static final int ARMED = 4; /** * Bitmask indicating the tab contains the mouse and the mouse button has * been pressed */ public static final int PRESSED = 8; /** * Bitmask indicating the tab is selected */ public static final int SELECTED = 16; /** * Bitmask indicating the tab is activated */ public static final int ACTIVE = 32; /** * State bitmask indicating a tab is not displayed at all and shouldn't be * painted. Implementations may more simply avoid not painting a tab by not * including it in the range returned by getFirstVisibleTab()/getLastVisibleTab(). * This state exists so that implementations have the option of returning * the entire range of tabs as visible and determining if one is truly * visble or not in getTabState() - sometimes doing it this way will be less * expensive. */ public static final int NOT_ONSCREEN = 64; /** * Bitmask indicating the tab is at the extreme left and * not clipped */ public static final int LEFTMOST = 128; /** * Bitmask indicating the tab is at the extreme right and * not clipped */ public static final int RIGHTMOST = 256; /** * Bitmask indicating that the tab contains the mouse and the mouse is in * the close button */ public static final int CLOSE_BUTTON_ARMED = 512; /** * Bitmask indicating that the tab's index is that of the selected index * less one */ public static final int BEFORE_SELECTED = 1024; /** * Bitmask indicating that the tab's index is that of the selected index * plus one */ public static final int AFTER_SELECTED = 2048; /** * Bitmask indicating that the mouse is in the tabs area */ public static final int MOUSE_IN_TABS_AREA = 4096; /** * Bitmask indicating that the mouse is inside the close button and has * been pressed. */ public static final int MOUSE_PRESSED_IN_CLOSE_BUTTON = 8192; /** * Bitmask indicating that the tab is in "attention" mode - blinking or * flashing to get the user's attention. */ public static final int ATTENTION = 16384; /** * Bitmask indicating that the tab's index is that of the armed index * less one */ public static final int BEFORE_ARMED = 32768; /** * Indicates the given tab is 'busy', i.e. the editor is still loading its data * or there's some lengthy process running in that tab. * @since 1.34 */ public static final int BUSY = 2*32768; /** * Indicates that the tab should be highlighted to draw user's attention. * @since 1.38 */ public static final int HIGHLIGHT = 2*BUSY; /** * Indicates the last constant defined - renderers that wish to add their * own bitmasks should use multiples of this number */ public static int STATE_LAST = HIGHLIGHT; private int pressedIndex = -1; private int containsMouseIndex = -1; private int closeButtonContainsMouseIndex = -1; private int mousePressedInCloseButtonIndex = -1; private boolean mouseInTabsArea = false; private boolean active = false; private int selectedIndex = -1; private int prev = -1; private int curr = -1; private int lastChangeType = NO_CHANGE; private int lastAffected = 0; private int lastChange = 0; /** Repaint policy bitmask indicating that a tab should be repainted whenever the mouse enters or exits it */ public static final int REPAINT_ON_MOUSE_ENTER_TAB = 1; /** Repaint policy bitmask indicating that all tabs should be repainted whenever the mouse enters or leaves the * area in which tabs are painted */ public static final int REPAINT_ALL_ON_MOUSE_ENTER_TABS_AREA = 3; /** Repaint policy bitmask indicating that the tab should be repainted when the mouse enters or exits the close * button region */ public static final int REPAINT_ON_MOUSE_ENTER_CLOSE_BUTTON = 4; /** Repaint policy bitmask indicating that the tab should be repainted on mouse pressed events */ public static final int REPAINT_ON_MOUSE_PRESSED = 8; /** Repaint policy bitmask indicating that the selected tab should be repainted when the activated state changes */ public static final int REPAINT_SELECTION_ON_ACTIVATION_CHANGE = 16; /** Repaint policy bitmask indicating that all tabs should be repainted when the activated state changes */ public static final int REPAINT_ALL_TABS_ON_ACTIVATION_CHANGE = 32; //includes selection /** Repaint policy bitmask indicating that a tab should be repainted when it becomes selected/unselected */ public static final int REPAINT_ON_SELECTION_CHANGE = 64; /** Repaint policy bitmask indicating that all tabs should be repainted whenever the selection changes */ public static final int REPAINT_ALL_TABS_ON_SELECTION_CHANGE = 128; /** Repaint policy bitmask indicating that the tab should be repainted when the close button is pressed */ public static final int REPAINT_ON_CLOSE_BUTTON_PRESSED = 256; /** * Get the state of a given tab. Subclasses are expected to override this * to provide information about states such as clipping which can only be * found out via layout information, such as LEFTMOST, RIGHTMOST, CLIP_LEFT * and CLIP_RIGHT. The state is used by tab renderers to determine how they * should paint themselves. * * @param tab The index of the tab * @return The state */ public int getState(int tab) { int result = 0; if (tab == pressedIndex) { result |= PRESSED; } if (tab == containsMouseIndex) { result |= ARMED; } if (tab == closeButtonContainsMouseIndex) { result |= CLOSE_BUTTON_ARMED; } if (tab == mousePressedInCloseButtonIndex) { result |= MOUSE_PRESSED_IN_CLOSE_BUTTON; } if (mouseInTabsArea) { result |= MOUSE_IN_TABS_AREA; } if (active) { result |= ACTIVE; } if (tab == selectedIndex) { result |= SELECTED; } if (tab != 0 && tab == selectedIndex + 1) { result |= AFTER_SELECTED; } if (tab == selectedIndex - 1) { result |= BEFORE_SELECTED; } if (tab == containsMouseIndex - 1) { result |= BEFORE_ARMED; } if (isAlarmTab(tab)) { result |= ATTENTION; } if (isHighlightTab( tab)) { result |= HIGHLIGHT; } return result; } /** For debugging, enables fetching tab state as a string */ String getStateString (int tab) { return stateToString (getState(tab)); } /** * Clear all mouse position related state information. This should be done * following events in the model that alter the state sufficiently that the * cached information is probably wrong. */ public void clearTransientStates() { pressedIndex = -1; containsMouseIndex = -1; closeButtonContainsMouseIndex = -1; mousePressedInCloseButtonIndex = -1; mouseInTabsArea = false; lastChangeType = NO_CHANGE; lastChange = 0; prev = -1; curr = -1; } /** * Set the index of the tab over which a mouse button has been pressed. * * @param i The tab which is pressed, or -1 to clear PRESSED from the state * of the previously pressed tab * @return Index of the tab which previously had the state PRESSED, or -1 */ public final int setPressed(int i) { prev = pressedIndex; pressedIndex = i; curr = i; possibleChange(prev, curr, PRESSED); return prev; } /** * Set the index of the tab which currently contains the mouse cursor. * * @param i The tab which contains the mouse cursor, or -1 to clear ARMED * from the state of the tab * @return Index of the tab which previously had the state ARMED, or -1 */ public final int setContainsMouse(int i) { prev = containsMouseIndex; containsMouseIndex = i; curr = i; possibleChange(prev, curr, ARMED); return prev; } /** * Set the index of the tab whose close button contains the mouse cursor. * * @param i The index of the tab whose close button contains the mouse * cursor, or -1 to clear CLOSE_BUTTON_CONTAINS_MOUSE from the * state of the tab which previously had it * @return Index of the tab which formerly had the state * CLOSE_BUTTON_CONTAINS_MOUSE, or -1 */ public final int setCloseButtonContainsMouse(int i) { prev = closeButtonContainsMouseIndex; closeButtonContainsMouseIndex = i; curr = i; possibleChange(prev, curr, CLOSE_BUTTON_ARMED); return prev; } /** * Set the index of the tab in which the mouse button has been pressed in * the close button. This is distinct from the combination of * CLOSE_BUTTON_ARMED and PRESSED, since the user may press the close button * and then drag away from the close button (clearing the CLOSE_BUTTON_ARMED * state) to abort closing a tab. * * @param i The tab in which the mouse was pressed while over the close * button * @return Index of the tab which previously had the state * MOUSE_PRESSED_IN_CLOSE_BUTTON, or -1 */ public final int setMousePressedInCloseButton(int i) { prev = mousePressedInCloseButtonIndex; mousePressedInCloseButtonIndex = i; curr = i; possibleChange(prev, curr, MOUSE_PRESSED_IN_CLOSE_BUTTON); return prev; } /** * Set the index of the tab which is currently selected. Note that users of * this class must ensure that this value stays up to date when changes * occur in the model such as inserting tabs before the selected one. * * @param i The tab index which is selected * @return Index of the tab which previously was selected, or -1 if none */ public final int setSelected(int i) { prev = selectedIndex; selectedIndex = i; curr = i; removeAlarmTab(i); removeHighlightTab(i); possibleChange(prev, curr, SELECTED); return prev; } /** * Set the condition for all tabs of the mouse being in the tabs area. * * @param b Whether the mouse is in the tabs area or not * @return The previous state with regard to the mouse being in the tabs * area */ public final boolean setMouseInTabsArea(boolean b) { boolean prev = mouseInTabsArea; mouseInTabsArea = b; possibleChange(prev, b, MOUSE_IN_TABS_AREA); return prev; } /** * Set the condition for all tabs of the component being activated. * * @param b Whether or not the component is activated * @return The previous state with regard to the component being activated */ public final boolean setActive(boolean b) { boolean prev = active; active = b; possibleChange(prev, b, ACTIVE); removeAlarmTab(selectedIndex); removeHighlightTab(selectedIndex); return prev; } private boolean isAlarmTab (int tab) { return attentionToggle && alarmTabs.contains(new Integer(tab)); } private final HashSet alarmTabs = new HashSet(6); /** Add a tab to the list of those which should "flash" or otherwise give * some notification to the user to get their attention */ public final void addAlarmTab (int alarmTab) { Integer in = new Integer(alarmTab); boolean added = alarmTabs.contains(in); boolean wasEmpty = alarmTabs.isEmpty(); if (!added) { alarmTabs.add (new Integer(alarmTab)); repaintTab (alarmTab); } if (wasEmpty) { startAlarmTimer(); attentionToggle = true; repaintTab (alarmTab); } } private final HashSet highlightTabs = new HashSet(6); private boolean isHighlightTab(int tab) { return highlightTabs.contains( tab ) && !alarmTabs.contains( tab ); } /** * Highlight the given tab. * @param highlightTab * @since 1.38 */ public final void addHighlightTab(int highlightTab) { boolean added = highlightTabs.add(highlightTab); if (added) { repaintTab (highlightTab); } } /** Remove a tab to the list of those which should "flash" or otherwise give * some notification to the user to get their attention */ public final void removeAlarmTab (int alarmTab) { Integer in = new Integer(alarmTab); boolean contained = alarmTabs.contains(in); if (contained) { alarmTabs.remove(in); boolean empty = alarmTabs.isEmpty(); boolean wasAttentionToggled = attentionToggle; if (alarmTabs.isEmpty()) { stopAlarmTimer(); } if (wasAttentionToggled) { repaintTab(alarmTab); } } } /** * Turn tab highlight off. * @param highlightTab * @since 1.38 */ public final void removeHighlightTab(int highlightTab) { boolean removed = highlightTabs.remove( highlightTab ); if( removed ) { repaintTab( highlightTab ); } } private Timer alarmTimer = null; private boolean attentionToggle = false; private void startAlarmTimer() { if (alarmTimer == null) { ActionListener al = new ActionListener() { @Override public void actionPerformed (ActionEvent ae) { if( !isDisplayable() ) { //#205421 - stop the timer if the TabbedContainer isn't //in component hierarchy anymore to avoid memory leaks stopAlarmTimer(); } attentionToggle = !attentionToggle; for (Iterator i=alarmTabs.iterator(); i.hasNext();) { repaintTab (((Integer) i.next()).intValue()); } } }; alarmTimer = new Timer (700, al); alarmTimer.setRepeats(true); } alarmTimer.start(); } /** * Check if the tab container is displayable, i.e. connected to component hierarchy. * The default implementation in this class always returns true. * @return True if the tab container this TabState is associated with is * still in component hierarchy, false otherwise. * @since 1.31 */ boolean isDisplayable() { return true; } private final void stopAlarmTimer() { if (alarmTimer != null && alarmTimer.isRunning()) { alarmTimer.stop(); attentionToggle = false; repaintAllTabs(); //XXX optimize } } boolean hasAlarmTabs() { return !alarmTabs.isEmpty(); } boolean hasHighlightTabs() { return !highlightTabs.isEmpty(); } void pruneTabs(int max) { if (!(hasAlarmTabs() || hasHighlightTabs())) { return; } for (Iterator i=alarmTabs.iterator(); i.hasNext();) { if (((Integer) i.next()).intValue() >= max) { i.remove(); } } for (Iterator i=highlightTabs.iterator(); i.hasNext();) { if (((Integer) i.next()).intValue() >= max) { i.remove(); } } if (alarmTabs.isEmpty()) { stopAlarmTimer(); } } int[] getAlarmTabs() { int[] alarms = (int[]) Utilities.toPrimitiveArray((Integer[]) alarmTabs.toArray(new Integer[0])); Arrays.sort(alarms); return alarms; } //Handling of insertions/deletions where we'll need to update the //list of blinking tabs here. void intervalAdded (ListDataEvent evt) { int start = evt.getIndex0(); int end = evt.getIndex1(); if (hasAlarmTabs()) { int[] alarms = (int[]) Utilities.toPrimitiveArray((Integer[]) alarmTabs.toArray(new Integer[0])); boolean changed = false; for (int i=0; i < alarms.length; i++) { if (alarms[i] >= start) { alarms[i] += (end - start) + 1; changed = true; } } if (changed) { alarmTabs.clear(); for (int i=0; i < alarms.length; i++) { addAlarmTab(alarms[i]); } } } if( hasHighlightTabs() ) { int[] highlights = (int[]) Utilities.toPrimitiveArray((Integer[]) highlightTabs.toArray(new Integer[0])); boolean changed = false; for (int i=0; i < highlights.length; i++) { if (highlights[i] >= start) { highlights[i] += (end - start) + 1; changed = true; } } if (changed) { highlightTabs.clear(); for (int i=0; i < highlights.length; i++) { addHighlightTab(highlights[i]); } } } } void intervalRemoved (ListDataEvent evt) { if (hasAlarmTabs()) { int start = evt.getIndex0(); int end = evt.getIndex1(); int[] alarms = (int[]) Utilities.toPrimitiveArray((Integer[]) alarmTabs.toArray(new Integer[0])); Arrays.sort(alarms); if (end == start) { //Faster to handle this case separately boolean changed = true; for (int i=0; i < alarms.length; i++) { if (alarms[i] > end) { alarms[i]--; } else if (alarms[i] == end) { alarms[i] = -1; } } if (changed) { alarmTabs.clear(); boolean added = false; for (int i=0; i < alarms.length; i++) { if (alarms[i] != -1) { addAlarmTab(alarms[i]); added = true; } } if (!added) { stopAlarmTimer(); } } return; } boolean changed = false; for (int i=0; i < alarms.length; i++) { if (alarms[i] >= start && alarms[i] <= end) { alarms[i] = -1; changed = true; } } for (int i=0; i < alarms.length; i++) { if (alarms[i] > end) { alarms[i] -= (end - start) + 1; changed = true; } } if (changed) { alarmTabs.clear(); boolean added = false; for (int i=0; i < alarms.length; i++) { if (alarms[i] != -1) { addAlarmTab(alarms[i]); added = true; } } if (!added) { stopAlarmTimer(); } } } if( hasAlarmTabs() ) { int start = evt.getIndex0(); int end = evt.getIndex1(); int[] highlights = (int[]) Utilities.toPrimitiveArray((Integer[]) highlightTabs.toArray(new Integer[0])); Arrays.sort(highlights); boolean changed = false; for (int i=0; i < highlights.length; i++) { if (highlights[i] >= start && highlights[i] <= end) { highlights[i] = -1; changed = true; } } for (int i=0; i < highlights.length; i++) { if (highlights[i] > end) { highlights[i] -= (end - start) + 1; changed = true; } } if (changed) { highlightTabs.clear(); for (int i=0; i < highlights.length; i++) { if (highlights[i] != -1) { addHighlightTab(highlights[i]); } } } } } void indicesAdded (ComplexListDataEvent e) { if (hasAlarmTabs()) { int[] alarms = (int[]) Utilities.toPrimitiveArray((Integer[]) alarmTabs.toArray(new Integer[0])); java.util.Arrays.sort(alarms); int[] indices = e.getIndices(); java.util.Arrays.sort(indices); boolean changed = false; for (int i=0; i < indices.length; i++) { for (int j=0; j < alarms.length; j++) { if (alarms[j] >= indices[i]) { alarms[j]++; changed = true; } } } if (changed) { alarmTabs.clear(); for (int i=0; i < alarms.length; i++) { if (alarms[i] != -1) { addAlarmTab(alarms[i]); } } } } if( hasHighlightTabs() ) { int[] highlights = (int[]) Utilities.toPrimitiveArray((Integer[]) highlightTabs.toArray(new Integer[0])); java.util.Arrays.sort(highlights); int[] indices = e.getIndices(); java.util.Arrays.sort(indices); boolean changed = false; for (int i=0; i < indices.length; i++) { for (int j=0; j < highlights.length; j++) { if (highlights[j] >= indices[i]) { highlights[j]++; changed = true; } } } if (changed) { highlightTabs.clear(); for (int i=0; i < highlights.length; i++) { if (highlights[i] != -1) { addHighlightTab(highlights[i]); } } } } } void indicesRemoved (ComplexListDataEvent e) { if (hasAlarmTabs()) { int[] indices = e.getIndices(); java.util.Arrays.sort(indices); int[] alarms = (int[]) Utilities.toPrimitiveArray((Integer[]) alarmTabs.toArray(new Integer[0])); java.util.Arrays.sort(alarms); if (alarms[alarms.length -1] >= indices[0]) { boolean changed = false; for (int i=0; i < alarms.length; i++) { //First weed out all deleted alarm tabs for (int j=0; j < indices.length; j++) { if (alarms[i] == indices[j]) { alarms[i] = -1; changed = true; } } } for (int i=0; i < alarms.length; i++) { //Now decrement those that remain that are affected int alarm = alarms[i]; for (int j=0; j < indices.length; j++) { if (alarm > indices[j]) { alarms[i]--; changed = true; } } } if (changed) { alarmTabs.clear(); boolean addedSome = false; for (int i=0; i < alarms.length; i++) { if (alarms[i] >= 0) { addAlarmTab(alarms[i]); addedSome = true; } } if (!addedSome) { stopAlarmTimer(); } } } else { //Some tab removed after the last blinking tab, don't care } } if( hasHighlightTabs() ) { int[] indices = e.getIndices(); java.util.Arrays.sort(indices); int[] highlights = (int[]) Utilities.toPrimitiveArray((Integer[]) highlightTabs.toArray(new Integer[0])); java.util.Arrays.sort(highlights); if (highlights[highlights.length -1] >= indices[0]) { boolean changed = false; for (int i=0; i < highlights.length; i++) { //First weed out all deleted alarm tabs for (int j=0; j < indices.length; j++) { if (highlights[i] == indices[j]) { highlights[i] = -1; changed = true; } } } for (int i=0; i < highlights.length; i++) { //Now decrement those that remain that are affected int alarm = highlights[i]; for (int j=0; j < indices.length; j++) { if (alarm > indices[j]) { highlights[i]--; changed = true; } } } if (changed) { highlightTabs.clear(); for (int i=0; i < highlights.length; i++) { if (highlights[i] >= 0) { addHighlightTab(highlights[i]); } } } } } repaintAllTabs(); } void indicesChanged (ComplexListDataEvent e) { if (hasAlarmTabs()) { if (e instanceof VeryComplexListDataEvent) { //it always will be VeryComplexListDataEvent ve = (VeryComplexListDataEvent) e; ArrayDiff dif = ((VeryComplexListDataEvent) e).getDiff(); List old = Arrays.asList(dif.getOldData()); List nue = Arrays.asList(dif.getNewData()); int[] alarms = (int[]) Utilities.toPrimitiveArray((Integer[]) alarmTabs.toArray(new Integer[0])); boolean changed = false; for (int i=0; i < alarms.length; i++) { Object o = old.get(alarms[i]); int idx = nue.indexOf(o); changed |= idx != alarms[i]; alarms[i] = nue.indexOf(o); } if (changed) { alarmTabs.clear(); boolean addedSome = false; for (int i=0; i < alarms.length; i++) { if (alarms[i] >= 0) { addAlarmTab(alarms[i]); addedSome = true; } } if (!addedSome) { stopAlarmTimer(); } } } } if( hasHighlightTabs() ) { if (e instanceof VeryComplexListDataEvent) { //it always will be VeryComplexListDataEvent ve = (VeryComplexListDataEvent) e; ArrayDiff dif = ((VeryComplexListDataEvent) e).getDiff(); List old = Arrays.asList(dif.getOldData()); List nue = Arrays.asList(dif.getNewData()); int[] highlights = (int[]) Utilities.toPrimitiveArray((Integer[]) highlightTabs.toArray(new Integer[0])); boolean changed = false; for (int i=0; i < highlights.length; i++) { Object o = old.get(highlights[i]); int idx = nue.indexOf(o); changed |= idx != highlights[i]; highlights[i] = nue.indexOf(o); } if (changed) { highlightTabs.clear(); for (int i=0; i < highlights.length; i++) { if (highlights[i] >= 0) { addHighlightTab(highlights[i]); } } } } } } void contentsChanged(ListDataEvent evt) { if (!hasAlarmTabs()) return; //Do nothing, just means some text or icons changed } //Change types /** Change type indicating no change happened (i.e. calling setSelected() with the same value it was previously * called with). */ public static final int NO_CHANGE = 0; /** Change type indicating a change of state for two tabs */ public static final int CHANGE_TAB_TO_TAB = 1; /** Change type indicating a change happened (such as the mouse leaving a tab) such that now no tab has the * state previously held by the affected tab */ public static final int CHANGE_TAB_TO_NONE = 2; /** Change type indicating that a state was added that no tab previously had */ public static final int CHANGE_NONE_TO_TAB = 3; public static final int CHANGE_TAB_TO_SELF = 4; /** Change type indicating one of the boolean state changes, such as STATE_ACTIVE */ public static final int ALL_TABS = Integer.MAX_VALUE; protected void possibleChange(boolean prevVal, boolean currVal, int type) { if (prevVal == currVal) { lastChangeType = NO_CHANGE; } else { lastChangeType = ALL_TABS; } if (lastChangeType != NO_CHANGE) { lastAffected = ALL_TABS; change(ALL_TABS, ALL_TABS, type, lastChangeType); } } protected void possibleChange(int lastTab, int currTab, int type) { if (lastTab == currTab) { lastChangeType = NO_CHANGE; } else { if (currTab == -1) { lastChangeType = CHANGE_TAB_TO_NONE; } else if (lastTab == -1) { lastChangeType = CHANGE_NONE_TO_TAB; } else { lastChangeType = CHANGE_TAB_TO_TAB; } } if (lastChangeType != NO_CHANGE) { lastAffected = currTab; change(lastTab, currTab, type, lastChangeType); } } public String toString() { StringBuffer sb = new StringBuffer(50); sb.append("TabState [lastTab="); sb.append(tabToString(prev)); sb.append(" currTab="); sb.append(tabToString(curr)); sb.append(" lastAffected="); sb.append(tabToString(lastAffected)); sb.append(" lastChangeType="); sb.append(changeToString(lastChangeType)); sb.append(" lastChange="); sb.append(stateToString(lastChange)); sb.append(" ]"); return sb.toString(); } /** * Called when a setter for a tab index has produced a change in a * state-affecting property, such as which tab contains the mouse. Fetches * the repaint policies, and if the change is one that the policy says * should produce a repaint, calls repaintTab for the appropriate tabs. * * @param lastTab The tab previously holding the state which has changed, * or -1 * @param currTab The tab currently holding the state which has changed, * or -1 * @param type The thing that changed. This will be one of the state * constants. * @param changeType This is one of the defined change types such as * ALL_TABS, TAB_TO_TAB, etc. */ protected void change(int lastTab, int currTab, int type, int changeType) { lastChange = type; // System.err.println("Change-type: " + stateToString(type) + " - " + changeToString (changeType) + " from " + tabToString (lastTab) + " to " + tabToString (currTab)); if (changeType == CHANGE_TAB_TO_TAB) { maybeRepaint(lastTab, type); } else if (changeType == CHANGE_TAB_TO_NONE) { maybeRepaint (lastTab, type); return; } else if (changeType == ALL_TABS && (getRepaintPolicy(currTab) & REPAINT_ALL_ON_MOUSE_ENTER_TABS_AREA) != 0) { repaintAllTabs(); return; } maybeRepaint(currTab, type); } protected void maybeRepaint(int tab, int type) { int rpol = getRepaintPolicy (tab); boolean go = false; switch (type) { case ACTIVE: go = (rpol & REPAINT_SELECTION_ON_ACTIVATION_CHANGE) != 0; if ((rpol & REPAINT_ALL_TABS_ON_ACTIVATION_CHANGE) != 0) { type = ALL_TABS; go = true; } break; case ARMED: go = (rpol & REPAINT_ON_MOUSE_ENTER_TAB) != 0 || tab == closeButtonContainsMouseIndex; closeButtonContainsMouseIndex = -1; break; case CLOSE_BUTTON_ARMED: go = (rpol & REPAINT_ON_MOUSE_ENTER_CLOSE_BUTTON) != 0; break; case MOUSE_IN_TABS_AREA: go = (rpol & REPAINT_ALL_ON_MOUSE_ENTER_TABS_AREA) != 0; break; case MOUSE_PRESSED_IN_CLOSE_BUTTON: go = (rpol & REPAINT_ON_CLOSE_BUTTON_PRESSED) != 0; break; case PRESSED: go = (rpol & REPAINT_ON_MOUSE_PRESSED) != 0; break; case SELECTED: go = (rpol & REPAINT_ON_SELECTION_CHANGE) != 0; if ((rpol & REPAINT_ALL_TABS_ON_SELECTION_CHANGE) != 0) { type = ALL_TABS; go = true; } break; case ATTENTION: case HIGHLIGHT: go = true; } if (go) { if (type == ALL_TABS) { repaintAllTabs(); } else { repaintTab(tab); } } } protected abstract void repaintTab(int tab); protected abstract void repaintAllTabs(); static final String changeToString(int change) { switch (change) { case NO_CHANGE: return "no change"; //NOI18N case CHANGE_TAB_TO_TAB: return "tab to tab"; //NOI18N case CHANGE_TAB_TO_NONE: return "tab to none"; //NOI18N case CHANGE_NONE_TO_TAB: return "none to tab"; //NOI18N case CHANGE_TAB_TO_SELF: return "tab to self"; //NOI18N case ALL_TABS: return "all tabs"; //NOI18N default : return "??? " + change; //NOI18N } } static final String tabToString(int tab) { if (tab == ALL_TABS) { return "all tabs"; //NOI18N } else if (tab == -1) { return "none"; //NOI18N } else { return Integer.toString(tab); } } /** * Static utility method to get a string representation of a state */ static final String stateToString(int st) { String[] states = new String[]{ "clip right", "clip left", "armed", "pressed", "selected", "active", "not onscreen", "leftmost", //NOI18N "rightmost", "in closebutton", "before selected", "after selected", "mouse in tabs area", //NOI18N "mouse pressed in close button" //NOI18N }; //NOI18N int[] vals = new int[]{ CLIP_RIGHT, CLIP_LEFT, ARMED, PRESSED, SELECTED, ACTIVE, NOT_ONSCREEN, LEFTMOST, RIGHTMOST, CLOSE_BUTTON_ARMED, BEFORE_SELECTED, AFTER_SELECTED, MOUSE_IN_TABS_AREA, MOUSE_PRESSED_IN_CLOSE_BUTTON}; StringBuffer sb = new StringBuffer(); for (int i = 0; i < vals.length; i++) { if ((st & vals[i]) != 0) { if (sb.length() > 0) { sb.append(','); } sb.append(states[i]); } } if (sb.length() == 0) { sb.append("no flags set"); //NOI18N } sb.append("="); sb.append(st); return sb.toString(); } static String repaintPolicyToString (int policy) { if (policy == 0) { return "repaint nothing"; } String[] names = new String[] { "REPAINT_ON_MOUSE_ENTER_TAB", "REPAINT_ALL_ON_MOUSE_ENTER_TABS_AREA", "REPAINT_ON_MOUSE_ENTER_CLOSE_BUTTON", "REPAINT_ON_MOUSE_PRESSED", "REPAINT_SELECTION_ON_ACTIVATION_CHANGE", "REPAINT_ALL_TABS_ON_ACTIVATION_CHANGE", "REPAINT_ON_SELECTION_CHANGE", "REPAINT_ALL_TABS_ON_SELECTION_CHANGE", "REPAINT_ON_CLOSE_BUTTON_PRESSED", }; int[] vals = new int[] { REPAINT_ON_MOUSE_ENTER_TAB, REPAINT_ALL_ON_MOUSE_ENTER_TABS_AREA, REPAINT_ON_MOUSE_ENTER_CLOSE_BUTTON, REPAINT_ON_MOUSE_PRESSED, REPAINT_SELECTION_ON_ACTIVATION_CHANGE, REPAINT_ALL_TABS_ON_ACTIVATION_CHANGE, REPAINT_ON_SELECTION_CHANGE, REPAINT_ALL_TABS_ON_SELECTION_CHANGE, REPAINT_ON_CLOSE_BUTTON_PRESSED, }; StringBuffer sb = new StringBuffer(); for (int i=0; i < vals.length; i++) { if ((policy & vals[i]) != 0) { sb.append (names[i]); if (i != vals.length-1) { sb.append ('+'); } } } return sb.toString(); } /** * Get the repaint policy that will be used to determine what tabs to repaint, based on state changes. * The default implementation in BasicTabDisplayerUI simply ignores the tab argument and returns a * single policy for all tabs created in BasicTabDisplayerUI.createRepaintPolicy() * * @param tab Index of tab in question * @return Type of repaint policy for given tab */ public abstract int getRepaintPolicy(int tab); }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy