]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.views.text/src/org/simantics/views/text/internal/TextViewerUndoHandler.java
Added org.simantics.views.text[.ontology] for modelled source viewers
[simantics/platform.git] / bundles / org.simantics.views.text / src / org / simantics / views / text / internal / TextViewerUndoHandler.java
diff --git a/bundles/org.simantics.views.text/src/org/simantics/views/text/internal/TextViewerUndoHandler.java b/bundles/org.simantics.views.text/src/org/simantics/views/text/internal/TextViewerUndoHandler.java
new file mode 100644 (file)
index 0000000..f1c8ade
--- /dev/null
@@ -0,0 +1,169 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Semantum Oy - (#7066) initial API and implementation
+ *******************************************************************************/
+package org.simantics.views.text.internal;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.concurrent.Callable;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExecutableExtension;
+import org.eclipse.jface.text.IUndoManager;
+import org.eclipse.jface.text.TextViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+
+/**
+ * Handles the undo/redo command for {@link TextViewer}s through
+ * {@link IUndoManager}.
+ * 
+ * <p>
+ * The implementation looks for an IUndoManager from the current focus control
+ * using the {@link TextViewerConstants#KEY_UNDO_MANAGER} data key. Its
+ * existence determines whether this handler {@link #isHandled()} returns
+ * <code>true</code> or <code>false</code>.
+ *
+ * <p>
+ * The handler expects to receive a single string as an argument through the
+ * extension definitions ({@link IExecutableExtension}) that determines which
+ * method is invoked from IUndoManager (<code>undo</code> or <code>redo</code>).
+ * 
+ * <p>
+ * Implementation is partially copied from
+ * <code>org.eclipse.ui.internal.handlers.WidgetMethodHandler</code>.
+ * 
+ * @since 1.28.0
+ */
+public class TextViewerUndoHandler extends AbstractHandler implements IExecutableExtension {
+
+       /**
+        * The parameters to pass to the method this handler invokes. This handler
+        * always passes no parameters.
+        */
+       protected static final Class<?>[] NO_PARAMETERS = new Class[0];
+
+       public TextViewerUndoHandler() {
+               display = Display.getCurrent();
+               if (display != null) {
+                       focusListener = new Listener() {
+                               @Override
+                               public void handleEvent(Event event) {
+                                       updateEnablement();
+                               }
+                       };
+                       display.addFilter(SWT.FocusIn, focusListener);
+               }
+       }
+
+       void updateEnablement() {
+               boolean rc = isHandled();
+               if (rc != isEnabled()) {
+                       setBaseEnabled(rc);
+               }
+       }
+
+       /**
+        * The name of the method to be invoked by this handler. This value should
+        * never be <code>null</code>.
+        */
+       protected String methodName;
+       private Listener focusListener;
+       private Display display;
+
+       @Override
+       public Object execute(final ExecutionEvent event) throws ExecutionException {
+               Callable<?> runnable = getMethodToExecute();
+               if (runnable != null) {
+                       try {
+                               runnable.call();
+                       } catch (ExecutionException e) {
+                               throw e;
+                       } catch (Exception e) {
+                               throw new ExecutionException("Unexpected failure executing method " + methodName + " through " + runnable);
+                       }
+               }
+               return null;
+       }
+
+       @Override
+       public final boolean isHandled() {
+               return getMethodToExecute() != null;
+       }
+
+       /**
+        * Looks up the method on the focus control.
+        *
+        * @return The method on the focus control; <code>null</code> if none.
+        */
+       protected Callable<Boolean> getMethodToExecute() {
+               Display display = Display.getCurrent();
+               if (display == null)
+                       return null;
+
+               Control focusControl = display.getFocusControl();
+               if (focusControl == null)
+                       return null;
+
+               IUndoManager undoManager = (IUndoManager) focusControl.getData(TextViewerConstants.KEY_UNDO_MANAGER);
+               if (undoManager == null)
+                       return null;
+
+               try {
+                       Method method = undoManager.getClass().getMethod(methodName, NO_PARAMETERS);
+                       if (method != null)
+                               return runner(undoManager, method);
+               } catch (NoSuchMethodException e) {
+                       //      Fall through...
+               }
+
+               return null;
+       }
+
+       protected Callable<Boolean> runner(IUndoManager undoManager, Method method) {
+               return () -> {
+                       try {
+                               method.invoke(undoManager);
+                               return true;
+                       } catch (IllegalAccessException e) {
+                               // The method is protected, so do nothing.
+                               return false;
+                       } catch (InvocationTargetException e) {
+                               throw new ExecutionException(
+                                               "An exception occurred while executing " //$NON-NLS-1$
+                                                               + method.getName(), e
+                                                               .getTargetException());
+
+                       }
+               };
+       }
+
+       @Override
+       public void setInitializationData(IConfigurationElement config, String propertyName, Object data) {
+               methodName = data.toString();
+       }
+
+       @Override
+       public void dispose() {
+               if (display != null && !display.isDisposed()) {
+                       display.removeFilter(SWT.FocusIn, focusListener);
+               }
+               display = null;
+               focusListener = null;
+       }
+
+}
\ No newline at end of file