Bug 573692: [UI-Workbench] Add ActionHandler supporting IUpdate.update

Change-Id: I538000e28b64fb7782841932689de8ad0d60ac9d
diff --git a/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ecommons/ui/workbench/texteditor/ActionHandler.java b/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ecommons/ui/workbench/texteditor/ActionHandler.java
new file mode 100644
index 0000000..21c91f0
--- /dev/null
+++ b/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ecommons/ui/workbench/texteditor/ActionHandler.java
@@ -0,0 +1,141 @@
+/*=============================================================================#
+ # Copyright (c) 2021 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.ecommons.ui.workbench.texteditor;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.HandlerEvent;
+import org.eclipse.core.commands.IHandlerListener;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.ui.texteditor.IUpdate;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+import org.eclipse.statet.jcommons.lang.ObjectUtils.ToStringBuilder;
+
+
+@NonNullByDefault
+public class ActionHandler extends AbstractHandler {
+	
+	
+	private final IAction action;
+	
+	private boolean isDisposed;
+	
+	private @Nullable IPropertyChangeListener propertyChangeListener;
+	
+	
+	public ActionHandler(final IAction action) {
+		this.action= action;
+	}
+	
+	@Override
+	public void dispose() {
+		this.isDisposed= true;
+		detachListener();
+		super.dispose();
+	}
+	
+	
+	public final IAction getAction() {
+		return this.action;
+	}
+	
+	
+	@Override
+	public void addHandlerListener(final IHandlerListener handlerListener) {
+		if (this.isDisposed) {
+			return;
+		}
+		if (!hasListeners()) {
+			attachListener();
+		}
+		super.addHandlerListener(handlerListener);
+	}
+	
+	private void attachListener() {
+		if (this.propertyChangeListener == null) {
+			final var listener= new IPropertyChangeListener() {
+				@Override
+				public void propertyChange(final PropertyChangeEvent event) {
+					final String property= event.getProperty();
+					fireHandlerChanged(new HandlerEvent(ActionHandler.this,
+							IAction.ENABLED.equals(property),
+							IAction.HANDLED.equals(property) ));
+				}
+			};
+			this.propertyChangeListener= listener;
+		}
+		this.action.addPropertyChangeListener(this.propertyChangeListener);
+	}
+	
+	private void detachListener() {
+		final var listener= this.propertyChangeListener;
+		if (listener != null) {
+			this.propertyChangeListener= null;
+			this.action.removePropertyChangeListener(listener);
+		}
+	}
+	
+	
+	@Override
+	public void setEnabled(final @Nullable Object evaluationContext) {
+		if (this.action instanceof IUpdate) {
+			((IUpdate)this.action).update();
+		}
+		setBaseEnabled(this.action.isEnabled());
+	}
+	
+	@Override
+	public final @Nullable Object execute(final ExecutionEvent event)
+			throws ExecutionException {
+		if ((this.action.getStyle() == IAction.AS_CHECK_BOX)
+				|| (this.action.getStyle() == IAction.AS_RADIO_BUTTON)) {
+			this.action.setChecked(!this.action.isChecked());
+		}
+		try {
+			final Object trigger= event.getTrigger();
+			final Event swtEvent= (trigger instanceof Event) ? (Event)trigger : new Event();
+			this.action.runWithEvent(swtEvent);
+			return null;
+		}
+		catch (final Exception e) {
+			throw new ExecutionException(e.getMessage(), e);
+		}
+	}
+	
+	@Override
+	public boolean isHandled() {
+		return this.action.isHandled();
+	}
+	
+	@Override
+	public boolean isEnabled() {
+		return this.action.isEnabled();
+	}
+	
+	
+	@Override
+	public final String toString() {
+		final ToStringBuilder sb= new ToStringBuilder(ActionHandler.class);
+		sb.append(' ', this.action.toString());
+		return sb.toString();
+	}
+	
+}