/******************************************************************************* * 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}. * *

* 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 * true or false. * *

* 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 (undo or redo). * *

* Implementation is partially copied from * org.eclipse.ui.internal.handlers.WidgetMethodHandler. * * @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 null. */ 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; null if none. */ protected Callable 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 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; } }