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

Last change on this file since 16276 was 16276, checked in by simon04, 4 years ago

fix #19075 - Presets: add popup menu to keys

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