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

Last change on this file since 9360 was 9360, checked in by simon04, 8 years ago

fix #12286 - Preset dialog: display the related key in a tooltip for every component

  • Property svn:eol-style set to native
File size: 9.9 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 void addMouseListener(MouseListener l) { }
95
96 /**
97 * Sets a text describing this property in the tooltip text
98 * @param propertyText a description for the modelled property
99 */
100 public final void setPropertyText(final String propertyText) {
101 model.setPropertyText(propertyText);
102 }
103
104 /**
105 * Set the new state.
106 * @param state The new state
107 */
108 public final void setState(State state) {
109 model.setState(state);
110 }
111
112 /**
113 * Return the current state, which is determined by the selection status of the model.
114 * @return The current state
115 */
116 public State getState() {
117 return model.getState();
118 }
119
120 @Override
121 public void setSelected(boolean b) {
122 if (b) {
123 setState(State.SELECTED);
124 } else {
125 setState(State.NOT_SELECTED);
126 }
127 }
128
129 private final class QuadStateDecorator implements ButtonModel {
130 private final ButtonModel other;
131 private String propertyText = null;
132
133 private QuadStateDecorator(ButtonModel other) {
134 this.other = other;
135 }
136
137 private void setState(State state) {
138 if (state == State.NOT_SELECTED) {
139 other.setArmed(false);
140 other.setPressed(false);
141 other.setSelected(false);
142 setToolTipText(propertyText == null
143 ? tr("false: the property is explicitly switched off")
144 : tr("false: the property ''{0}'' is explicitly switched off", propertyText));
145 } else if (state == State.SELECTED) {
146 other.setArmed(false);
147 other.setPressed(false);
148 other.setSelected(true);
149 setToolTipText(propertyText == null
150 ? tr("true: the property is explicitly switched on")
151 : tr("true: the property ''{0}'' is explicitly switched on", propertyText));
152 } else if (state == State.PARTIAL) {
153 other.setArmed(true);
154 other.setPressed(true);
155 other.setSelected(true);
156 setToolTipText(propertyText == null
157 ? tr("partial: different selected objects have different values, do not change")
158 : tr("partial: different selected objects have different values for ''{0}'', do not change", propertyText));
159 } else {
160 other.setArmed(true);
161 other.setPressed(true);
162 other.setSelected(false);
163 setToolTipText(propertyText == null
164 ? tr("unset: do not set this property on the selected objects")
165 : tr("unset: do not set the property ''{0}'' on the selected objects", propertyText));
166 }
167 }
168
169 protected void setPropertyText(String propertyText) {
170 this.propertyText = propertyText;
171 }
172
173 /**
174 * The current state is embedded in the selection / armed
175 * state of the model.
176 *
177 * We return the SELECTED state when the checkbox is selected
178 * but not armed, PARTIAL state when the checkbox is
179 * selected and armed (grey) and NOT_SELECTED when the
180 * checkbox is deselected.
181 * @return current state
182 */
183 private State getState() {
184 if (isSelected() && !isArmed()) {
185 // normal black tick
186 return State.SELECTED;
187 } else if (isSelected() && isArmed()) {
188 // don't care grey tick
189 return State.PARTIAL;
190 } else if (!isSelected() && !isArmed()) {
191 return State.NOT_SELECTED;
192 } else {
193 return State.UNSET;
194 }
195 }
196
197 /** Rotate to the next allowed state.*/
198 private void nextState() {
199 State current = getState();
200 for (int i = 0; i < allowed.length; i++) {
201 if (allowed[i] == current) {
202 setState((i == allowed.length-1) ? allowed[0] : allowed[i+1]);
203 break;
204 }
205 }
206 }
207
208 // ----------------------------------------------------------------------
209 // Filter: No one may change the armed/selected/pressed status except us.
210 // ----------------------------------------------------------------------
211
212 @Override
213 public void setArmed(boolean b) { }
214
215 @Override
216 public void setSelected(boolean b) { }
217
218 @Override
219 public void setPressed(boolean b) { }
220
221 /** We disable focusing on the component when it is not enabled. */
222 @Override
223 public void setEnabled(boolean b) {
224 setFocusable(b);
225 other.setEnabled(b);
226 }
227
228 // -------------------------------------------------------------------------------
229 // All these methods simply delegate to the "other" model that is being decorated.
230 // -------------------------------------------------------------------------------
231
232 @Override
233 public boolean isArmed() {
234 return other.isArmed();
235 }
236
237 @Override
238 public boolean isSelected() {
239 return other.isSelected();
240 }
241
242 @Override
243 public boolean isEnabled() {
244 return other.isEnabled();
245 }
246
247 @Override
248 public boolean isPressed() {
249 return other.isPressed();
250 }
251
252 @Override
253 public boolean isRollover() {
254 return other.isRollover();
255 }
256
257 @Override
258 public void setRollover(boolean b) {
259 other.setRollover(b);
260 }
261
262 @Override
263 public void setMnemonic(int key) {
264 other.setMnemonic(key);
265 }
266
267 @Override
268 public int getMnemonic() {
269 return other.getMnemonic();
270 }
271
272 @Override
273 public void setActionCommand(String s) {
274 other.setActionCommand(s);
275 }
276
277 @Override public String getActionCommand() {
278 return other.getActionCommand();
279 }
280
281 @Override public void setGroup(ButtonGroup group) {
282 other.setGroup(group);
283 }
284
285 @Override public void addActionListener(ActionListener l) {
286 other.addActionListener(l);
287 }
288
289 @Override public void removeActionListener(ActionListener l) {
290 other.removeActionListener(l);
291 }
292
293 @Override public void addItemListener(ItemListener l) {
294 other.addItemListener(l);
295 }
296
297 @Override public void removeItemListener(ItemListener l) {
298 other.removeItemListener(l);
299 }
300
301 @Override public void addChangeListener(ChangeListener l) {
302 other.addChangeListener(l);
303 }
304
305 @Override public void removeChangeListener(ChangeListener l) {
306 other.removeChangeListener(l);
307 }
308
309 @Override public Object[] getSelectedObjects() {
310 return other.getSelectedObjects();
311 }
312 }
313}
Note: See TracBrowser for help on using the repository browser.