Bug 572883: Pattern Viewer in Templates pages should support copy

- Add the action 'Select All' and 'Copy' to the context menu of the
  pattern viewer on the 'Templates' view.

Also:
- Set ActionDefitionId to the action on the templates preference page
  and on the edit templates dialog so that the key bindings are shown
  in the context menus.


Change-Id: I56c24ac1ea0ac1b7f3101f267ba201b6d0904009
Reviewed-on: https://git.eclipse.org/r/c/platform/eclipse.platform.text/+/179385
Tested-by: Platform Bot <platform-bot@eclipse.org>
Reviewed-by: Sebastian Ratz <sebastian.ratz@sap.com>
Reviewed-by: Matthias Becker <ma.becker@sap.com>
diff --git a/org.eclipse.ui.workbench.texteditor/META-INF/MANIFEST.MF b/org.eclipse.ui.workbench.texteditor/META-INF/MANIFEST.MF
index 802abc8..5d2171a 100644
--- a/org.eclipse.ui.workbench.texteditor/META-INF/MANIFEST.MF
+++ b/org.eclipse.ui.workbench.texteditor/META-INF/MANIFEST.MF
@@ -9,14 +9,15 @@
 Bundle-Localization: plugin
 Export-Package: 
  org.eclipse.ui.contentassist,
- org.eclipse.ui.internal.texteditor; texteditor="split"; mandatory:="texteditor"; x-friends:="org.eclipse.ui.editors",
+ org.eclipse.ui.internal.texteditor;texteditor=split;mandatory:=texteditor;x-friends:="org.eclipse.ui.editors",
  org.eclipse.ui.internal.texteditor.codemining;x-internal:=true,
  org.eclipse.ui.internal.texteditor.quickdiff;x-internal:=true,
  org.eclipse.ui.internal.texteditor.quickdiff.compare.equivalence;x-internal:=true,
  org.eclipse.ui.internal.texteditor.rulers;x-internal:=true,
  org.eclipse.ui.internal.texteditor.spelling;x-internal:=true,
+ org.eclipse.ui.internal.texteditor.templates;x-internal:=true,
  org.eclipse.ui.internal.views.minimap;x-internal:=true,
- org.eclipse.ui.texteditor; texteditor="split"; mandatory:="texteditor",
+ org.eclipse.ui.texteditor;texteditor=split;mandatory:=texteditor,
  org.eclipse.ui.texteditor.link,
  org.eclipse.ui.texteditor.quickdiff,
  org.eclipse.ui.texteditor.rulers,
diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/templates/TextViewerAction.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/templates/TextViewerAction.java
new file mode 100644
index 0000000..697d728
--- /dev/null
+++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/templates/TextViewerAction.java
@@ -0,0 +1,58 @@
+package org.eclipse.ui.internal.texteditor.templates;
+
+import org.eclipse.jface.action.Action;
+
+import org.eclipse.jface.text.ITextOperationTarget;
+import org.eclipse.jface.text.ITextViewer;
+
+import org.eclipse.ui.texteditor.IUpdate;
+
+public class TextViewerAction extends Action implements IUpdate {
+
+	private int fOperationCode = -1;
+	private ITextOperationTarget fOperationTarget;
+
+	/**
+	 * Creates a new action.
+	 *
+	 * @param viewer        the viewer
+	 * @param operationCode the opcode
+	 */
+	public TextViewerAction(ITextViewer viewer, int operationCode) {
+		fOperationCode = operationCode;
+		fOperationTarget = viewer.getTextOperationTarget();
+		update();
+	}
+
+	/**
+	 * Updates the enabled state of the action. Fires a property change if the
+	 * enabled state changes.
+	 *
+	 * @see Action#firePropertyChange(String, Object, Object)
+	 */
+	@Override
+	public void update() {
+		// XXX: workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=206111
+		if (fOperationCode == ITextOperationTarget.UNDO || fOperationCode == ITextOperationTarget.REDO)
+			return;
+
+		boolean wasEnabled = isEnabled();
+		boolean isEnabled = (fOperationTarget != null && fOperationTarget.canDoOperation(fOperationCode));
+		setEnabled(isEnabled);
+
+		if (wasEnabled != isEnabled) {
+			firePropertyChange(ENABLED, wasEnabled ? Boolean.TRUE : Boolean.FALSE,
+					isEnabled ? Boolean.TRUE : Boolean.FALSE);
+		}
+	}
+
+	/**
+	 * @see Action#run()
+	 */
+	@Override
+	public void run() {
+		if (fOperationCode != -1 && fOperationTarget != null) {
+			fOperationTarget.doOperation(fOperationCode);
+		}
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/templates/AbstractTemplatesPage.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/templates/AbstractTemplatesPage.java
index 8b797de..7b7e6b1 100644
--- a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/templates/AbstractTemplatesPage.java
+++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/templates/AbstractTemplatesPage.java
@@ -37,6 +37,8 @@
 import org.eclipse.swt.dnd.Transfer;
 import org.eclipse.swt.events.ControlEvent;
 import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.graphics.Point;
 import org.eclipse.swt.layout.GridData;
@@ -84,6 +86,7 @@
 import org.eclipse.jface.text.Document;
 import org.eclipse.jface.text.IDocument;
 import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextOperationTarget;
 import org.eclipse.jface.text.ITextViewerExtension5;
 import org.eclipse.jface.text.source.ISourceViewer;
 import org.eclipse.jface.text.source.SourceViewer;
@@ -101,6 +104,7 @@
 import org.eclipse.ui.dialogs.PreferencesUtil;
 import org.eclipse.ui.internal.texteditor.NLSUtility;
 import org.eclipse.ui.internal.texteditor.TextEditorPlugin;
+import org.eclipse.ui.internal.texteditor.templates.TextViewerAction;
 import org.eclipse.ui.part.Page;
 
 import org.eclipse.ui.texteditor.ITextEditor;
@@ -467,6 +471,8 @@
 	/** Paste action support for the editor. */
 	private IAction fEditorOldPasteAction;
 	private IAction fEditorPasteAction;
+	private TextViewerAction fPatternViewerCopyAction;
+	private TextViewerAction fPatternViewerSelectAllAction;
 
 
 	/**
@@ -488,13 +494,13 @@
 
 	@Override
 	public void createControl(Composite ancestor) {
-		setupActions();
-
+		createActions();
 		fControl= new SashForm(ancestor, SWT.VERTICAL);
 
 		createTemplateTree(fControl);
 		createPatternForm(fControl);
 
+		setupActions();
 		hookContextMenu();
 		initializeDND();
 		updateButtons();
@@ -605,6 +611,20 @@
 		return viewer;
 	}
 
+	private void fillPatternViewerContextMenu(IMenuManager menu) {
+		menu.add(new Separator(ITextEditorActionConstants.GROUP_EDIT));
+		menu.appendToGroup(ITextEditorActionConstants.GROUP_EDIT, fPatternViewerCopyAction);
+		menu.appendToGroup(ITextEditorActionConstants.GROUP_EDIT, fPatternViewerSelectAllAction);
+	}
+
+	/**
+	 * @param event The event
+	 */
+	private void updateCopyAction(SelectionChangedEvent event) {
+		if (fPatternViewerCopyAction != null)
+			fPatternViewerCopyAction.update();
+	}
+
 	/**
 	 * Returns the pattern viewer created by createPatternViewer()
 	 *
@@ -795,17 +815,36 @@
 	 * Setup the menu, context menu and toolbar actions.
 	 */
 	private void setupActions() {
-		createActions();
 		IActionBars actionBars= getSite().getActionBars();
 
-		actionBars.setGlobalActionHandler(ActionFactory.PASTE.getId(), fPasteAction);
 		fPasteAction.setActionDefinitionId(ActionFactory.PASTE.getCommandId());
 		fPasteAction.setText(TemplatesMessages.TemplatesPage_paste);
-		actionBars.setGlobalActionHandler(ActionFactory.COPY.getId(), fCopyAction);
 		fCopyAction.setActionDefinitionId(ActionFactory.COPY.getCommandId());
 		fCopyAction.setText(TemplatesMessages.TemplatesPage_copy);
+		fRemoveAction.setActionDefinitionId(ActionFactory.DELETE.getCommandId());
+		fRemoveAction.setText(TemplatesMessages.TemplatesPage_remove);
 		fillToolbar(actionBars);
 		fillMenu(actionBars);
+		actionBars.updateActionBars();
+
+		fTreeViewer.getControl().addFocusListener(new FocusListener() {
+
+			@Override
+			public void focusLost(FocusEvent e) {
+				actionBars.setGlobalActionHandler(ActionFactory.PASTE.getId(), null);
+				actionBars.setGlobalActionHandler(ActionFactory.COPY.getId(), null);
+				actionBars.setGlobalActionHandler(ActionFactory.DELETE.getId(), null);
+				actionBars.updateActionBars();
+			}
+
+			@Override
+			public void focusGained(FocusEvent e) {
+				actionBars.setGlobalActionHandler(ActionFactory.PASTE.getId(), fPasteAction);
+				actionBars.setGlobalActionHandler(ActionFactory.COPY.getId(), fCopyAction);
+				actionBars.setGlobalActionHandler(ActionFactory.DELETE.getId(), fRemoveAction);
+				actionBars.updateActionBars();
+			}
+		});
 	}
 
 	/**
@@ -1089,6 +1128,9 @@
 		viewForm.setTopLeft(previewLabel);
 
 		fPatternViewer= createPatternViewer(viewForm);
+
+		addActionsToPatternViewer(fPatternViewer);
+
 		viewForm.setContent(fPatternViewer.getControl());
 		viewForm.addControlListener(new ControlListener() {
 			@Override
@@ -1104,6 +1146,26 @@
 		});
 	}
 
+	private void addActionsToPatternViewer(SourceViewer viewer) {
+		// create actions
+		fPatternViewerCopyAction = new TextViewerAction(viewer, ITextOperationTarget.COPY);
+		fPatternViewerCopyAction.setActionDefinitionId(ActionFactory.COPY.getCommandId());
+		fPatternViewerCopyAction.setText(TemplatesMessages.EditTemplateDialog_copy);
+
+		fPatternViewerSelectAllAction = new TextViewerAction(viewer, ITextOperationTarget.SELECT_ALL);
+		fPatternViewerSelectAllAction.setActionDefinitionId(ActionFactory.SELECT_ALL.getCommandId());
+		fPatternViewerSelectAllAction.setText(TemplatesMessages.EditTemplateDialog_select_all);
+		viewer.addSelectionChangedListener(this::updateCopyAction);
+		// create context menu
+		MenuManager manager = new MenuManager(null, null);
+		manager.setRemoveAllWhenShown(true);
+		manager.addMenuListener(this::fillPatternViewerContextMenu);
+
+		StyledText text = viewer.getTextWidget();
+		Menu menu = manager.createContextMenu(text);
+		text.setMenu(menu);
+	}
+
 	/**
 	 * Hookup the context menu
 	 */
diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/templates/TemplatePreferencePage.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/templates/TemplatePreferencePage.java
index 43dbe2c..0d57da7 100644
--- a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/templates/TemplatePreferencePage.java
+++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/templates/TemplatePreferencePage.java
@@ -69,7 +69,6 @@
 import org.eclipse.text.templates.TemplatePersistenceData;
 import org.eclipse.text.templates.TemplateReaderWriter;
 
-import org.eclipse.jface.action.Action;
 import org.eclipse.jface.action.GroupMarker;
 import org.eclipse.jface.action.IAction;
 import org.eclipse.jface.action.IMenuManager;
@@ -92,6 +91,7 @@
 import org.eclipse.jface.viewers.IStructuredSelection;
 import org.eclipse.jface.viewers.ITableLabelProvider;
 import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
 import org.eclipse.jface.viewers.StructuredSelection;
 import org.eclipse.jface.viewers.TableViewer;
 import org.eclipse.jface.viewers.Viewer;
@@ -101,7 +101,6 @@
 import org.eclipse.jface.text.Document;
 import org.eclipse.jface.text.IDocument;
 import org.eclipse.jface.text.ITextOperationTarget;
-import org.eclipse.jface.text.ITextViewer;
 import org.eclipse.jface.text.contentassist.ContentAssistant;
 import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
 import org.eclipse.jface.text.contentassist.IContentAssistant;
@@ -119,11 +118,13 @@
 import org.eclipse.ui.IWorkbenchCommandConstants;
 import org.eclipse.ui.IWorkbenchPreferencePage;
 import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.actions.ActionFactory;
 import org.eclipse.ui.handlers.IHandlerActivation;
 import org.eclipse.ui.handlers.IHandlerService;
 import org.eclipse.ui.internal.texteditor.NLSUtility;
 import org.eclipse.ui.internal.texteditor.SWTUtil;
 import org.eclipse.ui.internal.texteditor.TextEditorPlugin;
+import org.eclipse.ui.internal.texteditor.templates.TextViewerAction;
 
 import org.eclipse.ui.texteditor.ITextEditorActionConstants;
 import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
@@ -153,55 +154,6 @@
 	 */
 	protected static class EditTemplateDialog extends StatusDialog {
 
-		private class TextViewerAction extends Action implements IUpdate {
-
-			private int fOperationCode= -1;
-			private ITextOperationTarget fOperationTarget;
-
-			/**
-			 * Creates a new action.
-			 *
-			 * @param viewer the viewer
-			 * @param operationCode the opcode
-			 */
-			public TextViewerAction(ITextViewer viewer, int operationCode) {
-				fOperationCode= operationCode;
-				fOperationTarget= viewer.getTextOperationTarget();
-				update();
-			}
-
-			/**
-			 * Updates the enabled state of the action.
-			 * Fires a property change if the enabled state changes.
-			 *
-			 * @see Action#firePropertyChange(String, Object, Object)
-			 */
-			@Override
-			public void update() {
-				// XXX: workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=206111
-				if (fOperationCode == ITextOperationTarget.REDO || fOperationCode == ITextOperationTarget.REDO)
-					return;
-
-				boolean wasEnabled= isEnabled();
-				boolean isEnabled= (fOperationTarget != null && fOperationTarget.canDoOperation(fOperationCode));
-				setEnabled(isEnabled);
-
-				if (wasEnabled != isEnabled) {
-					firePropertyChange(ENABLED, wasEnabled ? Boolean.TRUE : Boolean.FALSE, isEnabled ? Boolean.TRUE : Boolean.FALSE);
-				}
-			}
-
-			/**
-			 * @see Action#run()
-			 */
-			@Override
-			public void run() {
-				if (fOperationCode != -1 && fOperationTarget != null) {
-					fOperationTarget.doOperation(fOperationCode);
-				}
-			}
-		}
-
 		private final Template fOriginalTemplate;
 
 		private Text fNameText;
@@ -553,26 +505,32 @@
 			});
 
 			TextViewerAction action= new TextViewerAction(fPatternEditor, ITextOperationTarget.UNDO);
+			action.setActionDefinitionId(ActionFactory.UNDO.getCommandId());
 			action.setText(TemplatesMessages.EditTemplateDialog_undo);
 			fGlobalActions.put(ITextEditorActionConstants.UNDO, action);
 
 			action= new TextViewerAction(fPatternEditor, ITextOperationTarget.REDO);
+			action.setActionDefinitionId(ActionFactory.REDO.getCommandId());
 			action.setText(TemplatesMessages.EditTemplateDialog_redo);
 			fGlobalActions.put(ITextEditorActionConstants.REDO, action);
 
-			action= new TextViewerAction(fPatternEditor, ITextOperationTarget.CUT);
+			action = new TextViewerAction(fPatternEditor, ITextOperationTarget.CUT);
+			action.setActionDefinitionId(ActionFactory.CUT.getCommandId());
 			action.setText(TemplatesMessages.EditTemplateDialog_cut);
 			fGlobalActions.put(ITextEditorActionConstants.CUT, action);
 
-			action= new TextViewerAction(fPatternEditor, ITextOperationTarget.COPY);
+			action = new TextViewerAction(fPatternEditor, ITextOperationTarget.COPY);
+			action.setActionDefinitionId(ActionFactory.COPY.getCommandId());
 			action.setText(TemplatesMessages.EditTemplateDialog_copy);
 			fGlobalActions.put(ITextEditorActionConstants.COPY, action);
 
 			action= new TextViewerAction(fPatternEditor, ITextOperationTarget.PASTE);
+			action.setActionDefinitionId(ActionFactory.PASTE.getCommandId());
 			action.setText(TemplatesMessages.EditTemplateDialog_paste);
 			fGlobalActions.put(ITextEditorActionConstants.PASTE, action);
 
 			action= new TextViewerAction(fPatternEditor, ITextOperationTarget.SELECT_ALL);
+			action.setActionDefinitionId(ActionFactory.SELECT_ALL.getCommandId());
 			action.setText(TemplatesMessages.EditTemplateDialog_select_all);
 			fGlobalActions.put(ITextEditorActionConstants.SELECT_ALL, action);
 
@@ -776,6 +734,9 @@
 	/** The context type registry. */
 	private ContextTypeRegistry fContextTypeRegistry;
 
+	private TextViewerAction fPatternViewerCopyAction;
+
+	private TextViewerAction fPatternViewerSelectAllAction;
 
 	/**
 	 * Creates a new template preference page.
@@ -1060,9 +1021,46 @@
 		data.heightHint= convertHeightInCharsToPixels(5);
 		control.setLayoutData(data);
 
+		addActionsToPatternViewer(viewer);
+
 		return viewer;
 	}
 
+	private void addActionsToPatternViewer(SourceViewer viewer) {
+		// create actions
+		fPatternViewerCopyAction = new TextViewerAction(viewer, ITextOperationTarget.COPY);
+		fPatternViewerCopyAction.setActionDefinitionId(ActionFactory.COPY.getCommandId());
+		fPatternViewerCopyAction.setText(TemplatesMessages.EditTemplateDialog_copy);
+
+		fPatternViewerSelectAllAction = new TextViewerAction(viewer, ITextOperationTarget.SELECT_ALL);
+		fPatternViewerSelectAllAction.setActionDefinitionId(ActionFactory.SELECT_ALL.getCommandId());
+		fPatternViewerSelectAllAction.setText(TemplatesMessages.EditTemplateDialog_select_all);
+
+		viewer.addSelectionChangedListener(this::updateCopyAction);
+		// create context menu
+		MenuManager manager = new MenuManager(null, null);
+		manager.setRemoveAllWhenShown(true);
+		manager.addMenuListener(this::fillPatternViewerContextMenu);
+
+		StyledText text = viewer.getTextWidget();
+		Menu menu = manager.createContextMenu(text);
+		text.setMenu(menu);
+	}
+
+	private void fillPatternViewerContextMenu(IMenuManager menu) {
+		menu.add(new Separator(ITextEditorActionConstants.GROUP_EDIT));
+		menu.appendToGroup(ITextEditorActionConstants.GROUP_EDIT, fPatternViewerCopyAction);
+		menu.appendToGroup(ITextEditorActionConstants.GROUP_EDIT, fPatternViewerSelectAllAction);
+	}
+
+	/**
+	 * @param event The event
+	 */
+	private void updateCopyAction(SelectionChangedEvent event) {
+		if (fPatternViewerCopyAction != null)
+			fPatternViewerCopyAction.update();
+	}
+
 	/**
 	 * Creates, configures and returns a source viewer to present the template
 	 * pattern on the preference page. Clients may override to provide a custom