1 /*******************************************************************************
2  * Copyright (c) 2005, 2015 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.ui.internal.navigator;
15 
16 import org.eclipse.jface.action.Action;
17 import org.eclipse.jface.action.IAction;
18 import org.eclipse.jface.util.IPropertyChangeListener;
19 import org.eclipse.jface.util.PropertyChangeEvent;
20 import org.eclipse.swt.SWT;
21 import org.eclipse.swt.events.KeyAdapter;
22 import org.eclipse.swt.events.KeyEvent;
23 import org.eclipse.swt.events.MouseAdapter;
24 import org.eclipse.swt.events.MouseEvent;
25 import org.eclipse.swt.widgets.Event;
26 import org.eclipse.swt.widgets.Listener;
27 import org.eclipse.swt.widgets.Text;
28 import org.eclipse.ui.IActionBars;
29 import org.eclipse.ui.PlatformUI;
30 import org.eclipse.ui.actions.ActionFactory;
31 
32 /**
33  * Handles the redirection of the global Cut, Copy, Paste, and
34  * Select All actions to either the current inline text control
35  * or the part's supplied action handler.
36  * <p>
37  * This class may be instantiated; it is not intended to be subclassed.
38  * </p><p>
39  * Example usage:
40  * </p>
41  * <pre>
42  * textActionHandler = new TextActionHandler(this.getViewSite().getActionBars());
43  * textActionHandler.addText((Text)textCellEditor1.getControl());
44  * textActionHandler.addText((Text)textCellEditor2.getControl());
45  * textActionHandler.setSelectAllAction(selectAllAction);
46  * </pre>
47  */
48 public class TextActionHandler {
49 	private DeleteActionHandler textDeleteAction = new DeleteActionHandler();
50 
51 	private CutActionHandler textCutAction = new CutActionHandler();
52 
53 	private CopyActionHandler textCopyAction = new CopyActionHandler();
54 
55 	private PasteActionHandler textPasteAction = new PasteActionHandler();
56 
57 	private SelectAllActionHandler textSelectAllAction = new SelectAllActionHandler();
58 
59 	private IAction deleteAction;
60 
61 	private IAction cutAction;
62 
63 	private IAction copyAction;
64 
65 	private IAction pasteAction;
66 
67 	private IAction selectAllAction;
68 
69 	private IPropertyChangeListener deleteActionListener = new PropertyChangeListener(
70 			textDeleteAction);
71 
72 	private IPropertyChangeListener cutActionListener = new PropertyChangeListener(
73 			textCutAction);
74 
75 	private IPropertyChangeListener copyActionListener = new PropertyChangeListener(
76 			textCopyAction);
77 
78 	private IPropertyChangeListener pasteActionListener = new PropertyChangeListener(
79 			textPasteAction);
80 
81 	private IPropertyChangeListener selectAllActionListener = new PropertyChangeListener(
82 			textSelectAllAction);
83 
84 	private Listener textControlListener = new TextControlListener();
85 
86 	private Text activeTextControl;
87 
88 	private MouseAdapter mouseAdapter = new MouseAdapter() {
89 		@Override
90 		public void mouseUp(MouseEvent e) {
91 			updateActionsEnableState();
92 		}
93 	};
94 
95 	private KeyAdapter keyAdapter = new KeyAdapter() {
96 		@Override
97 		public void keyReleased(KeyEvent e) {
98 			updateActionsEnableState();
99 		}
100 	};
101 
102 	private class TextControlListener implements Listener {
103 		@Override
handleEvent(Event event)104 		public void handleEvent(Event event) {
105 			switch (event.type) {
106 			case SWT.Activate:
107 				activeTextControl = (Text) event.widget;
108 				updateActionsEnableState();
109 				break;
110 			case SWT.Deactivate:
111 				activeTextControl = null;
112 				updateActionsEnableState();
113 				break;
114 			default:
115 				break;
116 			}
117 		}
118 	}
119 
120 	private class PropertyChangeListener implements IPropertyChangeListener {
121 		private IAction actionHandler;
122 
PropertyChangeListener(IAction actionHandler)123 		protected PropertyChangeListener(IAction actionHandler) {
124 			super();
125 			this.actionHandler = actionHandler;
126 		}
127 
128 		@Override
propertyChange(PropertyChangeEvent event)129 		public void propertyChange(PropertyChangeEvent event) {
130 			if (activeTextControl != null) {
131 				return;
132 			}
133 			if (event.getProperty().equals(IAction.ENABLED)) {
134 				Boolean bool = (Boolean) event.getNewValue();
135 				actionHandler.setEnabled(bool.booleanValue());
136 			}
137 		}
138 	}
139 
140 	private class DeleteActionHandler extends Action {
DeleteActionHandler()141 		protected DeleteActionHandler() {
142 			super(CommonNavigatorMessages.Delete);
143 			setId("TextDeleteActionHandler");//$NON-NLS-1$
144 			setEnabled(false);
145 			PlatformUI.getWorkbench().getHelpSystem().setHelp(this,
146 					INavigatorHelpContextIds.TEXT_DELETE_ACTION);
147 		}
148 
149 		@Override
runWithEvent(Event event)150 		public void runWithEvent(Event event) {
151 			if (activeTextControl != null && !activeTextControl.isDisposed()) {
152 				activeTextControl.clearSelection();
153 				return;
154 			}
155 			if (deleteAction != null) {
156 				deleteAction.runWithEvent(event);
157 				return;
158 			}
159 		}
160 
161 		/**
162 		 * Update state.
163 		 */
updateEnabledState()164 		public void updateEnabledState() {
165 			if (activeTextControl != null && !activeTextControl.isDisposed()) {
166 				setEnabled(activeTextControl.getSelectionCount() > 0
167 						|| activeTextControl.getCaretPosition() < activeTextControl
168 								.getCharCount());
169 				return;
170 			}
171 			if (deleteAction != null) {
172 				setEnabled(deleteAction.isEnabled());
173 				return;
174 			}
175 			setEnabled(false);
176 		}
177 	}
178 
179 	private class CutActionHandler extends Action {
180 		protected CutActionHandler() {
181 			super(CommonNavigatorMessages.Cut);
182 			setId("TextCutActionHandler");//$NON-NLS-1$
183 			setEnabled(false);
184 			PlatformUI.getWorkbench().getHelpSystem().setHelp(this,
185 					INavigatorHelpContextIds.TEXT_CUT_ACTION);
186 		}
187 
188 		@Override
189 		public void runWithEvent(Event event) {
190 			if (activeTextControl != null && !activeTextControl.isDisposed()) {
191 				activeTextControl.cut();
192 				return;
193 			}
194 			if (cutAction != null) {
195 				cutAction.runWithEvent(event);
196 				return;
197 			}
198 		}
199 
200 		/**
201 		 * Update state.
202 		 */
203 		public void updateEnabledState() {
204 			if (activeTextControl != null && !activeTextControl.isDisposed()) {
205 				setEnabled(activeTextControl.getSelectionCount() > 0);
206 				return;
207 			}
208 			if (cutAction != null) {
209 				setEnabled(cutAction.isEnabled());
210 				return;
211 			}
212 			setEnabled(false);
213 		}
214 	}
215 
216 	private class CopyActionHandler extends Action {
CopyActionHandler()217 		protected CopyActionHandler() {
218 			super(CommonNavigatorMessages.Copy);
219 			setId("TextCopyActionHandler");//$NON-NLS-1$
220 			setEnabled(false);
221 			PlatformUI.getWorkbench().getHelpSystem().setHelp(this,
222 					INavigatorHelpContextIds.TEXT_COPY_ACTION);
223 		}
224 
225 		@Override
runWithEvent(Event event)226 		public void runWithEvent(Event event) {
227 			if (activeTextControl != null && !activeTextControl.isDisposed()) {
228 				activeTextControl.copy();
229 				return;
230 			}
231 			if (copyAction != null) {
232 				copyAction.runWithEvent(event);
233 				return;
234 			}
235 		}
236 
237 		/**
238 		 * Update the state.
239 		 */
updateEnabledState()240 		public void updateEnabledState() {
241 			if (activeTextControl != null && !activeTextControl.isDisposed()) {
242 				setEnabled(activeTextControl.getSelectionCount() > 0);
243 				return;
244 			}
245 			if (copyAction != null) {
246 				setEnabled(copyAction.isEnabled());
247 				return;
248 			}
249 			setEnabled(false);
250 		}
251 	}
252 
253 	private class PasteActionHandler extends Action {
PasteActionHandler()254 		protected PasteActionHandler() {
255 			super(CommonNavigatorMessages.Paste);
256 			setId("TextPasteActionHandler");//$NON-NLS-1$
257 			setEnabled(false);
258 			PlatformUI.getWorkbench().getHelpSystem().setHelp(this,
259 					INavigatorHelpContextIds.TEXT_PASTE_ACTION);
260 		}
261 
262 		@Override
runWithEvent(Event event)263 		public void runWithEvent(Event event) {
264 			if (activeTextControl != null && !activeTextControl.isDisposed()) {
265 				activeTextControl.paste();
266 				return;
267 			}
268 			if (pasteAction != null) {
269 				pasteAction.runWithEvent(event);
270 				return;
271 			}
272 		}
273 
274 		/**
275 		 * Update the state
276 		 */
updateEnabledState()277 		public void updateEnabledState() {
278 			if (activeTextControl != null && !activeTextControl.isDisposed()) {
279 				setEnabled(true);
280 				return;
281 			}
282 			if (pasteAction != null) {
283 				setEnabled(pasteAction.isEnabled());
284 				return;
285 			}
286 			setEnabled(false);
287 		}
288 	}
289 
290 	private class SelectAllActionHandler extends Action {
SelectAllActionHandler()291 		protected SelectAllActionHandler() {
292 			super(CommonNavigatorMessages.TextAction_selectAll);
293 			setId("TextSelectAllActionHandler");//$NON-NLS-1$
294 			setEnabled(false);
295 			PlatformUI.getWorkbench().getHelpSystem().setHelp(this,
296 					INavigatorHelpContextIds.TEXT_SELECT_ALL_ACTION);
297 		}
298 
299 		@Override
runWithEvent(Event event)300 		public void runWithEvent(Event event) {
301 			if (activeTextControl != null && !activeTextControl.isDisposed()) {
302 				activeTextControl.selectAll();
303 				return;
304 			}
305 			if (selectAllAction != null) {
306 				selectAllAction.runWithEvent(event);
307 				return;
308 			}
309 		}
310 
311 		/**
312 		 * Update the state.
313 		 */
updateEnabledState()314 		public void updateEnabledState() {
315 			if (activeTextControl != null && !activeTextControl.isDisposed()) {
316 				setEnabled(true);
317 				return;
318 			}
319 			if (selectAllAction != null) {
320 				setEnabled(selectAllAction.isEnabled());
321 				return;
322 			}
323 			setEnabled(false);
324 		}
325 	}
326 
327 	/**
328 	 * Creates a <code>Text</code> control action handler
329 	 * for the global Cut, Copy, Paste, Delete, and Select All
330 	 * of the action bar.
331 	 *
332 	 * @param actionBar the action bar to register global
333 	 *    action handlers for Cut, Copy, Paste, Delete,
334 	 * 	  and Select All
335 	 */
TextActionHandler(IActionBars actionBar)336 	public TextActionHandler(IActionBars actionBar) {
337 		super();
338 		actionBar.setGlobalActionHandler(ActionFactory.CUT.getId(),
339 				textCutAction);
340 		actionBar.setGlobalActionHandler(ActionFactory.COPY.getId(),
341 				textCopyAction);
342 		actionBar.setGlobalActionHandler(ActionFactory.PASTE.getId(),
343 				textPasteAction);
344 		actionBar.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(),
345 				textSelectAllAction);
346 		actionBar.setGlobalActionHandler(ActionFactory.DELETE.getId(),
347 				textDeleteAction);
348 	}
349 
350 	/**
351 	 * Add a <code>Text</code> control to the handler
352 	 * so that the Cut, Copy, Paste, Delete, and Select All
353 	 * actions are redirected to it when active.
354 	 *
355 	 * @param textControl the inline <code>Text</code> control
356 	 */
addText(Text textControl)357 	public void addText(Text textControl) {
358 		if (textControl == null) {
359 			return;
360 		}
361 
362 		activeTextControl = textControl;
363 		textControl.addListener(SWT.Activate, textControlListener);
364 		textControl.addListener(SWT.Deactivate, textControlListener);
365 
366 		// We really want a selection listener but it is not supported so we
367 		// use a key listener and a mouse listener to know when selection changes
368 		// may have occured
369 		textControl.addKeyListener(keyAdapter);
370 		textControl.addMouseListener(mouseAdapter);
371 
372 	}
373 
374 	/**
375 	 * Dispose of this action handler
376 	 */
dispose()377 	public void dispose() {
378 		setCutAction(null);
379 		setCopyAction(null);
380 		setPasteAction(null);
381 		setSelectAllAction(null);
382 		setDeleteAction(null);
383 	}
384 
385 	/**
386 	 * Removes a <code>Text</code> control from the handler
387 	 * so that the Cut, Copy, Paste, Delete, and Select All
388 	 * actions are no longer redirected to it when active.
389 	 *
390 	 * @param textControl the inline <code>Text</code> control
391 	 */
removeText(Text textControl)392 	public void removeText(Text textControl) {
393 		if (textControl == null) {
394 			return;
395 		}
396 
397 		textControl.removeListener(SWT.Activate, textControlListener);
398 		textControl.removeListener(SWT.Deactivate, textControlListener);
399 
400 		textControl.removeMouseListener(mouseAdapter);
401 		textControl.removeKeyListener(keyAdapter);
402 
403 		activeTextControl = null;
404 		updateActionsEnableState();
405 	}
406 
407 	/**
408 	 * Set the default <code>IAction</code> handler for the Copy
409 	 * action. This <code>IAction</code> is run only if no active
410 	 * inline text control.
411 	 *
412 	 * @param action the <code>IAction</code> to run for the
413 	 *    Copy action, or <code>null</code> if not interested.
414 	 */
setCopyAction(IAction action)415 	public void setCopyAction(IAction action) {
416 		if (copyAction == action) {
417 			return;
418 		}
419 
420 		if (copyAction != null) {
421 			copyAction.removePropertyChangeListener(copyActionListener);
422 		}
423 
424 		copyAction = action;
425 
426 		if (copyAction != null) {
427 			copyAction.addPropertyChangeListener(copyActionListener);
428 		}
429 
430 		textCopyAction.updateEnabledState();
431 	}
432 
433 	/**
434 	 * Set the default <code>IAction</code> handler for the Cut
435 	 * action. This <code>IAction</code> is run only if no active
436 	 * inline text control.
437 	 *
438 	 * @param action the <code>IAction</code> to run for the
439 	 *    Cut action, or <code>null</code> if not interested.
440 	 */
setCutAction(IAction action)441 	public void setCutAction(IAction action) {
442 		if (cutAction == action) {
443 			return;
444 		}
445 
446 		if (cutAction != null) {
447 			cutAction.removePropertyChangeListener(cutActionListener);
448 		}
449 
450 		cutAction = action;
451 
452 		if (cutAction != null) {
453 			cutAction.addPropertyChangeListener(cutActionListener);
454 		}
455 
456 		textCutAction.updateEnabledState();
457 	}
458 
459 	/**
460 	 * Set the default <code>IAction</code> handler for the Paste
461 	 * action. This <code>IAction</code> is run only if no active
462 	 * inline text control.
463 	 *
464 	 * @param action the <code>IAction</code> to run for the
465 	 *    Paste action, or <code>null</code> if not interested.
466 	 */
setPasteAction(IAction action)467 	public void setPasteAction(IAction action) {
468 		if (pasteAction == action) {
469 			return;
470 		}
471 
472 		if (pasteAction != null) {
473 			pasteAction.removePropertyChangeListener(pasteActionListener);
474 		}
475 
476 		pasteAction = action;
477 
478 		if (pasteAction != null) {
479 			pasteAction.addPropertyChangeListener(pasteActionListener);
480 		}
481 
482 		textPasteAction.updateEnabledState();
483 	}
484 
485 	/**
486 	 * Set the default <code>IAction</code> handler for the Select All
487 	 * action. This <code>IAction</code> is run only if no active
488 	 * inline text control.
489 	 *
490 	 * @param action the <code>IAction</code> to run for the
491 	 *    Select All action, or <code>null</code> if not interested.
492 	 */
setSelectAllAction(IAction action)493 	public void setSelectAllAction(IAction action) {
494 		if (selectAllAction == action) {
495 			return;
496 		}
497 
498 		if (selectAllAction != null) {
499 			selectAllAction
500 					.removePropertyChangeListener(selectAllActionListener);
501 		}
502 
503 		selectAllAction = action;
504 
505 		if (selectAllAction != null) {
506 			selectAllAction.addPropertyChangeListener(selectAllActionListener);
507 		}
508 
509 		textSelectAllAction.updateEnabledState();
510 	}
511 
512 	/**
513 	 * Set the default <code>IAction</code> handler for the Delete
514 	 * action. This <code>IAction</code> is run only if no active
515 	 * inline text control.
516 	 *
517 	 * @param action the <code>IAction</code> to run for the
518 	 *    Delete action, or <code>null</code> if not interested.
519 	 */
setDeleteAction(IAction action)520 	public void setDeleteAction(IAction action) {
521 		if (deleteAction == action) {
522 			return;
523 		}
524 
525 		if (deleteAction != null) {
526 			deleteAction.removePropertyChangeListener(deleteActionListener);
527 		}
528 
529 		deleteAction = action;
530 
531 		if (deleteAction != null) {
532 			deleteAction.addPropertyChangeListener(deleteActionListener);
533 		}
534 
535 		textDeleteAction.updateEnabledState();
536 	}
537 
538 	/**
539 	 * Update the enable state of the Cut, Copy,
540 	 * Paste, Delete, and Select All action handlers
541 	 */
updateActionsEnableState()542 	private void updateActionsEnableState() {
543 		textCutAction.updateEnabledState();
544 		textCopyAction.updateEnabledState();
545 		textPasteAction.updateEnabledState();
546 		textSelectAllAction.updateEnabledState();
547 		textDeleteAction.updateEnabledState();
548 	}
549 }
550 
551