source: josm/trunk/src/org/openstreetmap/josm/gui/widgets/QuadStateCheckBox.java@ 10763

Last change on this file since 10763 was 10763, checked in by Don-vip, 8 years ago

sonar - squid:S2156 - "final" classes should not have "protected" members

  • Property svn:eol-style set to native
File size: 10.0 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.widgets;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.event.ActionEvent;
7import java.awt.event.ActionListener;
8import java.awt.event.ItemListener;
9import java.awt.event.MouseAdapter;
10import java.awt.event.MouseEvent;
11import java.awt.event.MouseListener;
12
13import javax.swing.AbstractAction;
14import javax.swing.ActionMap;
15import javax.swing.ButtonGroup;
16import javax.swing.ButtonModel;
17import javax.swing.Icon;
18import javax.swing.JCheckBox;
19import javax.swing.SwingUtilities;
20import javax.swing.event.ChangeListener;
21import javax.swing.plaf.ActionMapUIResource;
22
23import org.openstreetmap.josm.tools.Utils;
24
25/**
26 * A four-state checkbox. The states are enumerated in {@link State}.
27 * @since 591
28 */
29public class QuadStateCheckBox extends JCheckBox {
30
31 /**
32 * The 4 possible states of this checkbox.
33 */
34 public enum State {
35 /** Not selected: the property is explicitly switched off */
36 NOT_SELECTED,
37 /** Selected: the property is explicitly switched on */
38 SELECTED,
39 /** Unset: do not set this property on the selected objects */
40 UNSET,
41 /** Partial: different selected objects have different values, do not change */
42 PARTIAL
43 }
44
45 private final transient QuadStateDecorator model;
46 private State[] allowed;
47
48 /**
49 * Constructs a new {@code QuadStateCheckBox}.
50 * @param text the text of the check box
51 * @param icon the Icon image to display
52 * @param initial The initial state
53 * @param allowed The allowed states
54 */
55 public QuadStateCheckBox(String text, Icon icon, State initial, State ... allowed) {
56 super(text, icon);
57 this.allowed = Utils.copyArray(allowed);
58 // Add a listener for when the mouse is pressed
59 super.addMouseListener(new MouseAdapter() {
60 @Override public void mousePressed(MouseEvent e) {
61 grabFocus();
62 model.nextState();
63 }
64 });
65 // Reset the keyboard action map
66 ActionMap map = new ActionMapUIResource();
67 map.put("pressed", new AbstractAction() {
68 @Override
69 public void actionPerformed(ActionEvent e) {
70 grabFocus();
71 model.nextState();
72 }
73 });
74 map.put("released", null);
75 SwingUtilities.replaceUIActionMap(this, map);
76 // set the model to the adapted model
77 model = new QuadStateDecorator(getModel());
78 setModel(model);
79 setState(initial);
80 }
81
82 /**
83 * Constructs a new {@code QuadStateCheckBox}.
84 * @param text the text of the check box
85 * @param initial The initial state
86 * @param allowed The allowed states
87 */
88 public QuadStateCheckBox(String text, State initial, State ... allowed) {
89 this(text, null, initial, allowed);
90 }
91
92 /** Do not let anyone add mouse listeners */
93 @Override
94 public synchronized void addMouseListener(MouseListener l) {
95 // Do nothing
96 }
97
98 /**
99 * Sets a text describing this property in the tooltip text
100 * @param propertyText a description for the modelled property
101 */
102 public final void setPropertyText(final String propertyText) {
103 model.setPropertyText(propertyText);
104 }
105
106 /**
107 * Set the new state.
108 * @param state The new state
109 */
110 public final void setState(State state) {
111 model.setState(state);
112 }
113
114 /**
115 * Return the current state, which is determined by the selection status of the model.
116 * @return The current state
117 */
118 public State getState() {
119 return model.getState();
120 }
121
122 @Override
123 public void setSelected(boolean b) {
124 if (b) {
125 setState(State.SELECTED);
126 } else {
127 setState(State.NOT_SELECTED);
128 }
129 }
130
131 private final class QuadStateDecorator implements ButtonModel {
132 private final ButtonModel other;
133 private String propertyText;
134
135 private QuadStateDecorator(ButtonModel other) {
136 this.other = other;
137 }
138
139 private void setState(State state) {
140 if (state == State.NOT_SELECTED) {
141 other.setArmed(false);
142 other.setPressed(false);
143 other.setSelected(false);
144 setToolTipText(propertyText == null
145 ? tr("false: the property is explicitly switched off")
146 : tr("false: the property ''{0}'' is explicitly switched off", propertyText));
147 } else if (state == State.SELECTED) {
148 other.setArmed(false);
149 other.setPressed(false);
150 other.setSelected(true);
151 setToolTipText(propertyText == null
152 ? tr("true: the property is explicitly switched on")
153 : tr("true: the property ''{0}'' is explicitly switched on", propertyText));
154 } else if (state == State.PARTIAL) {
155 other.setArmed(true);
156 other.setPressed(true);
157 other.setSelected(true);
158 setToolTipText(propertyText == null
159 ? tr("partial: different selected objects have different values, do not change")
160 : tr("partial: different selected objects have different values for ''{0}'', do not change", propertyText));
161 } else {
162 other.setArmed(true);
163 other.setPressed(true);
164 other.setSelected(false);
165 setToolTipText(propertyText == null
166 ? tr("unset: do not set this property on the selected objects")
167 : tr("unset: do not set the property ''{0}'' on the selected objects", propertyText));
168 }
169 }
170
171 private void setPropertyText(String propertyText) {
172 this.propertyText = propertyText;
173 }
174
175 /**
176 * The current state is embedded in the selection / armed
177 * state of the model.
178 *
179 * We return the SELECTED state when the checkbox is selected
180 * but not armed, PARTIAL state when the checkbox is
181 * selected and armed (grey) and NOT_SELECTED when the
182 * checkbox is deselected.
183 * @return current state
184 */
185 private State getState() {
186 if (isSelected() && !isArmed()) {
187 // normal black tick
188 return State.SELECTED;
189 } else if (isSelected() && isArmed()) {
190 // don't care grey tick
191 return State.PARTIAL;
192 } else if (!isSelected() && !isArmed()) {
193 return State.NOT_SELECTED;
194 } else {
195 return State.UNSET;
196 }
197 }
198
199 /** Rotate to the next allowed state.*/
200 private void nextState() {
201 State current = getState();
202 for (int i = 0; i < allowed.length; i++) {
203 if (allowed[i] == current) {
204 setState((i == allowed.length-1) ? allowed[0] : allowed[i+1]);
205 break;
206 }
207 }
208 }
209
210 // ----------------------------------------------------------------------
211 // Filter: No one may change the armed/selected/pressed status except us.
212 // ----------------------------------------------------------------------
213
214 @Override
215 public void setArmed(boolean b) {
216 // Do nothing
217 }
218
219 @Override
220 public void setSelected(boolean b) {
221 // Do nothing
222 }
223
224 @Override
225 public void setPressed(boolean b) {
226 // Do nothing
227 }
228
229 /** We disable focusing on the component when it is not enabled. */
230 @Override
231 public void setEnabled(boolean b) {
232 setFocusable(b);
233 other.setEnabled(b);
234 }
235
236 // -------------------------------------------------------------------------------
237 // All these methods simply delegate to the "other" model that is being decorated.
238 // -------------------------------------------------------------------------------
239
240 @Override
241 public boolean isArmed() {
242 return other.isArmed();
243 }
244
245 @Override
246 public boolean isSelected() {
247 return other.isSelected();
248 }
249
250 @Override
251 public boolean isEnabled() {
252 return other.isEnabled();
253 }
254
255 @Override
256 public boolean isPressed() {
257 return other.isPressed();
258 }
259
260 @Override
261 public boolean isRollover() {
262 return other.isRollover();
263 }
264
265 @Override
266 public void setRollover(boolean b) {
267 other.setRollover(b);
268 }
269
270 @Override
271 public void setMnemonic(int key) {
272 other.setMnemonic(key);
273 }
274
275 @Override
276 public int getMnemonic() {
277 return other.getMnemonic();
278 }
279
280 @Override
281 public void setActionCommand(String s) {
282 other.setActionCommand(s);
283 }
284
285 @Override public String getActionCommand() {
286 return other.getActionCommand();
287 }
288
289 @Override public void setGroup(ButtonGroup group) {
290 other.setGroup(group);
291 }
292
293 @Override public void addActionListener(ActionListener l) {
294 other.addActionListener(l);
295 }
296
297 @Override public void removeActionListener(ActionListener l) {
298 other.removeActionListener(l);
299 }
300
301 @Override public void addItemListener(ItemListener l) {
302 other.addItemListener(l);
303 }
304
305 @Override public void removeItemListener(ItemListener l) {
306 other.removeItemListener(l);
307 }
308
309 @Override public void addChangeListener(ChangeListener l) {
310 other.addChangeListener(l);
311 }
312
313 @Override public void removeChangeListener(ChangeListener l) {
314 other.removeChangeListener(l);
315 }
316
317 @Override public Object[] getSelectedObjects() {
318 return other.getSelectedObjects();
319 }
320 }
321}
Note: See TracBrowser for help on using the repository browser.