source: josm/trunk/src/org/openstreetmap/josm/gui/widgets/TextContextualPopupMenu.java@ 9231

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

javadoc update

  • Property svn:eol-style set to native
File size: 9.1 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.GraphicsEnvironment;
7import java.awt.Toolkit;
8import java.awt.event.ActionEvent;
9import java.awt.event.KeyEvent;
10import java.beans.PropertyChangeEvent;
11import java.beans.PropertyChangeListener;
12
13import javax.swing.AbstractAction;
14import javax.swing.Action;
15import javax.swing.ImageIcon;
16import javax.swing.JMenuItem;
17import javax.swing.JPopupMenu;
18import javax.swing.KeyStroke;
19import javax.swing.event.UndoableEditEvent;
20import javax.swing.event.UndoableEditListener;
21import javax.swing.text.DefaultEditorKit;
22import javax.swing.text.JTextComponent;
23import javax.swing.undo.CannotRedoException;
24import javax.swing.undo.CannotUndoException;
25import javax.swing.undo.UndoManager;
26
27import org.openstreetmap.josm.Main;
28import org.openstreetmap.josm.tools.ImageProvider;
29
30/**
31 * A popup menu designed for text components. It displays the following actions:
32 * <ul>
33 * <li>Undo</li>
34 * <li>Redo</li>
35 * <li>Cut</li>
36 * <li>Copy</li>
37 * <li>Paste</li>
38 * <li>Delete</li>
39 * <li>Select All</li>
40 * </ul>
41 * @since 5886
42 */
43public class TextContextualPopupMenu extends JPopupMenu {
44
45 private static final String EDITABLE = "editable";
46
47 protected JTextComponent component;
48 protected boolean undoRedo;
49 protected final UndoAction undoAction = new UndoAction();
50 protected final RedoAction redoAction = new RedoAction();
51 protected final UndoManager undo = new UndoManager();
52
53 protected final transient UndoableEditListener undoEditListener = new UndoableEditListener() {
54 @Override
55 public void undoableEditHappened(UndoableEditEvent e) {
56 undo.addEdit(e.getEdit());
57 undoAction.updateUndoState();
58 redoAction.updateRedoState();
59 }
60 };
61
62 protected final transient PropertyChangeListener propertyChangeListener = new PropertyChangeListener() {
63 @Override
64 public void propertyChange(PropertyChangeEvent evt) {
65 if (EDITABLE.equals(evt.getPropertyName())) {
66 removeAll();
67 addMenuEntries();
68 }
69 }
70 };
71
72 /**
73 * Creates a new {@link TextContextualPopupMenu}.
74 */
75 protected TextContextualPopupMenu() {
76 // Restricts visibility
77 }
78
79 /**
80 * Attaches this contextual menu to the given text component.
81 * A menu can only be attached to a single component.
82 * @param component The text component that will display the menu and handle its actions.
83 * @param undoRedo {@code true} if undo/redo must be supported
84 * @return {@code this}
85 * @see #detach()
86 */
87 protected TextContextualPopupMenu attach(JTextComponent component, boolean undoRedo) {
88 if (component != null && !isAttached()) {
89 this.component = component;
90 this.undoRedo = undoRedo;
91 if (undoRedo && component.isEditable()) {
92 component.getDocument().addUndoableEditListener(undoEditListener);
93 if (!GraphicsEnvironment.isHeadless()) {
94 component.getInputMap().put(
95 KeyStroke.getKeyStroke(KeyEvent.VK_Z, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), undoAction);
96 component.getInputMap().put(
97 KeyStroke.getKeyStroke(KeyEvent.VK_Y, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), redoAction);
98 }
99 }
100 addMenuEntries();
101 component.addPropertyChangeListener(EDITABLE, propertyChangeListener);
102 }
103 return this;
104 }
105
106 private void addMenuEntries() {
107 if (component.isEditable()) {
108 if (undoRedo) {
109 add(new JMenuItem(undoAction));
110 add(new JMenuItem(redoAction));
111 addSeparator();
112 }
113 addMenuEntry(component, tr("Cut"), DefaultEditorKit.cutAction, null);
114 }
115 addMenuEntry(component, tr("Copy"), DefaultEditorKit.copyAction, "copy");
116 if (component.isEditable()) {
117 addMenuEntry(component, tr("Paste"), DefaultEditorKit.pasteAction, "paste");
118 addMenuEntry(component, tr("Delete"), DefaultEditorKit.deleteNextCharAction, null);
119 }
120 addSeparator();
121 addMenuEntry(component, tr("Select All"), DefaultEditorKit.selectAllAction, null);
122 }
123
124 /**
125 * Detaches this contextual menu from its text component.
126 * @return {@code this}
127 * @see #attach(JTextComponent, boolean)
128 */
129 protected TextContextualPopupMenu detach() {
130 if (isAttached()) {
131 component.removePropertyChangeListener(EDITABLE, propertyChangeListener);
132 removeAll();
133 if (undoRedo) {
134 component.getDocument().removeUndoableEditListener(undoEditListener);
135 }
136 component = null;
137 }
138 return this;
139 }
140
141 /**
142 * Creates a new {@link TextContextualPopupMenu} and enables it for the given text component.
143 * @param component The component that will display the menu and handle its actions.
144 * @param undoRedo Enables or not Undo/Redo feature. Not recommended for table cell editors, unless each cell provides its own editor
145 * @return The {@link PopupMenuLauncher} responsible of displaying the popup menu.
146 * Call {@link #disableMenuFor} with this object if you want to disable the menu later.
147 * @see #disableMenuFor
148 */
149 public static PopupMenuLauncher enableMenuFor(JTextComponent component, boolean undoRedo) {
150 PopupMenuLauncher launcher = new PopupMenuLauncher(new TextContextualPopupMenu().attach(component, undoRedo), true);
151 component.addMouseListener(launcher);
152 return launcher;
153 }
154
155 /**
156 * Disables the {@link TextContextualPopupMenu} attached to the given popup menu launcher and text component.
157 * @param component The component that currently displays the menu and handles its actions.
158 * @param launcher The {@link PopupMenuLauncher} obtained via {@link #enableMenuFor}.
159 * @see #enableMenuFor
160 */
161 public static void disableMenuFor(JTextComponent component, PopupMenuLauncher launcher) {
162 if (launcher.getMenu() instanceof TextContextualPopupMenu) {
163 ((TextContextualPopupMenu) launcher.getMenu()).detach();
164 component.removeMouseListener(launcher);
165 }
166 }
167
168 /**
169 * Determines if this popup is currently attached to a component.
170 * @return {@code true} if this popup is currently attached to a component, {@code false} otherwise.
171 */
172 public final boolean isAttached() {
173 return component != null;
174 }
175
176 protected void addMenuEntry(JTextComponent component, String label, String actionName, String iconName) {
177 Action action = component.getActionMap().get(actionName);
178 if (action != null) {
179 JMenuItem mi = new JMenuItem(action);
180 mi.setText(label);
181 if (iconName != null && Main.pref.getBoolean("text.popupmenu.useicons", true)) {
182 ImageIcon icon = new ImageProvider(iconName).setWidth(16).get();
183 if (icon != null) {
184 mi.setIcon(icon);
185 }
186 }
187 add(mi);
188 }
189 }
190
191 protected class UndoAction extends AbstractAction {
192
193 /**
194 * Constructs a new {@code UndoAction}.
195 */
196 public UndoAction() {
197 super(tr("Undo"));
198 setEnabled(false);
199 }
200
201 @Override
202 public void actionPerformed(ActionEvent e) {
203 try {
204 undo.undo();
205 } catch (CannotUndoException ex) {
206 if (Main.isTraceEnabled()) {
207 Main.trace(ex.getMessage());
208 }
209 } finally {
210 updateUndoState();
211 redoAction.updateRedoState();
212 }
213 }
214
215 public void updateUndoState() {
216 if (undo.canUndo()) {
217 setEnabled(true);
218 putValue(Action.NAME, undo.getUndoPresentationName());
219 } else {
220 setEnabled(false);
221 putValue(Action.NAME, tr("Undo"));
222 }
223 }
224 }
225
226 protected class RedoAction extends AbstractAction {
227
228 /**
229 * Constructs a new {@code RedoAction}.
230 */
231 public RedoAction() {
232 super(tr("Redo"));
233 setEnabled(false);
234 }
235
236 @Override
237 public void actionPerformed(ActionEvent e) {
238 try {
239 undo.redo();
240 } catch (CannotRedoException ex) {
241 if (Main.isTraceEnabled()) {
242 Main.trace(ex.getMessage());
243 }
244 } finally {
245 updateRedoState();
246 undoAction.updateUndoState();
247 }
248 }
249
250 public void updateRedoState() {
251 if (undo.canRedo()) {
252 setEnabled(true);
253 putValue(Action.NAME, undo.getRedoPresentationName());
254 } else {
255 setEnabled(false);
256 putValue(Action.NAME, tr("Redo"));
257 }
258 }
259 }
260}
Note: See TracBrowser for help on using the repository browser.