1 package hep.wired.util;
2
3 import java.awt.*;
4 import java.awt.event.*;
5 import javax.swing.*;
6 import javax.swing.event.*;
7 import javax.swing.plaf.*;
8
9 /***
10 * Maintenance tip - There were some tricks to getting this code
11 * working:
12 *
13 * 1. You have to overwite addMouseListener() to do nothing
14 * 2. You have to add a mouse event on mousePressed by calling
15 * super.addMouseListener()
16 * 3. You have to replace the UIActionMap for the keyboard event
17 * "pressed" with your own one.
18 * 4. You have to remove the UIActionMap for the keyboard event
19 * "released".
20 * 5. You have to grab focus when the next state is entered,
21 * otherwise clicking on the component won't get the focus.
22 * 6. You have to make a TristateDecorator as a button model that
23 * wraps the original button model and does state management.
24 *
25 * Taken from 2003-12-02 The Java Specialists' Newsletter [Issue 082] -
26 * TristateCheckBox based on the Swing JCheckBox
27 * http://www.javaspecialists.co.za/archive/Issue082.html
28 *
29 * @author Mark Donszelmann
30 * @version $Id: TristateCheckBox.java 385 2004-08-04 00:27:33Z duns $
31 */
32 public class TristateCheckBox extends JCheckBox {
33 /*** This is a type-safe enumerated type */
34 public static class State { private State() { } }
35 public static final State NOT_SELECTED = new State();
36 public static final State SELECTED = new State();
37 public static final State DONT_CARE = new State();
38
39 private final TristateDecorator model;
40
41 public TristateCheckBox(String text, Icon icon, State initial) {
42 super(text, icon);
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65 model = new TristateDecorator(getModel());
66 setModel(model);
67 setState(initial);
68 }
69
70 public TristateCheckBox(String text, State initial) {
71 this(text, null, initial);
72 }
73
74 public TristateCheckBox(String text) {
75 this(text, DONT_CARE);
76 }
77
78 public TristateCheckBox() {
79 this(null);
80 }
81
82 /***
83 * No one may add mouse listeners, not even Swing!
84 */
85
86
87
88
89 /***
90 * Set the new state to either SELECTED, NOT_SELECTED or
91 * DONT_CARE. If state == null, it is treated as DONT_CARE.
92 */
93 public void setState(State state) {
94 model.setState(state);
95 }
96
97 /***
98 * Return the current state, which is determined by the
99 * selection status of the model.
100 */
101 public State getState() {
102 return model.getState();
103 }
104
105 /***
106 * Exactly which Design Pattern is this? Is it an Adapter,
107 * a Proxy or a Decorator? In this case, my vote lies with the
108 * Decorator, because we are extending functionality and
109 * "decorating" the original model with a more powerful model.
110 */
111 private class TristateDecorator implements ButtonModel {
112
113 private final ButtonModel other;
114
115 private TristateDecorator(ButtonModel other) {
116 this.other = other;
117 }
118
119 private void setState(State state) {
120 if (state == NOT_SELECTED) {
121 other.setArmed(false);
122 setPressed(false);
123 setSelected(false);
124 } else if (state == SELECTED) {
125 other.setArmed(false);
126 setPressed(false);
127 setSelected(true);
128 } else {
129 other.setArmed(true);
130 setPressed(true);
131 setSelected(true);
132 }
133 }
134
135 /***
136 * The current state is embedded in the selection / armed
137 * state of the model.
138 *
139 * We return the SELECTED state when the checkbox is selected
140 * but not armed, DONT_CARE state when the checkbox is
141 * selected and armed (grey) and NOT_SELECTED when the
142 * checkbox is deselected.
143 */
144 private State getState() {
145 if (isSelected() && !isArmed()) {
146
147 return SELECTED;
148 } else if (isSelected() && isArmed()) {
149
150 return DONT_CARE;
151 } else {
152
153 return NOT_SELECTED;
154 }
155 }
156
157 /***
158 * We rotate between NOT_SELECTED, SELECTED and DONT_CARE.
159 */
160 private void nextState() {
161 State current = getState();
162 if (current == NOT_SELECTED) {
163 setState(SELECTED);
164 } else if (current == SELECTED) {
165 setState(DONT_CARE);
166 } else if (current == DONT_CARE) {
167 setState(NOT_SELECTED);
168 }
169 }
170
171 /***
172 * Filter: No one may change the armed status except us.
173 */
174 public void setArmed(boolean b) {
175
176 other.setArmed(b);
177 }
178
179 /***
180 * We disable focusing on the component when it is not
181 * enabled.
182 */
183 public void setEnabled(boolean b) {
184 setFocusable(b);
185 other.setEnabled(b);
186 }
187
188
189
190
191
192 public boolean isArmed() {
193 return other.isArmed();
194 }
195
196 public boolean isSelected() {
197 return other.isSelected();
198 }
199
200 public boolean isEnabled() {
201 return other.isEnabled();
202 }
203
204 public boolean isPressed() {
205 return other.isPressed();
206 }
207
208 public boolean isRollover() {
209 return other.isRollover();
210 }
211
212 public void setSelected(boolean b) {
213 other.setSelected(b);
214 }
215
216 public void setPressed(boolean b) {
217 other.setPressed(b);
218 }
219
220 public void setRollover(boolean b) {
221 other.setRollover(b);
222 }
223
224 public void setMnemonic(int key) {
225 other.setMnemonic(key);
226 }
227
228 public int getMnemonic() {
229 return other.getMnemonic();
230 }
231
232 public void setActionCommand(String s) {
233 other.setActionCommand(s);
234 }
235
236 public String getActionCommand() {
237 return other.getActionCommand();
238 }
239
240 public void setGroup(ButtonGroup group) {
241 other.setGroup(group);
242 }
243
244 public void addActionListener(ActionListener l) {
245 other.addActionListener(l);
246 }
247
248 public void removeActionListener(ActionListener l) {
249 other.removeActionListener(l);
250 }
251
252 public void addItemListener(ItemListener l) {
253 other.addItemListener(l);
254 }
255
256 public void removeItemListener(ItemListener l) {
257 other.removeItemListener(l);
258 }
259
260 public void addChangeListener(ChangeListener l) {
261 other.addChangeListener(l);
262 }
263
264 public void removeChangeListener(ChangeListener l) {
265 other.removeChangeListener(l);
266 }
267
268 public Object[] getSelectedObjects() {
269 return other.getSelectedObjects();
270 }
271 }
272 }
273