/*=============================================================================#
# Copyright (c) 2007-2016 Stephan Wahlbrink (WalWare.de) and others.
# 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:
# Stephan Wahlbrink - initial API and implementation
#=============================================================================*/
package de.walware.statet.nico.ui.actions;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.jface.window.IShellProvider;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.commands.ICommandService;
import org.eclipse.ui.progress.IProgressService;
import org.eclipse.ui.services.IServiceLocator;
import de.walware.ecommons.ui.util.UIAccess;
import de.walware.statet.nico.core.runtime.ToolProcess;
import de.walware.statet.nico.core.util.IToolProvider;
import de.walware.statet.nico.core.util.IToolRetargetable;
/**
* Abstract command handler (not tool event handler)
* supporting tool assignment by the {@link IToolRetargetable} interface
*/
public abstract class ToolRetargetableHandler extends AbstractHandler implements IToolRetargetable {
protected static final int S_INIT = 1;
protected static final int S_ONAIR = 2;
protected static final int S_DISPOSED = 3;
protected static class ChangedStateException extends IllegalStateException {
private static final long serialVersionUID = 1L;
}
protected class ElementUpdater implements Runnable {
private final String fCommandId;
public ElementUpdater(final String commandId) {
fCommandId = commandId;
assert (getServiceLocator() != null);
}
@Override
public void run() {
final ICommandService commandService = (ICommandService) getServiceLocator().getService(ICommandService.class);
if (commandService != null) {
commandService.refreshElements(fCommandId, null);
}
}
public void schedule() {
final Display display = UIAccess.getDisplay(getShell());
if (display != null) {
if (display.getThread() == Thread.currentThread()) {
run();
}
else {
display.asyncExec(this);
}
}
}
}
/** providing the active tool */
private final IToolProvider fToolProvider;
/** optional service locator */
private final IServiceLocator fServiceLocator;
/** internal state of this handler */
private int fState;
private ToolProcess fTool;
public ToolRetargetableHandler(final IToolProvider toolProvider, final IServiceLocator serviceLocator) {
super();
fState = S_INIT;
fServiceLocator = serviceLocator;
fToolProvider = toolProvider;
if (fToolProvider != null) {
fToolProvider.addToolRetargetable(this);
}
}
/**
* Must be call at the end of the constructor to finish initialization.
*/
protected void init() {
setTool(fToolProvider.getTool());
fState = S_ONAIR;
}
/**
* {@inheritDoc}
*/
@Override
public void dispose() {
if (fState >= S_DISPOSED) {
return;
}
fState = S_DISPOSED;
if (fToolProvider != null) {
fToolProvider.removeToolRetargetable(this);
}
synchronized (this) {
fTool = null;
}
super.dispose();
}
@Override
public final void setTool(final ToolProcess tool) {
boolean update = false;
synchronized (this) {
if (fState == S_DISPOSED) {
return;
}
if ((fTool == null) ?
(tool == null && getState() == S_ONAIR) :
(tool == fTool) ) {
return;
}
fTool = tool;
update = handleToolChanged();
}
if (update) {
doRefresh();
}
}
@Override
public void toolTerminated() {
boolean update = false;
synchronized (this) {
update = handleToolChanged();
}
if (update) {
doRefresh();
}
}
protected final int getState() {
return fState;
}
/**
* @return current associated tool
*/
public final ToolProcess getTool() {
return fTool;
}
/**
* Checks if action is enabled for the current tool and returns it
*
* @return current associated tool
*/
protected final ToolProcess getCheckedTool() {
synchronized (this) {
if (fState != S_ONAIR || !isEnabled()) {
throw new ChangedStateException();
}
return fTool;
}
}
/**
* Optional service locator (window, view, page,...)
*
* @return the service locator or <code>null</code>
*/
protected IServiceLocator getServiceLocator() {
return fServiceLocator;
}
protected IProgressService getProgressService() {
return (IProgressService) getServiceLocator().getService(IProgressService.class);
}
protected Shell getShell() {
if (fServiceLocator instanceof IShellProvider) {
return ((IShellProvider) fServiceLocator).getShell();
}
return null;
}
/**
* Is called when the tool changed. Can be overwritten to
* update the handler state.
*
* @return if {@link #doRefresh()} should be called
*/
public boolean handleToolChanged() {
final boolean isEnabled = evaluateEnabled();
if (isEnabled != isEnabled()) {
setBaseEnabled(isEnabled);
return true;
}
return false;
}
/**
* Is called when the tool was terminated. Can be overwritten to
* update the handler state or do nothing.
*
* @return if {@link #doRefresh()} should be called
*/
public boolean handleToolTerminated() {
return handleToolChanged();
}
/**
* Computes the enablement state of the handler. Can be overwritten
* to change the criteria.
*
* @return if handler should set to enabled
*/
protected boolean evaluateEnabled() {
final ToolProcess tool = getTool();
return (tool != null
&& !tool.isTerminated());
}
@Override
public final Object execute(final ExecutionEvent event) throws ExecutionException {
if (fToolProvider != null) {
final ToolProcess tool = fToolProvider.getTool();
if (tool != fTool) {
setTool(tool);
}
}
try {
doExecute(event);
}
catch (final ChangedStateException e) {
}
return null;
}
protected void doRefresh() {
}
protected abstract Object doExecute(final ExecutionEvent event);
}