/*******************************************************************************
* Copyright (c) 2010 Philipp Kursawe.
* 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:
* Philipp Kursawe (phil.kursawe@gmail.com) - initial API and implementation
******************************************************************************/
package copyto.ui.internal.commands;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.expressions.EvaluationContext;
import org.eclipse.core.expressions.IEvaluationContext;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdapterManager;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.LocalResourceManager;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StyledCellLabelProvider;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.StyledString.Styler;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.jface.window.Window;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics.TextStyle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.commands.IElementUpdater;
import org.eclipse.ui.dialogs.ListDialog;
import org.eclipse.ui.handlers.HandlerUtil;
import org.eclipse.ui.menus.UIElement;
import org.eclipse.ui.progress.IProgressConstants;
import org.eclipse.ui.progress.UIJob;
import org.eclipse.ui.progress.WorkbenchJob;
import org.osgi.framework.FrameworkUtil;
import osgiutils.services.ServiceRunnable;
import osgiutils.services.SimpleServiceRunnable;
import osgiutils.services.Services;
import copyto.core.Copyable;
import copyto.core.Results;
import copyto.core.Target;
import copyto.core.TargetDescriptor;
import copyto.core.TargetService;
import copyto.ui.IconProvider;
import copyto.ui.UIResultHandler;
import copyto.ui.WorkbenchResultHandler;
import copyto.ui.internal.Messages;
import copyto.ui.internal.models.TextSelectionCopyable;
import copyto.ui.internal.preferences.CopyToPreferencePage;
import eclipseutils.core.extensions.BaseExtensionDescriptor;
import eclipseutils.core.extensions.ExpressionEvaluatingVisitor;
import eclipseutils.core.extensions.ExtensionPoints;
import eclipseutils.core.extensions.ExtensionVisitor;
/**
*
* There are 2 types of handlers: 1. ElementHandler 2. TextEditorHandler
*
* @author <a href="mailto:kursawe@topsystem.de">Philipp Kursawe</a>
* @since 1.0
*/
public class CopyToHandler extends AbstractHandler implements IElementUpdater {
/**
*
*/
public static final String COMMAND_ID = "copyto.command"; //$NON-NLS-1$
private final IAdapterManager adapterManager = Platform.getAdapterManager();
private final class ResultHandlerDescriptor extends BaseExtensionDescriptor {
public ResultHandlerDescriptor(IConfigurationElement configElement) {
super(configElement);
}
public ImageDescriptor getImage() {
ImageDescriptor descriptor = null;
URL url = getFileLocation("icon");
if (url != null) {
descriptor = ImageDescriptor.createFromURL(url);
} else {
if (getAttribute("iconProvider") != null) {
try {
IconProvider provider = createExecutableExtension("iconProvider");
if (provider != null) {
descriptor = provider.getIcon();
}
} catch (CoreException e) {
}
}
}
return descriptor;
}
}
class LabelProvider extends StyledCellLabelProvider implements
ILabelProvider {
private final LocalResourceManager resourceManager = new LocalResourceManager(
JFaceResources.getResources());
@Override
public void dispose() {
resourceManager.dispose();
super.dispose();
}
@Override
public void update(ViewerCell cell) {
final StyledString text = new StyledString();
final ResultHandlerDescriptor element = (ResultHandlerDescriptor) cell
.getElement();
text.append(getText(element), new Styler() {
@Override
public void applyStyles(final TextStyle textStyle) {
textStyle.font = JFaceResources.getFontRegistry().getBold(
JFaceResources.DIALOG_FONT);
}
});
appendDescription(text, element);
cell.setText(text.toString());
cell.setStyleRanges(text.getStyleRanges());
cell.setImage(getImage(element));
super.update(cell);
}
private void appendDescription(StyledString text,
ResultHandlerDescriptor element) {
String desc = element.getAttribute("description");
if (desc != null) {
text.append("\n" + desc, StyledString.DECORATIONS_STYLER);
}
}
public Image getImage(Object element) {
ResultHandlerDescriptor config = (ResultHandlerDescriptor) element;
ImageDescriptor descriptor = config.getImage();
if (descriptor != null) {
return resourceManager.createImage(descriptor);
}
return null;
}
public String getText(Object element) {
return ((ResultHandlerDescriptor) element).getName();
}
}
class CollectJob extends Job {
private ISelection selection;
private final IEditorPart editorPart;
private final TargetDescriptor targetDesc;
private Shell shell;
private IWorkbench workbench;
public CollectJob(final ExecutionEvent event,
final TargetDescriptor currentTarget) {
super(Messages.CopyToHandler_JobName);
targetDesc = currentTarget;
selection = HandlerUtil.getActiveMenuSelection(event);
if (selection == null) {
selection = HandlerUtil.getCurrentSelection(event);
}
shell = HandlerUtil.getActiveShell(event);
workbench = HandlerUtil.getActiveWorkbenchWindow(event)
.getWorkbench();
editorPart = HandlerUtil.getActiveEditor(event);
}
@Override
protected IStatus run(final IProgressMonitor monitor) {
if (selection.isEmpty()) {
return Status.OK_STATUS;
}
final SubMonitor subMonitor = SubMonitor.convert(monitor,
Messages.CopyToHandler_CollectTask, 100);
final List<Copyable> items = new ArrayList<Copyable>();
if (selection instanceof IStructuredSelection) {
final IStructuredSelection ss = (IStructuredSelection) selection;
final Iterator<?> it = ss.iterator();
while (it.hasNext()) {
final Object item = it.next();
Copyable copyable = (Copyable) adapterManager.loadAdapter(
item, Copyable.class.getName());
if (copyable == null) {
copyable = getResourceCopyable(item);
}
if (copyable != null) {
items.add(copyable);
}
}
} else if (selection instanceof ITextSelection) {
final ITextSelection textSelection = (ITextSelection) selection;
Copyable copyable;
if (textSelection.getLength() > 0) {
copyable = new TextSelectionCopyable(textSelection);
} else {
copyable = (Copyable) adapterManager.loadAdapter(
editorPart, Copyable.class.getName());
}
if (null == copyable) {
final IEditorInput editorInput = editorPart
.getEditorInput();
copyable = getResourceCopyable(editorInput);
}
if (copyable != null) {
items.add(copyable);
}
}
Services.run(TargetService.class,
new SimpleServiceRunnable<TargetService>() {
@Override
protected void runWithService(TargetService service) {
service.setLastSelected(targetDesc.getId());
}
});
Target target = targetDesc.createTarget();
final Results results = target.transfer(subMonitor, items
.toArray(new Copyable[items.size()]));
if (!results.getFailures().isEmpty()) {
setProperty(IProgressConstants.KEEP_PROPERTY, true);
setProperty(
IProgressConstants.NO_IMMEDIATE_ERROR_PROMPT_PROPERTY,
true);
return new Status(IStatus.ERROR, FrameworkUtil.getBundle(
getClass()).getSymbolicName(), NLS.bind(
Messages.CopyToHandler_CopyError,
targetDesc.getLabel(), results.getFailures().size()));
}
if (results.getSuccesses().isEmpty()) {
return Status.CANCEL_STATUS;
}
IEvaluationContext context = new EvaluationContext(null, results);
context.addVariable("result.successes", results.getSuccesses());
context.addVariable("result.failures", results.getFailures());
context.addVariable("result.target", results.getTarget());
final Collection<ResultHandlerDescriptor> elements = ExtensionPoints
.visitAll(
"copyto.core.resultHandlers",
new ExpressionEvaluatingVisitor<ResultHandlerDescriptor>(
context, false,
new ExtensionVisitor<ResultHandlerDescriptor>() {
@Override
protected ResultHandlerDescriptor create(
IConfigurationElement configElement) {
return new ResultHandlerDescriptor(
configElement);
}
}));
if (elements.size() == 1) {
ResultHandlerDescriptor config = elements.iterator().next();
runAction(config, results);
} else {
WorkbenchJob job = new WorkbenchJob("Choose action") {
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
final ILabelProvider labelProvider = new LabelProvider();
ListDialog dialog = new ListDialog(shell) {
@Override
protected int getTableStyle() {
return SWT.NO_SCROLL | SWT.BORDER
| SWT.V_SCROLL;
}
@Override
protected Control createDialogArea(
Composite container) {
Control control = super
.createDialogArea(container);
Table table = getTableViewer().getTable();
table.addListener(SWT.MeasureItem,
new Listener() {
public void handleEvent(Event event) {
event.height = 28;
}
});
return control;
}
};
dialog.setTitle("Select action");
dialog.setMessage(NLS
.bind(
"Selection was copied successfully to \"{0}\".\nWhat do you want to do now?",
targetDesc.getLabel()));
dialog.setHelpAvailable(false);
dialog.setLabelProvider(labelProvider);
dialog.setContentProvider(ArrayContentProvider
.getInstance());
dialog.setInput(elements);
if (Window.OK == dialog.open()) {
Object[] dialogResults = dialog.getResult();
if (dialogResults != null) {
for (Object result : dialogResults) {
runAction((ResultHandlerDescriptor) result,
results);
}
}
}
return Status.OK_STATUS;
}
};
job.setSystem(true);
job.schedule();
}
return Status.OK_STATUS;
}
private void runAction(ResultHandlerDescriptor config,
final Results results) {
try {
final UIResultHandler handler = (UIResultHandler) config
.createExecutableExtension();
UIJob handlerJob = new UIJob(config.getName()) {
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
if (handler instanceof WorkbenchResultHandler) {
((WorkbenchResultHandler) handler).init(workbench);
}
handler.handleResults(results, shell);
return Status.OK_STATUS;
}
};
handlerJob.setSystem(true);
handlerJob.schedule();
} catch (CoreException e) {
}
}
}
/**
* If CTRL key hold down 1. First collect all Copyable 2. Group them by
* mime-type 3. Show wizard page for each mime-type (resolve vars before
* displaying page) 4. Upon "Finish click", send to server -> report
* progress
*/
public Object execute(final ExecutionEvent event) throws ExecutionException {
TargetDescriptor currentTarget = (TargetDescriptor) event
.getObjectParameterForExecution("id"); //$NON-NLS-1$
if (currentTarget == null) {
currentTarget = Services.run(TargetService.class,
new ServiceRunnable<TargetService, TargetDescriptor>() {
public TargetDescriptor run(final TargetService service) {
TargetDescriptor target = service.getLastSelected();
if (target == null && service.count() == 1) {
target = service.findFirst();
}
return target;
}
});
if (null == currentTarget) {
if (event.getTrigger() instanceof Event) {
final Event triggerEvent = (Event) event.getTrigger();
if (triggerEvent.widget instanceof ToolItem) {
triggerEvent.detail = 4; // Drop-down
final Rectangle point = ((ToolItem) triggerEvent.widget)
.getBounds();
triggerEvent.x = point.x;
triggerEvent.y = point.height;
triggerEvent.widget.notifyListeners(SWT.Selection,
triggerEvent);
return null;
}
}
// In all other cases show the preferences
CopyToPreferencePage.show(HandlerUtil.getActiveShell(event));
return null;
}
}
final Job job = new CollectJob(event, currentTarget);
job.schedule();
/*
Display.getDefault().asyncExec(new Runnable() {
public void run() {
runAction.run();
// TODO: If user chooses to cancel, we want to preserve
// the entry
// in the progress view:
// setProperty(IProgressConstants.KEEP_PROPERTY, true);
}
});
return new Status(IStatus.OK, HttpCopyToHandler.symbolicName,
NLS.bind("Selection copied successfully to {0}.",
handler.getHost()));
}
};
job.setProperty(IProgressConstants.ACTION_PROPERTY, runAction);
job.schedule();*/
return null;
}
@SuppressWarnings("unused")
private boolean showDialog(final Object trigger) {
if (trigger instanceof Event) {
final Event triggerEvent = (Event) trigger;
final int modifier = triggerEvent.stateMask & SWT.MODIFIER_MASK;
return ((modifier & SWT.CTRL) == SWT.CTRL);
}
return false;
}
@SuppressWarnings("rawtypes")
public void updateElement(final UIElement element, final Map parameters) {
Services.run(TargetService.class,
new SimpleServiceRunnable<TargetService>() {
@Override
public void runWithService(final TargetService service) {
// First display the last selected item as tooltip
String text = Messages.CopyToHandler_DropDown_Tooltip_SelectTarget;
TargetDescriptor target = service.getLastSelected();
// If there is no such item, see how many items there
// are
if (target == null) {
final int count = service.count();
// If there is none yet, we set a tooltip that will
// aid the user
if (count == 0) {
text = Messages.CopyToHandler_DropDown_Tooltip_Configure;
} else if (count == 1) {
target = service.findFirst();
}
}
if (target != null) {
text = NLS
.bind(
Messages.CopyToHandler_DropDown_Tooltip_CopyTo,
target.getLabel());
}
final String finalText = text;
Display.getDefault().asyncExec(new Runnable() {
public void run() {
element.setTooltip(finalText);
}
});
}
});
}
/**
* Tries to adapt the item first to IResource and then to Copyable.
*
* @param item
* @return
*/
private Copyable getResourceCopyable(final Object item) {
final IResource resource = (IResource) adapterManager.loadAdapter(item,
IResource.class.getName());
if (resource != null) {
return (Copyable) adapterManager.loadAdapter(resource,
Copyable.class.getName());
}
return null;
}
}