/*******************************************************************************
* Copyright (c) 2013 Pivotal Software, Inc.
* 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:
* Pivotal Software, Inc. - initial API and implementation
*******************************************************************************/
package org.springsource.ide.eclipse.commons.ui.launch;
import java.util.ArrayList;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationListener;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.ActionContributionItem;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkbenchWindowPulldownDelegate;
import org.eclipse.ui.actions.CompoundContributionItem;
import org.springsource.ide.eclipse.commons.frameworks.core.ExceptionUtil;
import org.springsource.ide.eclipse.commons.internal.core.CorePlugin;
import org.springsource.ide.eclipse.commons.ui.launch.LaunchList.Item;
/**
* Abstract superclass for an launch toolbar button with pulldown
* that applies some operation to launchConfiguration from some historic list of
* launch configs.
*
* @author Kris De Volder
*/
public abstract class AbstractLaunchToolbarPulldown implements IWorkbenchWindowPulldownDelegate, ILaunchConfigurationListener {
private IWorkbenchWindow window;
private Menu menu;
private LaunchList.Listener launchListener;
private LaunchList launches = createList().addListener(launchListener = new LaunchList.Listener() {
public void changed() {
if (action!=null && window!=null) {
uiUpdate();
}
}
});
private IAction action;
public AbstractLaunchToolbarPulldown() {
DebugPlugin.getDefault().getLaunchManager().addLaunchConfigurationListener(this);
}
/**
* FActory method to create or obtain an instance that keeps track of the launches to be
* shown in the pulldown menu.
*/
protected abstract LaunchList createList();
@Override
public void run(IAction action) {
this.action = action;
final LaunchList.Item l = launches.getLast();
if (l!=null) {
Job job = new Job(getOperationName()) {
@Override
protected IStatus run(IProgressMonitor monitor) {
try {
performOperation(l);
return Status.OK_STATUS;
} catch (Exception e) {
return ExceptionUtil.status(e);
}
}
};
job.schedule();
} else {
MessageDialog.openError(window.getShell(), "No Processes Found",
"Couldn't "+getOperationName()+": no active processes"
);
}
}
protected abstract String getOperationName();
protected abstract void performOperation(LaunchList.Item l) throws DebugException;
@Override
public void selectionChanged(IAction action, ISelection selection) {
this.action = action;
update();
}
/**
* Update labels etc. This method must only be called from the UI thread.
*/
private void update() {
if (action!=null) {
Item launch = launches.getLast();
String label = getOperationName();
if (launch!=null) {
label = label + " " + launch.getName();
}
action.setText(label);
action.setToolTipText(label);
action.setEnabled(launch!=null);
}
}
/**
* Update labels etc. This method may be called from a non-ui thread.
*/
private void uiUpdate() {
Shell shell = window.getShell();
if (shell!=null) {
//We may not be in the UIThread here. So take care before futzing with the widgets!
Display display = shell.getDisplay();
if (display!=null) {
display.asyncExec(new Runnable() {
public void run() {
update();
}
});
}
}
}
@Override
public void dispose() {
if (menu!=null) {
menu.dispose();
menu = null;
}
if (launches!=null && launchListener!=null) {
launches.removeListener(launchListener);
launches = null;
}
DebugPlugin.getDefault().getLaunchManager().removeLaunchConfigurationListener(this);
}
@Override
public void init(IWorkbenchWindow window) {
this.window = window;
update();
}
@Override
public Menu getMenu(Control parent) {
if (menu!=null) {
menu.dispose();
}
menu = new Menu(parent);
fillMenu();
return menu;
}
private void fillMenu() {
new SubMenuProvider(launches).fill(menu, -1);
}
private static final IContributionItem EMPTY_ITEM = new ActionContributionItem(new EmptyAction("No active processes"));
/**
* An action that is disabled and does nothing. Its only purpose is to
* show text when there is nothing else to show in an otherwise empty menu.
*/
private static class EmptyAction extends Action {
public EmptyAction(String label) {
super(label);
}
@Override
public boolean isEnabled() {
return false;
}
}
/**
* Dynamically creates menus to relaunch currently active launches.
*/
private class SubMenuProvider extends CompoundContributionItem {
private final LaunchList launches;
public SubMenuProvider(LaunchList launches) {
this.launches = launches;
}
private class RelaunchAction extends Action {
private final Item launch;
public RelaunchAction(LaunchList.Item launch) {
this.launch = launch;
this.setText(launch.getName());
}
@Override
public void run() {
try {
performOperation(launch);
} catch (DebugException e) {
CorePlugin.log(e);
}
}
}
@Override
protected IContributionItem[] getContributionItems() {
return createContributionItems();
}
private IContributionItem[] createContributionItems() {
ArrayList<IContributionItem> items = new ArrayList<IContributionItem>();
for (Item launch : launches.getLaunches()) {
items.add(new ActionContributionItem(new RelaunchAction(launch)));
}
if (items.isEmpty()) {
items.add(EMPTY_ITEM);
}
// Return item in reverse order (so older item at the bottom of the menu).
IContributionItem[] array = new IContributionItem[items.size()];
for (int i = 0; i < array.length; i++) {
array[array.length-i-1] = items.get(i);
}
return array;
}
}
@Override
public void launchConfigurationAdded(ILaunchConfiguration configuration) {
uiUpdate(); //force update tooltip on button in case label changed on acount of the
// config it launches got renamed or deleted
}
@Override
public void launchConfigurationChanged(ILaunchConfiguration configuration) {
}
@Override
public void launchConfigurationRemoved(ILaunchConfiguration configuration) {
uiUpdate(); //force update tooltip on button in case labe changed on acount of the
// config it launches got renamed or deleted
}
}