| 1 | // License: GPL. For details, see LICENSE file.
|
|---|
| 2 | package org.openstreetmap.josm.actions;
|
|---|
| 3 |
|
|---|
| 4 | import java.awt.event.ActionEvent;
|
|---|
| 5 | import java.util.HashSet;
|
|---|
| 6 | import java.util.Set;
|
|---|
| 7 |
|
|---|
| 8 | import javax.swing.ButtonModel;
|
|---|
| 9 | import javax.swing.JCheckBox;
|
|---|
| 10 | import javax.swing.JCheckBoxMenuItem;
|
|---|
| 11 | import javax.swing.JRadioButton;
|
|---|
| 12 | import javax.swing.JRadioButtonMenuItem;
|
|---|
| 13 | import javax.swing.JToggleButton;
|
|---|
| 14 |
|
|---|
| 15 | import org.openstreetmap.josm.tools.ImageProvider;
|
|---|
| 16 | import org.openstreetmap.josm.tools.Logging;
|
|---|
| 17 | import org.openstreetmap.josm.tools.Shortcut;
|
|---|
| 18 |
|
|---|
| 19 | /**
|
|---|
| 20 | * Abstract class for Toggle Actions.
|
|---|
| 21 | * @since 6220
|
|---|
| 22 | */
|
|---|
| 23 | public abstract class ToggleAction extends JosmAction {
|
|---|
| 24 |
|
|---|
| 25 | private final transient Set<ButtonModel> buttonModels = new HashSet<>();
|
|---|
| 26 |
|
|---|
| 27 | /**
|
|---|
| 28 | * Constructs a {@code ToggleAction}.
|
|---|
| 29 | *
|
|---|
| 30 | * @param name the action's text as displayed on the menu (if it is added to a menu)
|
|---|
| 31 | * @param icon the icon to use
|
|---|
| 32 | * @param tooltip a longer description of the action that will be displayed in the tooltip. Please note
|
|---|
| 33 | * that html is not supported for menu actions on some platforms.
|
|---|
| 34 | * @param shortcut a ready-created shortcut object or null if you don't want a shortcut. But you always
|
|---|
| 35 | * do want a shortcut, remember you can always register it with group=none, so you
|
|---|
| 36 | * won't be assigned a shortcut unless the user configures one. If you pass null here,
|
|---|
| 37 | * the user CANNOT configure a shortcut for your action.
|
|---|
| 38 | * @param registerInToolbar register this action for the toolbar preferences?
|
|---|
| 39 | * @param toolbarId identifier for the toolbar preferences. The iconName is used, if this parameter is null
|
|---|
| 40 | * @param installAdapters false, if you don't want to install layer changed and selection changed adapters
|
|---|
| 41 | */
|
|---|
| 42 | protected ToggleAction(String name, ImageProvider icon, String tooltip, Shortcut shortcut, boolean registerInToolbar,
|
|---|
| 43 | String toolbarId, boolean installAdapters) {
|
|---|
| 44 | super(name, icon, tooltip, shortcut, registerInToolbar, toolbarId, installAdapters);
|
|---|
| 45 | // It is required to set the SELECTED_KEY to a non-null value in order to let Swing components update it
|
|---|
| 46 | setSelected(false);
|
|---|
| 47 | }
|
|---|
| 48 |
|
|---|
| 49 | /**
|
|---|
| 50 | * Constructs a {@code ToggleAction}.
|
|---|
| 51 | *
|
|---|
| 52 | * @param name the action's text as displayed on the menu (if it is added to a menu)
|
|---|
| 53 | * @param iconName the name of icon to use
|
|---|
| 54 | * @param tooltip a longer description of the action that will be displayed in the tooltip. Please note
|
|---|
| 55 | * that html is not supported for menu actions on some platforms.
|
|---|
| 56 | * @param shortcut a ready-created shortcut object or null if you don't want a shortcut. But you always
|
|---|
| 57 | * do want a shortcut, remember you can always register it with group=none, so you
|
|---|
| 58 | * won't be assigned a shortcut unless the user configures one. If you pass null here,
|
|---|
| 59 | * the user CANNOT configure a shortcut for your action.
|
|---|
| 60 | * @param registerInToolbar register this action for the toolbar preferences?
|
|---|
| 61 | */
|
|---|
| 62 | protected ToggleAction(String name, String iconName, String tooltip, Shortcut shortcut, boolean registerInToolbar) {
|
|---|
| 63 | super(name, iconName, tooltip, shortcut, registerInToolbar);
|
|---|
| 64 | // It is required to set the SELECTED_KEY to a non-null value in order to let Swing components update it
|
|---|
| 65 | setSelected(false);
|
|---|
| 66 | }
|
|---|
| 67 |
|
|---|
| 68 | protected final void setSelected(boolean selected) {
|
|---|
| 69 | putValue(SELECTED_KEY, selected);
|
|---|
| 70 | }
|
|---|
| 71 |
|
|---|
| 72 | /**
|
|---|
| 73 | * Determines if this action is currently being selected.
|
|---|
| 74 | * @return {@code true} if this action is currently being selected, {@code false} otherwise
|
|---|
| 75 | */
|
|---|
| 76 | public final boolean isSelected() {
|
|---|
| 77 | Object selected = getValue(SELECTED_KEY);
|
|---|
| 78 | if (selected instanceof Boolean) {
|
|---|
| 79 | return (Boolean) selected;
|
|---|
| 80 | } else {
|
|---|
| 81 | Logging.warn(getClass().getName() + " does not define a boolean for SELECTED_KEY but " + selected +
|
|---|
| 82 | ". You should report it to JOSM developers.");
|
|---|
| 83 | return false;
|
|---|
| 84 | }
|
|---|
| 85 | }
|
|---|
| 86 |
|
|---|
| 87 | /**
|
|---|
| 88 | * Adds a button model
|
|---|
| 89 | * @param model The button model to add
|
|---|
| 90 | */
|
|---|
| 91 | public final void addButtonModel(ButtonModel model) {
|
|---|
| 92 | if (model != null && !buttonModels.contains(model)) {
|
|---|
| 93 | buttonModels.add(model);
|
|---|
| 94 | model.setSelected(isSelected());
|
|---|
| 95 | }
|
|---|
| 96 | }
|
|---|
| 97 |
|
|---|
| 98 | /**
|
|---|
| 99 | * Removes a button model
|
|---|
| 100 | * @param model The button model to remove
|
|---|
| 101 | */
|
|---|
| 102 | public final void removeButtonModel(ButtonModel model) {
|
|---|
| 103 | if (model != null) {
|
|---|
| 104 | buttonModels.remove(model);
|
|---|
| 105 | }
|
|---|
| 106 | }
|
|---|
| 107 |
|
|---|
| 108 | protected void notifySelectedState() {
|
|---|
| 109 | boolean selected = isSelected();
|
|---|
| 110 | for (ButtonModel model: buttonModels) {
|
|---|
| 111 | if (model.isSelected() != selected) {
|
|---|
| 112 | model.setSelected(selected);
|
|---|
| 113 | }
|
|---|
| 114 | }
|
|---|
| 115 | }
|
|---|
| 116 |
|
|---|
| 117 | /**
|
|---|
| 118 | * Toggles the selected action state, if needed according to the ActionEvent that triggered the action.
|
|---|
| 119 | * This method will do nothing if the action event comes from a Swing component
|
|---|
| 120 | * supporting the SELECTED_KEY property because the component already set the selected state.
|
|---|
| 121 | * This method needs to be called especially if the action is associated with a keyboard
|
|---|
| 122 | * shortcut to ensure correct selected state.
|
|---|
| 123 | * @param e ActionEvent that triggered the action
|
|---|
| 124 | * @see <a href="https://docs.oracle.com/javase/8/docs/api/javax/swing/Action.html">Interface Action</a>
|
|---|
| 125 | */
|
|---|
| 126 | protected final void toggleSelectedState(ActionEvent e) {
|
|---|
| 127 | if (e == null || !(e.getSource() instanceof JToggleButton ||
|
|---|
| 128 | e.getSource() instanceof JCheckBox ||
|
|---|
| 129 | e.getSource() instanceof JRadioButton ||
|
|---|
| 130 | e.getSource() instanceof JCheckBoxMenuItem ||
|
|---|
| 131 | e.getSource() instanceof JRadioButtonMenuItem
|
|---|
| 132 | )) {
|
|---|
| 133 | setSelected(!isSelected());
|
|---|
| 134 | }
|
|---|
| 135 | }
|
|---|
| 136 | }
|
|---|