1 /* 2 * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package javax.swing.plaf.nimbus; 26 27 import java.util.HashMap; 28 import java.util.Map; 29 import javax.swing.JComponent; 30 import javax.swing.plaf.synth.SynthConstants; 31 32 /** 33 * <p>Represents a built in, or custom, state in Nimbus.</p> 34 * 35 * <p>Synth provides several built in states, which are: 36 * <ul> 37 * <li>Enabled</li> 38 * <li>Mouse Over</li> 39 * <li>Pressed</li> 40 * <li>Disabled</li> 41 * <li>Focused</li> 42 * <li>Selected</li> 43 * <li>Default</li> 44 * </ul> 45 * 46 * <p>However, there are many more states that could be described in a LookAndFeel, and it 47 * would be nice to style components differently based on these different states. 48 * For example, a progress bar could be "indeterminate". It would be very convenient 49 * to allow this to be defined as a "state".</p> 50 * 51 * <p>This class, State, is intended to be used for such situations. 52 * Simply implement the abstract #isInState method. It returns true if the given 53 * JComponent is "in this state", false otherwise. This method will be called 54 * <em>many</em> times in <em>performance sensitive loops</em>. It must execute 55 * very quickly.</p> 56 * 57 * <p>For example, the following might be an implementation of a custom 58 * "Indeterminate" state for JProgressBars:</p> 59 * 60 * <pre><code> 61 * public final class IndeterminateState extends State<JProgressBar> { 62 * public IndeterminateState() { 63 * super("Indeterminate"); 64 * } 65 * 66 * @Override 67 * protected boolean isInState(JProgressBar c) { 68 * return c.isIndeterminate(); 69 * } 70 * } 71 * </code></pre> 72 */ 73 public abstract class State<T extends JComponent>{ 74 static final Map<String, StandardState> standardStates = new HashMap<String, StandardState>(7); 75 static final State Enabled = new StandardState(SynthConstants.ENABLED); 76 static final State MouseOver = new StandardState(SynthConstants.MOUSE_OVER); 77 static final State Pressed = new StandardState(SynthConstants.PRESSED); 78 static final State Disabled = new StandardState(SynthConstants.DISABLED); 79 static final State Focused = new StandardState(SynthConstants.FOCUSED); 80 static final State Selected = new StandardState(SynthConstants.SELECTED); 81 static final State Default = new StandardState(SynthConstants.DEFAULT); 82 83 private String name; 84 85 /** 86 * <p>Create a new custom State. Specify the name for the state. The name should 87 * be unique within the states set for any one particular component. 88 * The name of the state should coincide with the name used in UIDefaults.</p> 89 * 90 * <p>For example, the following would be correct:</p> 91 * <pre><code> 92 * defaults.put("Button.States", "Enabled, Foo, Disabled"); 93 * defaults.put("Button.Foo", new FooState("Foo")); 94 * </code></pre> 95 * 96 * @param name a simple user friendly name for the state, such as "Indeterminate" 97 * or "EmbeddedPanel" or "Blurred". It is customary to use camel case, 98 * with the first letter capitalized. 99 */ 100 protected State(String name) { 101 this.name = name; 102 } 103 104 @Override public String toString() { return name; } 105 106 /** 107 * <p>This is the main entry point, called by NimbusStyle.</p> 108 * 109 * <p>There are both custom states and standard states. Standard states 110 * correlate to the states defined in SynthConstants. When a UI delegate 111 * constructs a SynthContext, it specifies the state that the component is 112 * in according to the states defined in SynthConstants. Our NimbusStyle 113 * will then take this state, and query each State instance in the style 114 * asking whether isInState(c, s).</p> 115 * 116 * <p>Now, only the standard states care about the "s" param. So we have 117 * this odd arrangement:</p> 118 * <ul> 119 * <li>NimbusStyle calls State.isInState(c, s)</li> 120 * <li>State.isInState(c, s) simply delegates to State.isInState(c)</li> 121 * <li><em>EXCEPT</em>, StandardState overrides State.isInState(c, s) and 122 * returns directly from that method after checking its state, and 123 * does not call isInState(c) (since it is not needed for standard states).</li> 124 * </ul> 125 */ 126 boolean isInState(T c, int s) { 127 return isInState(c); 128 } 129 130 /** 131 * <p>Gets whether the specified JComponent is in the custom state represented 132 * by this class. <em>This is an extremely performance sensitive loop.</em> 133 * Please take proper precautions to ensure that it executes quickly.</p> 134 * 135 * <p>Nimbus uses this method to help determine what state a JComponent is 136 * in. For example, a custom State could exist for JProgressBar such that 137 * it would return <code>true</code> when the progress bar is indeterminate. 138 * Such an implementation of this method would simply be:</p> 139 * 140 * <pre><code> return c.isIndeterminate();</code></pre> 141 * 142 * @param c the JComponent to test. This will never be null. 143 * @return true if <code>c</code> is in the custom state represented by 144 * this <code>State</code> instance 145 */ 146 protected abstract boolean isInState(T c); 147 148 String getName() { return name; } 149 150 static boolean isStandardStateName(String name) { 151 return standardStates.containsKey(name); 152 } 153 154 static StandardState getStandardState(String name) { 155 return standardStates.get(name); 156 } 157 158 static final class StandardState extends State<JComponent> { 159 private int state; 160 161 private StandardState(int state) { 162 super(toString(state)); 163 this.state = state; 164 standardStates.put(getName(), this); 165 } 166 167 public int getState() { 168 return state; 169 } 170 171 @Override 172 boolean isInState(JComponent c, int s) { 173 return (s & state) == state; 174 } 175 176 @Override 177 protected boolean isInState(JComponent c) { 178 throw new AssertionError("This method should never be called"); 179 } 180 181 private static String toString(int state) { 182 StringBuffer buffer = new StringBuffer(); 183 if ((state & SynthConstants.DEFAULT) == SynthConstants.DEFAULT) { 184 buffer.append("Default"); 185 } 186 if ((state & SynthConstants.DISABLED) == SynthConstants.DISABLED) { 187 if (buffer.length() > 0) buffer.append("+"); 188 buffer.append("Disabled"); 189 } 190 if ((state & SynthConstants.ENABLED) == SynthConstants.ENABLED) { 191 if (buffer.length() > 0) buffer.append("+"); 192 buffer.append("Enabled"); 193 } 194 if ((state & SynthConstants.FOCUSED) == SynthConstants.FOCUSED) { 195 if (buffer.length() > 0) buffer.append("+"); 196 buffer.append("Focused"); 197 } 198 if ((state & SynthConstants.MOUSE_OVER) == SynthConstants.MOUSE_OVER) { 199 if (buffer.length() > 0) buffer.append("+"); 200 buffer.append("MouseOver"); 201 } 202 if ((state & SynthConstants.PRESSED) == SynthConstants.PRESSED) { 203 if (buffer.length() > 0) buffer.append("+"); 204 buffer.append("Pressed"); 205 } 206 if ((state & SynthConstants.SELECTED) == SynthConstants.SELECTED) { 207 if (buffer.length() > 0) buffer.append("+"); 208 buffer.append("Selected"); 209 } 210 return buffer.toString(); 211 } 212 } 213 }