alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 *
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 *
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2004 Sun
 * Microsystems, Inc. All Rights Reserved.
 */
package org.netbeans.swing.tabcontrol.plaf;

/**
 * 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; public static final int MOUSE_PRESSED_IN_CLOSE_BUTTON = 8192; /** * Indicates the last constant defined - renderers that wish to add their * own bitmasks should use multiples of this number */ public static int STATE_LAST = MOUSE_PRESSED_IN_CLOSE_BUTTON; 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; } 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; 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); return prev; } //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; // log("Change-type: " + stateToString(type) + " - " + changeToString (changeType) + " from " + tabToString (lastTab) + " to " + tabToString (currTab)); if (changeType == CHANGE_TAB_TO_NONE && currTab != -1) { Thread.dumpStack(); } 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) { 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; 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; // log("Case is selected change, go for " + tab + " is " + go); if ((rpol & REPAINT_ALL_TABS_ON_SELECTION_CHANGE) != 0) { // log("now using all tabs - policy is " + repaintPolicyToString(rpol)); type = ALL_TABS; go = true; } } if (go) { if (type == ALL_TABS) { repaintAllTabs(); } else { // log("Really repainting tab " + tab); //repaintTab(tab); repaintAllTabs(); //XXX } } } 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 * @return */ public abstract int getRepaintPolicy(int tab); }

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.