blob: 783599a9639f3999df61a1f97a191db7b53a1bfe [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2018 QNX Software Systems and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* QNX Software Systems - initial API and implementation
* Freescale Semiconductor
* SSI Schaefer
* IBM Corporation - Bug 517809
*******************************************************************************/
package org.eclipse.debug.internal.ui.groups;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.internal.core.DebugCoreMessages;
import org.eclipse.debug.internal.core.groups.GroupLaunchConfigurationDelegate;
import org.eclipse.debug.internal.core.groups.GroupLaunchElement;
import org.eclipse.debug.internal.core.groups.GroupLaunchElement.GroupElementPostLaunchAction;
import org.eclipse.debug.internal.ui.DebugPluginImages;
import org.eclipse.debug.internal.ui.DebugUIMessages;
import org.eclipse.debug.internal.ui.DebugUIPlugin;
import org.eclipse.debug.internal.ui.IInternalDebugUIConstants;
import org.eclipse.debug.internal.ui.SWTFactory;
import org.eclipse.debug.internal.ui.launchConfigurations.LaunchConfigurationManager;
import org.eclipse.debug.internal.ui.launchConfigurations.LaunchHistory;
import org.eclipse.debug.ui.AbstractLaunchConfigurationTab;
import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.debug.ui.ILaunchConfigurationDialog;
import org.eclipse.debug.ui.ILaunchConfigurationTab;
import org.eclipse.debug.ui.ILaunchGroup;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.BaseLabelProvider;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ICheckStateProvider;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.window.Window;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.activities.WorkbenchActivityHelper;
/**
* Tab group for Launch Group.
*
* @since 3.12
*/
public class GroupLaunchConfigurationTabGroup extends AbstractLaunchConfigurationTabGroup {
static class ContentProvider implements IStructuredContentProvider, ITreeContentProvider {
protected List<GroupLaunchElement> input;
@Override
public Object[] getElements(Object inputElement) {
return getChildren(inputElement);
}
@Override
public void dispose() {
input = null;
}
@Override
@SuppressWarnings("unchecked") // nothing we can do about this
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
if (newInput instanceof List<?>) {
input = (List<GroupLaunchElement>) newInput;
}
}
@Override
public Object[] getChildren(Object parentElement) {
return (parentElement == input) ? input.toArray() : null;
}
@Override
public Object getParent(Object element) {
return (element == input) ? null : input;
}
@Override
public boolean hasChildren(Object element) {
return (element == input) ? (input.size() > 0) : false;
}
}
static class LabelProvider extends BaseLabelProvider implements ITableLabelProvider {
@Override
public Image getColumnImage(Object element, int columnIndex) {
if (!(element instanceof GroupLaunchElement)) {
return null;
}
if (columnIndex == 0) {
GroupLaunchElement el = (GroupLaunchElement) element;
if (el.data == null || !isValidLaunchReference(el.data)) {
Image errorImage = PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJS_ERROR_TSK);
return errorImage;
}
try {
String key = el.data.getType().getIdentifier();
return DebugPluginImages.getImage(key);
} catch (CoreException e) {
Image errorImage = PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJS_ERROR_TSK);
return errorImage;
}
}
return null;
}
@Override
public String getColumnText(Object element, int columnIndex) {
if (!(element instanceof GroupLaunchElement)) {
return null;
}
GroupLaunchElement el = (GroupLaunchElement) element;
// launch name
if (columnIndex == 0) {
try {
return (el.data != null) ? el.data.getType().getName() + "::" + el.name : el.name; //$NON-NLS-1$
} catch (CoreException e) {
return el.name;
}
}
// launch mode
if (columnIndex == 1) {
return getLabel(el.mode) + (el.adoptIfRunning ? DebugUIMessages.GroupLaunchConfigurationTabGroup_lblAdopt : ""); //$NON-NLS-1$
}
// launch post action
if (columnIndex == 2) {
GroupElementPostLaunchAction action = el.action;
switch (action) {
case NONE:
return ""; //$NON-NLS-1$
case WAIT_FOR_TERMINATION:
return action.getDescription();
case DELAY:
final Object actionParam = el.actionParam;
return NLS.bind(DebugUIMessages.GroupLaunchConfigurationTabGroup_13, actionParam instanceof Integer ? Integer.toString((Integer) actionParam) : "?"); //$NON-NLS-1$
case OUTPUT_REGEXP:
return NLS.bind(DebugUIMessages.GroupLaunchConfigurationTabGroup_0, el.actionParam);
default:
assert false : "new post launch action missing logic here"; //$NON-NLS-1$
return ""; //$NON-NLS-1$
}
}
return null;
}
}
static class CheckStateProvider implements ICheckStateProvider {
@Override
public boolean isChecked(Object element) {
if (element instanceof GroupLaunchElement) {
return ((GroupLaunchElement)element).enabled;
}
return false;
}
@Override
public boolean isGrayed(Object element) {
return false;
}
}
static abstract class ButtonComposite extends Composite implements SelectionListener {
Button upButton;
Button downButton;
Button addButton;
Button deleteButton;
Button editButton;
public ButtonComposite(Composite parent, int style) {
super(parent, style);
setLayout(new GridLayout());
upButton = createPushButton(this, DebugUIMessages.GroupLaunchConfigurationTabGroup_1);
downButton = createPushButton(this, DebugUIMessages.GroupLaunchConfigurationTabGroup_2);
editButton = createPushButton(this, DebugUIMessages.GroupLaunchConfigurationTabGroup_3);
addButton = createPushButton(this, DebugUIMessages.GroupLaunchConfigurationTabGroup_4);
deleteButton = createPushButton(this, DebugUIMessages.GroupLaunchConfigurationTabGroup_5);
}
protected abstract void updateWidgetEnablement();
/**
* Helper method to create a push button.
*
* @param parent
* the parent control
* @param key
* the resource name used to supply the button's label text
* @return Button
*/
protected Button createPushButton(Composite parent, String key) {
Button button = SWTFactory.createPushButton(parent, key, null);
button.addSelectionListener(this);
return button;
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {
// nothing
}
@Override
public void widgetSelected(SelectionEvent e) {
Widget widget = e.widget;
if (widget == upButton) {
upPressed();
} else if (widget == downButton) {
downPressed();
} else if (widget == addButton) {
addPressed();
} else if (widget == deleteButton) {
deletePressed();
} else if (widget == editButton) {
editPressed();
}
}
protected abstract void addPressed();
protected abstract void editPressed();
protected abstract void deletePressed();
protected abstract void downPressed();
protected abstract void upPressed();
}
static class GroupLaunchTab extends AbstractLaunchConfigurationTab {
protected CheckboxTreeViewer treeViewer;
protected List<GroupLaunchElement> input = new ArrayList<>();
/**
* copy of the initial state of the configuration used for cycle
* checking. This is not updated when the user changes settings!
*/
private ILaunchConfiguration self;
@Override
public void createControl(Composite parent) {
Composite comp = new Composite(parent, SWT.NONE);
setControl(comp);
//comp.setBackground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_DARK_GREEN));
comp.setLayout(new GridLayout(2, false));
treeViewer = new CheckboxTreeViewer(comp, SWT.BORDER | SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL | SWT.FULL_SELECTION);
Tree table = treeViewer.getTree();
table.setFont(parent.getFont());
treeViewer.setContentProvider(new ContentProvider());
treeViewer.setLabelProvider(new LabelProvider());
treeViewer.setCheckStateProvider(new CheckStateProvider());
table.setHeaderVisible(true);
table.setLayoutData(new GridData(GridData.FILL_BOTH));
TreeColumn col1 = new TreeColumn(table, SWT.NONE);
col1.setText(DebugUIMessages.GroupLaunchConfigurationTabGroup_6);
col1.setWidth(300);
TreeColumn col2 = new TreeColumn(table, SWT.NONE);
col2.setText(DebugUIMessages.GroupLaunchConfigurationTabGroup_7);
col2.setWidth(100);
TreeColumn col3 = new TreeColumn(table, SWT.NONE);
col3.setText(DebugUIMessages.GroupLaunchConfigurationTabGroup_12);
col3.setWidth(100);
treeViewer.setInput(input);
final ButtonComposite buts = new ButtonComposite(comp, SWT.NONE) {
@Override
protected void addPressed() {
GroupLaunchConfigurationSelectionDialog dialog =
GroupLaunchConfigurationSelectionDialog.createDialog(
treeViewer.getControl().getShell(), GroupLaunchElement.MODE_INHERIT, false, self);
if (dialog.open() == Window.OK) {
ILaunchConfiguration[] configs = dialog.getSelectedLaunchConfigurations();
if (configs.length < 1) {
return;
}
for (ILaunchConfiguration config : configs) {
GroupLaunchElement el = new GroupLaunchElement();
input.add(el);
el.index = input.size() - 1;
el.enabled = true;
applyFromDialog(el, dialog, config);
treeViewer.refresh(true);
treeViewer.setChecked(el, el.enabled);
}
updateWidgetEnablement();
updateLaunchConfigurationDialog();
}
}
@Override
protected void updateWidgetEnablement(){
downButton.setEnabled(isDownEnabled());
upButton.setEnabled(isUpEnabled());
int selectionCount = getSelectionCount();
editButton.setEnabled(selectionCount == 1);
deleteButton.setEnabled(selectionCount > 0);
}
@Override
protected void editPressed() {
int index = getSingleSelectionIndex();
if (index < 0) {
return;
}
GroupLaunchElement el = input.get(index);
GroupLaunchConfigurationSelectionDialog dialog =
GroupLaunchConfigurationSelectionDialog.createDialog(
treeViewer.getControl().getShell(), el.mode, true, self);
if (isValidLaunchReference(el.data)) {
dialog.setInitialSelection(el);
}
if (dialog.open() == Window.OK) {
ILaunchConfiguration[] confs = dialog.getSelectedLaunchConfigurations();
if (confs.length < 0) {
return;
}
assert confs.length == 1 : "invocation of the dialog for editing an entry sholdn't allow OK to be hit if the user chooses multiple launch configs in the dialog"; //$NON-NLS-1$
applyFromDialog(el, dialog, confs[0]);
treeViewer.refresh(true);
updateWidgetEnablement();
updateLaunchConfigurationDialog();
}
}
private void applyFromDialog(GroupLaunchElement el, GroupLaunchConfigurationSelectionDialog dialog, ILaunchConfiguration config) {
el.name = config.getName();
el.data = config;
el.mode = dialog.getMode();
el.action = dialog.getAction();
el.adoptIfRunning = dialog.getAdoptIfRunning();
el.actionParam = dialog.getActionParam();
}
@Override
protected void deletePressed() {
int[] indices = getMultiSelectionIndices();
if (indices.length < 1) {
return;
}
// need to delete from high to low
for (int i = indices.length - 1; i >= 0; i--) {
input.remove(indices[i]);
}
treeViewer.refresh(true);
updateWidgetEnablement();
updateLaunchConfigurationDialog();
}
/**
* @return the index of the selection if a single item is
* selected. If zero or multiple are selected, -1 is
* returned
*/
private int getSingleSelectionIndex() {
StructuredSelection sel = (StructuredSelection) treeViewer.getSelection();
if (sel.size() != 1) {
return -1;
}
GroupLaunchElement el = ((GroupLaunchElement) sel
.getFirstElement());
return input.indexOf(el);
}
/**
* @return the indices of one or more selected items. Indices
* are always returned in ascending order
*/
private int[] getMultiSelectionIndices() {
StructuredSelection sel = (StructuredSelection) treeViewer.getSelection();
List<Integer> indices = new ArrayList<>();
for (Iterator<?> iter = sel.iterator(); iter.hasNext(); ) {
GroupLaunchElement el = (GroupLaunchElement) iter.next();
indices.add(input.indexOf(el));
}
int[] result = new int[indices.size()];
for (int i = 0; i < result.length; i++) {
result[i] = indices.get(i);
}
return result;
}
private int getSelectionCount() {
return ((StructuredSelection)treeViewer.getSelection()).size();
}
@Override
protected void downPressed() {
if (!isDownEnabled()) {
return;
}
int index = getSingleSelectionIndex();
GroupLaunchElement x = input.get(index);
input.set(index, input.get(index + 1));
input.set(index + 1, x);
treeViewer.refresh(true);
updateWidgetEnablement();
updateLaunchConfigurationDialog();
}
protected boolean isDownEnabled() {
final int index = getSingleSelectionIndex();
return (index >= 0) && (index != input.size() - 1);
}
protected boolean isUpEnabled(){
return getSingleSelectionIndex() > 0;
}
@Override
protected void upPressed() {
if (!isUpEnabled()) {
return;
}
int index = getSingleSelectionIndex();
GroupLaunchElement x = input.get(index);
input.set(index, input.get(index - 1));
input.set(index - 1, x);
treeViewer.refresh(true);
updateWidgetEnablement();
updateLaunchConfigurationDialog();
}
};
treeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(SelectionChangedEvent event) {
buts.updateWidgetEnablement();
}
});
treeViewer.getTree().addSelectionListener(new SelectionAdapter(){
@Override
public void widgetDefaultSelected(SelectionEvent e) {
buts.editPressed();
}
});
treeViewer.addCheckStateListener(new ICheckStateListener(){
@Override
public void checkStateChanged(CheckStateChangedEvent event) {
((GroupLaunchElement)event.getElement()).enabled = event.getChecked();
updateLaunchConfigurationDialog();
}
});
buts.updateWidgetEnablement();
GridData layoutData = new GridData(GridData.GRAB_VERTICAL);
layoutData.verticalAlignment = SWT.BEGINNING;
buts.setLayoutData(layoutData);
}
@Override
public String getName() {
return DebugUIMessages.GroupLaunchConfigurationTabGroup_10;
}
@Override
public Image getImage() {
return DebugUITools.getImage(IInternalDebugUIConstants.IMG_OBJS_LAUNCH_GROUP);
}
@Override
public void initializeFrom(ILaunchConfiguration configuration) {
try {
self = configuration.copy(configuration.getName());
} catch (CoreException e) {
DebugPlugin.log(e);
}
// replace the input from previously shown launch configurations
input = GroupLaunchConfigurationDelegate.createLaunchElements(configuration);
if (treeViewer != null) {
treeViewer.setInput(input);
}
}
@Override
public void performApply(ILaunchConfigurationWorkingCopy configuration) {
GroupLaunchConfigurationDelegate.storeLaunchElements(configuration, input);
}
@Override
public void setDefaults(ILaunchConfigurationWorkingCopy configuration) {
// defaults is empty list
}
@Override
public boolean isValid(ILaunchConfiguration launchConfig) {
setMessage(null);
setErrorMessage(null);
int validLaunches = 0;
// test if each launch is valid
for (GroupLaunchElement element : input) {
if (element.enabled) {
if ( element.data == null) {
// error referencing invalid launch
setErrorMessage(MessageFormat.format(DebugUIMessages.GroupLaunchConfigurationTabGroup_14,
element.name));
return false;
} else if (!isValidLaunchReference(element.data)) {
// error referencing invalid launch
setErrorMessage(MessageFormat.format(DebugUIMessages.GroupLaunchConfigurationTabGroup_15,
element.name));
return false;
}
validLaunches++;
}
}
if (validLaunches < 1) {
// must have at least one valid and enabled launch
setErrorMessage(DebugUIMessages.GroupLaunchConfigurationTabGroup_16);
return false;
}
return true;
}
}
public GroupLaunchConfigurationTabGroup() {
// nothing
}
/**
* Test if a launch configuration is a valid reference.
*
* @param config configuration reference
* @return <code>true</code> if it is a valid reference, <code>false</code>
* if launch configuration should be filtered
*/
public static boolean isValidLaunchReference(ILaunchConfiguration config) {
if (config == null) {
return false;
}
return DebugUIPlugin.doLaunchConfigurationFiltering(config) && !WorkbenchActivityHelper.filterItem(config);
}
@Override
public void createTabs(ILaunchConfigurationDialog dialog, String mode) {
ILaunchConfigurationTab[] tabs = new ILaunchConfigurationTab[] {//
new GroupLaunchTab(), //
new CommonTabLite() //
};
setTabs(tabs);
}
public static Map<String, ILaunchGroup> getModes() {
if (modes == null) {
initializeModes();
}
return modes;
}
private static Map<String, ILaunchGroup> modes;
/**
* Required to satisfy the tree in mode inherit.
*/
private static final class InheritModeGroup implements ILaunchGroup {
@Override
public ImageDescriptor getImageDescriptor() {
return null;
}
@Override
public ImageDescriptor getBannerImageDescriptor() {
return null;
}
@Override
public String getLabel() {
return DebugCoreMessages.GroupLaunchElement_inherit_launch_mode_label;
}
@Override
public String getIdentifier() {
return null;
}
@Override
public String getCategory() {
return null;
}
@Override
public String getMode() {
return null;
}
@Override
public boolean isPublic() {
return false;
}
}
private static synchronized void initializeModes() {
if (modes != null) {
return;
}
modes = new LinkedHashMap<>();
modes.put(GroupLaunchElement.MODE_INHERIT, new InheritModeGroup());
Set<ILaunchGroup> sortedGroups = new TreeSet<>((a, b) -> {
return a.getLabel().compareTo(b.getLabel());
});
LaunchConfigurationManager mgr = DebugUIPlugin.getDefault().getLaunchConfigurationManager();
sortedGroups.addAll(Arrays.asList(mgr.getLaunchGroups()));
for (ILaunchGroup launchGroup : sortedGroups) {
LaunchHistory history = mgr.getLaunchHistory(launchGroup.getIdentifier());
if (history == null) {
// mode currently not supported.
continue;
}
String modeName = launchGroup.getMode();
if (!modes.containsKey(modeName)) {
modes.put(modeName, launchGroup);
}
}
}
public static String getLabel(String mode) {
if (mode == null || mode.isEmpty())
{
return ""; //$NON-NLS-1$
}
if (modes == null) {
initializeModes();
}
ILaunchGroup launchGrp = modes.get(mode);
return DebugUIPlugin.removeAccelerators(launchGrp.getLabel());
}
}