1 /*
2 * Copyright (c) 2007 Creative Sphere Limited.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 *
10 * Creative Sphere - initial API and implementation
11 *
12 */
13 package org.abstracthorizon.aequo.action;
14
15 import java.awt.event.ActionEvent;
16 import java.net.URL;
17 import java.text.MessageFormat;
18
19 import javax.swing.AbstractAction;
20 import javax.swing.Action;
21 import javax.swing.Icon;
22 import javax.swing.ImageIcon;
23 import javax.swing.JOptionPane;
24 import javax.swing.ProgressMonitor;
25
26 import org.abstracthorizon.aequo.GlobalContext;
27 import org.abstracthorizon.aequo.util.TaskUtilities;
28
29 /**
30 * Base action to be extended. It provides {@link ProgressMonitor} and confirmation facilities to
31 * subclasses.
32 *
33 * @author Daniel Sendula
34 */
35 public abstract class BaseAction extends AbstractAction {
36
37 /** Message to be displayed while progressing through the operation */
38 protected Object message;
39
40 /** Global context */
41 protected GlobalContext context;
42
43 /** Is confirmation required before operation is to start */
44 protected boolean confirmRequired;
45
46 /** Action is directional - it performs operation on source and destination column */
47 protected boolean directionalAction;
48
49 /** Column this operation is supposed to be invoked on */
50 protected int column = 0;
51
52 /** Destination column for the action */
53 protected int destColumn = -1;
54
55 /** Empty icon for aligning icons in the menus */
56 public static final Icon EMPTY_ICON = new ImageIcon(BaseAction.class.getResource("Empty.png"));
57
58 /** <-- */
59 public static final String PREFIX = "<--";
60
61 /** --> */
62 public static final String SUFFIX = "-->";
63
64
65
66 /**
67 * Constructor
68 */
69 public BaseAction() {
70 autoloadIcon();
71 }
72
73 /**
74 * Loads icon from the url obtained from {@link #getIconURL()} method
75 */
76 protected void autoloadIcon() {
77 URL url = getIconURL();
78 if (url != null) {
79 Icon icon = new ImageIcon(url);
80 putValue(Action.SMALL_ICON, icon);
81 } else {
82 putValue(Action.SMALL_ICON, EMPTY_ICON);
83 }
84 }
85
86 /**
87 * This method checks if resource with the name of the class exists in the classes directory
88 *
89 * @return URL or <code>null</code>
90 */
91 protected URL getIconURL() {
92 String name = getClass().getName();
93 int i = name.lastIndexOf('.');
94 if (i > 0) {
95 name = name.substring(i + 1);
96 }
97 if (name.endsWith("Action")) {
98 name = name.substring(0, name.length() - 6);
99 }
100 URL url = getClass().getResource(name + ".png");
101
102 if (directionalAction && (url == null)) {
103 url = getClass().getResource(name + "_" + column + ".png");
104 }
105 return url;
106 }
107
108 /**
109 * Sets name of the action
110 * @param name name
111 */
112 public void setName(String name) {
113 putValue(Action.NAME, name);
114 }
115
116 /**
117 * Sets description of the action
118 * @param description description
119 */
120 public void setDescription(String description) {
121 putValue(Action.SHORT_DESCRIPTION, description);
122 putValue(Action.LONG_DESCRIPTION, description);
123 }
124
125 /**
126 * Sets the global context
127 * @param context
128 */
129 public void setGlobalContext(GlobalContext context) {
130 this.context = context;
131 }
132
133 /**
134 * Returns global context
135 * @return global context
136 */
137 public GlobalContext getGlobalContext() {
138 return context;
139 }
140
141 /**
142 * Sets if confirmation is required
143 * @param confirmRequired is confirmation required
144 */
145 public void setConfirmRequired(boolean confirmRequired) {
146 this.confirmRequired = confirmRequired;
147 }
148
149 /**
150 * Returns if confirmation is required
151 * @return <code>true</code> if confirmation is required
152 */
153 public boolean isConfirmRequired() {
154 return confirmRequired;
155 }
156
157 /**
158 * Sets if action is directional.
159 * @param directional is action directional.
160 */
161 public void setDirectionalAction(boolean directional) {
162 this.directionalAction = directional;
163 }
164
165 /**
166 * Returns if action is directional.
167 *
168 * @return <code>true</code> if action is directional.
169 * @see #directionalAction
170 */
171 public boolean isDirectionalAction() {
172 return directionalAction;
173 }
174
175 /**
176 * Sets message
177 * @param message message
178 * @see #message
179 */
180 public void setMessage(Object message) {
181 this.message = message;
182 }
183
184 /**
185 * Returns message
186 * @return message
187 * @see #message
188 */
189 public Object getMessage() {
190 return message;
191 }
192
193 /**
194 * Sets column
195 * @param column column
196 * @see #column
197 */
198 public void setColumn(int column) {
199 this.column = column;
200 if (directionalAction) {
201 destColumn = 1 - column;
202 autoloadIcon();
203 Object name = super.getValue(Action.NAME);
204 if ((name != null) && (changeSupport != null)) {
205 changeSupport.firePropertyChange(Action.NAME, name, getValue(Action.NAME));
206 }
207 }
208 }
209
210 /**
211 * Returns column
212 * @return column
213 * @see #column
214 */
215 public int getColumn() {
216 return column;
217 }
218
219
220 /**
221 * Sets destination column
222 * @param column destination column
223 */
224 public void setDestinationColumn(int column) {
225 this.destColumn = column;
226 }
227
228 /**
229 * Returns destination column
230 * @return destination column
231 */
232 public int getDestinationColumn() {
233 return destColumn;
234 }
235
236 /**
237 * Returns value as per {@link Action} interface but replaces name on the fly with
238 * message formatted with prefix and suffix as first and second parameter.
239 *
240 * @param key key
241 *
242 * @return value
243 */
244 public Object getValue(String key) {
245 Object value = super.getValue(key);
246
247 if (directionalAction && key.equals(Action.NAME) && (value != null)) {
248 String prefix = "";
249 if (isEnabled() && (column > destColumn)) {
250 prefix = PREFIX;
251 }
252 String suffix = "";
253 if (isEnabled() && (destColumn > column)) {
254 suffix = SUFFIX;
255 }
256 value = MessageFormat.format(value.toString(), new Object[]{prefix, suffix});
257 }
258 return value;
259 }
260
261
262 /**
263 * Invoken by the framework (system). This method checks if action is enabled and then invokes it.
264 * @param e event
265 */
266 public void actionPerformed(final ActionEvent e) {
267 if (isEnabled()) {
268 invokeAction(e);
269 }
270 }
271
272 /**
273 * Invokes action. This method checks if confirmation is required and if so
274 * it invokes it in separate thread.
275 * @param e event
276 */
277 protected void invokeAction(final ActionEvent e) {
278 int res = 0;
279 if (isConfirmRequired()) {
280 res = showConfirmDialog();
281 }
282 if (!confirmRequired || (res == JOptionPane.OK_OPTION)) {
283 final ProgressMonitor progressMonitor = new ProgressMonitor(null, message, "", 0, 1);
284 progressMonitor.setProgress(0);
285 progressMonitor.setMillisToDecideToPopup(500);
286 progressMonitor.setMillisToPopup(1000);
287 Runnable task = new Runnable() {
288 public void run() {
289 try {
290 perform(e, progressMonitor);
291 } finally {
292 progressMonitor.close();
293 }
294 }
295 };
296 TaskUtilities.invokeLater(task);
297 }
298 }
299
300 /**
301 * This method shows confirmation dialog
302 * @return result as in {@link JOptionPane#showConfirmDialog(java.awt.Component, Object)}
303 */
304 protected int showConfirmDialog() {
305 int res = JOptionPane.showConfirmDialog(null, updateMessage(), "Confirm", JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, null);
306 return res;
307 }
308
309 /**
310 * This method allows sub-classes to update message with, for instance,
311 * list of selected files, before being displayed on the dialog.
312 *
313 * @return message
314 */
315 protected Object updateMessage() {
316 return message;
317 }
318
319 /**
320 * <p>
321 * This method is to be called for action to be invoked. This method
322 * is called from {@link #perform(ActionEvent, ProgressMonitor).
323 * </p>
324 * <p>
325 * Subclasses are supposed to override this method if no progress is
326 * to be monitored during the action. Otherwise they should override
327 * {@link #perform(ActionEvent, ProgressMonitor)}
328 * </p>
329 *
330 * @param e event
331 */
332 public void perform(final ActionEvent e) {
333 }
334
335 /**
336 * <p>
337 * This method is called from {@link #invokeAction(ActionEvent)} method.
338 * </p>
339 * <p>
340 * Subclasses are supposed to override this method if progress is to be
341 * monitored. Otherwise override {@link #perform(ActionEvent)}
342 * </p>
343 *
344 * @param e event
345 * @param monitor monitor
346 */
347 public void perform(final ActionEvent e, ProgressMonitor monitor) {
348 perform(e);
349 }
350 }