diff options
author | Markus Duft | 2016-05-11 13:52:28 +0000 |
---|---|---|
committer | Markus Duft | 2016-11-30 09:42:25 +0000 |
commit | 97be8772a897b47a48b2d4b6ef33b46811408b7f (patch) | |
tree | ef2793a75f11741be425b7d236695df5f0f252d2 /org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui | |
parent | 66da2950cc347953d38ae225f82b5f23f5e960e7 (diff) | |
download | eclipse.platform.debug-97be8772a897b47a48b2d4b6ef33b46811408b7f.tar.gz eclipse.platform.debug-97be8772a897b47a48b2d4b6ef33b46811408b7f.tar.xz eclipse.platform.debug-97be8772a897b47a48b2d4b6ef33b46811408b7f.zip |
Initial contribution of Launch Groups
The initial contribution is a slightly modified/polished version of the
launch group code in org.eclipse.cdt.launch.
A follow up commit will contain additional modifications to merge
features from another (external) implementation of launch groups.
Bug: 492788
Change-Id: Ie0a902b857a5c2cc02f5c36d8591001493bd6e19
Signed-off-by: Markus Duft <markus.duft@ssi-schaefer.com>
Diffstat (limited to 'org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui')
10 files changed, 1963 insertions, 0 deletions
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/DebugUIMessages.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/DebugUIMessages.java index be895c0d4..93095e320 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/DebugUIMessages.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/DebugUIMessages.java @@ -237,6 +237,37 @@ public class DebugUIMessages extends NLS { public static String GoToAddressComposite_7; // + // Launch Groups + // + public static String GroupLaunch_Cannot_launch; + public static String GroupLaunch_Cycle; + public static String GroupLaunch_Error; + public static String GroupLaunchConfigurationSelectionDialog_10; + public static String GroupLaunchConfigurationSelectionDialog_11; + public static String GroupLaunchConfigurationSelectionDialog_12; + public static String GroupLaunchConfigurationSelectionDialog_13; + public static String GroupLaunchConfigurationSelectionDialog_14; + public static String GroupLaunchConfigurationSelectionDialog_15; + public static String GroupLaunchConfigurationSelectionDialog_4; + public static String GroupLaunchConfigurationSelectionDialog_5; + public static String GroupLaunchConfigurationSelectionDialog_7; + public static String GroupLaunchConfigurationSelectionDialog_8; + public static String GroupLaunchConfigurationSelectionDialog_9; + public static String GroupLaunchConfigurationTabGroup_1; + public static String GroupLaunchConfigurationTabGroup_10; + public static String GroupLaunchConfigurationTabGroup_12; + public static String GroupLaunchConfigurationTabGroup_13; + public static String GroupLaunchConfigurationTabGroup_14; + public static String GroupLaunchConfigurationTabGroup_15; + public static String GroupLaunchConfigurationTabGroup_16; + public static String GroupLaunchConfigurationTabGroup_2; + public static String GroupLaunchConfigurationTabGroup_3; + public static String GroupLaunchConfigurationTabGroup_4; + public static String GroupLaunchConfigurationTabGroup_5; + public static String GroupLaunchConfigurationTabGroup_6; + public static String GroupLaunchConfigurationTabGroup_7; + + // // Blocks // public static String WorkingDirectoryBlock_4; diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/DebugUIMessages.properties b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/DebugUIMessages.properties index 8cc9fc674..04df7fa8d 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/DebugUIMessages.properties +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/DebugUIMessages.properties @@ -109,6 +109,38 @@ MonitorMemoryBlockDialog_MonitorMemory = Monitor Memory MonitorMemoryBlockDialog_NumberOfBytes = Number of bytes: GoToAddressDialog_Address = Address: GoToAddressDialog_GoToAddress = Go To Address... + +############################################################## +# Launch Groups +############################################################## +GroupLaunch_Cannot_launch=Cannot launch ''{0}'' in the ''{1}'' mode +GroupLaunch_Cycle=Infinite loop detected for ''{0}'' group +GroupLaunch_Error=Error +GroupLaunchConfigurationSelectionDialog_4=Launch Mode: +GroupLaunchConfigurationSelectionDialog_5=Use default mode when launching +GroupLaunchConfigurationSelectionDialog_7=Select a launch configuration +GroupLaunchConfigurationSelectionDialog_8=Post launch action: +GroupLaunchConfigurationSelectionDialog_9=Seconds: +GroupLaunchConfigurationSelectionDialog_10=Enter valid number of seconds +GroupLaunchConfigurationSelectionDialog_11=Select only one launch configuration +GroupLaunchConfigurationSelectionDialog_12=Add Launch Configuration +GroupLaunchConfigurationSelectionDialog_13=Edit Launch Configuration +GroupLaunchConfigurationSelectionDialog_14=Add one or more launch configurations to the launch group +GroupLaunchConfigurationSelectionDialog_15=Edit an existing entry in the launch group +GroupLaunchConfigurationTabGroup_1=Up +GroupLaunchConfigurationTabGroup_2=Down +GroupLaunchConfigurationTabGroup_3=Edit... +GroupLaunchConfigurationTabGroup_4=Add... +GroupLaunchConfigurationTabGroup_5=Remove +GroupLaunchConfigurationTabGroup_6=Name +GroupLaunchConfigurationTabGroup_7=Mode +GroupLaunchConfigurationTabGroup_10=Launches +GroupLaunchConfigurationTabGroup_12=Action +GroupLaunchConfigurationTabGroup_13=Delay {0} seconds +GroupLaunchConfigurationTabGroup_14=Launch {0} does not exist. +GroupLaunchConfigurationTabGroup_15=Launch {0} is filtered. +GroupLaunchConfigurationTabGroup_16=Must have at least one valid enabled launch. + CodePagesPrefDialog_1=Select Codepages CodePagesPrefDialog_2=Memory to ASCII strings: CodePagesPrefDialog_4=Memory to EBCDIC strings: diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/ComboControlledStackComposite.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/ComboControlledStackComposite.java new file mode 100644 index 000000000..fed29d2dc --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/ComboControlledStackComposite.java @@ -0,0 +1,138 @@ +/******************************************************************************* + * Copyright (c) 2009, 2016 QNX Software Systems 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: + * QNX Software Systems - initial API and implementation + * Freescale Semiconductor + *******************************************************************************/ +package org.eclipse.debug.internal.ui.groups; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StackLayout; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; + +/** + * Stack Composite - Switch between panes controlled by combo box + * <p> + * Copied from CDT (org.eclipse.cdt.launch) + */ +class ComboControlledStackComposite extends Composite { + private Composite fArea; + private Combo fCombo; + private Map<String, Composite> tabMap; // label ==> tab + private StackLayout layout; + private Label fLabel; + + public ComboControlledStackComposite(Composite parent, int style) { + super(parent, style); + tabMap = new LinkedHashMap<String, Composite>(); + setLayout(new GridLayout(2, false)); + createContents(this); + } + + public void setLabelText(String label) { + fLabel.setText(label); + } + public void addItem(String label, Composite tab) { + tabMap.put(label, tab); + fCombo.add(label); + if (layout.topControl==null) { + layout.topControl = tab; + fCombo.setText(label); + } + } + + public void deleteItem(String label) { + if (fCombo.getText().equals(label)) { + setSelection(fCombo.getItem(0)); + } + Composite tab = tabMap.get(label); + if (tab != null) { + tab.dispose(); + tabMap.remove(label); + } + } + + public void setSelection(String label) { + fCombo.setText(label); + setPage(label); + } + + protected void createContents(Composite parent) { + fLabel = createLabel(this); + fCombo = createCombo(this); + GridData cgd = new GridData(GridData.FILL_HORIZONTAL); + + fCombo.setLayoutData(cgd); + fArea = createTabArea(this); + GridData agd = new GridData(GridData.FILL_BOTH); + agd.horizontalSpan = 2; + fArea.setLayoutData(agd); + } + + + public Composite getStackParent() { + return fArea; + } + + public Label getLabel() { + return fLabel; + } + + public Combo getCombo() { + return fCombo; + } + + protected Composite createTabArea(Composite parent) { + Composite comp = new Composite(parent, SWT.NONE); + layout = new StackLayout(); + comp.setLayout(layout); + + return comp; + } + + + protected Label createLabel(Composite parent) { + Label label = new Label(parent, SWT.WRAP); + return label; + } + + protected Combo createCombo(Composite parent) { + Combo box = new Combo(parent, SWT.READ_ONLY); + box.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + String name = fCombo.getText(); + comboSelected(name); + } + }); + return box; + } + + protected void comboSelected(String label) { + setPage(label); + } + + protected void setPage(String label) { + layout.topControl = tabMap.get(label); + getStackParent().layout(); + } + + public Control getTopControl() { + return layout != null ? layout.topControl : null; + } +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/CommonTabLite.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/CommonTabLite.java new file mode 100644 index 000000000..4261844f4 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/CommonTabLite.java @@ -0,0 +1,603 @@ +/******************************************************************************* + * Copyright (c) 2000, 2012, 2016 IBM Corporation 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: + * IBM Corporation - initial API and implementation + * Freescale Semiconductor (stripped out functionality from platform debug version) + *******************************************************************************/ +package org.eclipse.debug.internal.ui.groups; + + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.debug.internal.core.IInternalDebugCoreConstants; +import org.eclipse.debug.internal.ui.DebugUIPlugin; +import org.eclipse.debug.internal.ui.IDebugHelpContextIds; +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.LaunchConfigurationsMessages; +import org.eclipse.debug.internal.ui.launchConfigurations.LaunchGroupExtension; +import org.eclipse.debug.internal.ui.launchConfigurations.LaunchHistory; +import org.eclipse.debug.ui.AbstractLaunchConfigurationTab; +import org.eclipse.debug.ui.DebugUITools; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.debug.ui.ILaunchGroup; +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.CheckStateChangedEvent; +import org.eclipse.jface.viewers.CheckboxTableViewer; +import org.eclipse.jface.viewers.ICheckStateListener; +import org.eclipse.jface.viewers.ILabelProviderListener; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.ITableLabelProvider; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.accessibility.AccessibleAdapter; +import org.eclipse.swt.accessibility.AccessibleEvent; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +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.Control; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.dialogs.ContainerSelectionDialog; + +/** + * This class was taken from org.eclipse.debug.ui. We expose a Common tab for + * Multilaunch that has only a subset of the standard tab's properties. + * + * Launch configuration tab used to specify the location a launch configuration + * is stored in, whether it should appear in the favorites list, and perspective + * switching behavior for an associated launch. + * <p> + * Clients may instantiate this class. + * <p> + * Copied from CDT (org.eclipse.cdt.launch) + */ +class CommonTabLite extends AbstractLaunchConfigurationTab { + + /** + * Provides a persistible dialog for selecting the shared project location + * @since 3.2 + */ + class SharedLocationSelectionDialog extends ContainerSelectionDialog { + private final String SETTINGS_ID = IDebugUIConstants.PLUGIN_ID + ".SHARED_LAUNCH_CONFIGURATON_DIALOG"; //$NON-NLS-1$ + + public SharedLocationSelectionDialog(Shell parentShell, IContainer initialRoot, boolean allowNewContainerName, String message) { + super(parentShell, initialRoot, allowNewContainerName, message); + } + + @Override + protected IDialogSettings getDialogBoundsSettings() { + IDialogSettings settings = DebugUIPlugin.getDefault().getDialogSettings(); + IDialogSettings section = settings.getSection(SETTINGS_ID); + if (section == null) { + section = settings.addNewSection(SETTINGS_ID); + } + return section; + } + } + + /** + * This attribute exists solely for the purpose of making sure that invalid shared locations + * can be revertible. This attribute is not saveable and will never appear in a saved + * launch configuration. + * @since 3.3 + */ + private static final String BAD_CONTAINER = "bad_container_name"; //$NON-NLS-1$ + + // Local/shared UI widgets + private Button fLocalRadioButton; + private Button fSharedRadioButton; + private Text fSharedLocationText; + private Button fSharedLocationButton; + + /** + * Check box list for specifying favorites + */ + private CheckboxTableViewer fFavoritesTable; + + /** + * Modify listener that simply updates the owning launch configuration dialog. + */ + private ModifyListener fBasicModifyListener = new ModifyListener() { + @Override + public void modifyText(ModifyEvent evt) { + updateLaunchConfigurationDialog(); + } + }; + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#createControl(org.eclipse.swt.widgets.Composite) + */ + @Override + public void createControl(Composite parent) { + Composite comp = new Composite(parent, SWT.NONE); + setControl(comp); + PlatformUI.getWorkbench().getHelpSystem().setHelp(getControl(), IDebugHelpContextIds.LAUNCH_CONFIGURATION_DIALOG_COMMON_TAB); + comp.setLayout(new GridLayout(2, true)); + comp.setFont(parent.getFont()); + + createSharedConfigComponent(comp); + createFavoritesComponent(comp); + } + + /** + * Creates the favorites control + * @param parent the parent composite to add this one to + * @since 3.2 + */ + private void createFavoritesComponent(Composite parent) { + Group favComp = SWTFactory.createGroup(parent, LaunchConfigurationsMessages.CommonTab_Display_in_favorites_menu__10, 1, 1, GridData.FILL_BOTH); + fFavoritesTable = CheckboxTableViewer.newCheckList(favComp, SWT.CHECK | SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION); + Control table = fFavoritesTable.getControl(); + GridData gd = new GridData(GridData.FILL_BOTH); + table.setLayoutData(gd); + table.setFont(parent.getFont()); + fFavoritesTable.setContentProvider(new FavoritesContentProvider()); + fFavoritesTable.setLabelProvider(new FavoritesLabelProvider()); + fFavoritesTable.addCheckStateListener(new ICheckStateListener() { + @Override + public void checkStateChanged(CheckStateChangedEvent event) { + updateLaunchConfigurationDialog(); + } + }); + } + + /** + * Creates the shared config component + * @param parent the parent composite to add this component to + * @since 3.2 + */ + private void createSharedConfigComponent(Composite parent) { + Group group = SWTFactory.createGroup(parent, LaunchConfigurationsMessages.CommonTab_0, 3, 2, GridData.FILL_HORIZONTAL); + Composite comp = SWTFactory.createComposite(group, parent.getFont(), 3, 3, GridData.FILL_BOTH, 0, 0); + fLocalRadioButton = createRadioButton(comp, LaunchConfigurationsMessages.CommonTab_L_ocal_3); + GridData gd = new GridData(); + gd.horizontalSpan = 3; + fLocalRadioButton.setLayoutData(gd); + fSharedRadioButton = createRadioButton(comp, LaunchConfigurationsMessages.CommonTab_S_hared_4); + fSharedRadioButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent evt) { + handleSharedRadioButtonSelected(); + } + }); + fSharedLocationText = SWTFactory.createSingleText(comp, 1); + fSharedLocationText.getAccessible().addAccessibleListener(new AccessibleAdapter() { + @Override + public void getName(AccessibleEvent e) { + e.result = LaunchConfigurationsMessages.CommonTab_S_hared_4; + } + }); + fSharedLocationText.addModifyListener(fBasicModifyListener); + fSharedLocationButton = createPushButton(comp, LaunchConfigurationsMessages.CommonTab__Browse_6, null); + fSharedLocationButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent evt) { + handleSharedLocationButtonSelected(); + } + }); + + fLocalRadioButton.setSelection(true); + setSharedEnabled(false); + } + + /** + * handles the shared radio button being selected + */ + private void handleSharedRadioButtonSelected() { + setSharedEnabled(isShared()); + updateLaunchConfigurationDialog(); + } + + /** + * Sets the widgets for specifying that a launch configuration is to be shared to the enable value + * @param enable the enabled value for + */ + private void setSharedEnabled(boolean enable) { + fSharedLocationText.setEnabled(enable); + fSharedLocationButton.setEnabled(enable); + } + + private String getDefaultSharedConfigLocation(ILaunchConfiguration config) { + String path = IInternalDebugCoreConstants.EMPTY_STRING; + try { + IResource[] res = config.getMappedResources(); + if(res != null) { + IProject proj; + for (int i = 0; i < res.length; i++) { + proj = res[i].getProject(); + if(proj != null && proj.isAccessible()) { + return proj.getFullPath().toOSString(); + } + } + } + } + catch (CoreException e) {DebugUIPlugin.log(e);} + return path; + } + + /** + * if the shared radio button is selected, indicating that the launch configuration is to be shared + * @return true if the radio button is selected, false otherwise + */ + private boolean isShared() { + return fSharedRadioButton.getSelection(); + } + + /** + * Handles the shared location button being selected + */ + private void handleSharedLocationButtonSelected() { + String currentContainerString = fSharedLocationText.getText(); + IContainer currentContainer = getContainer(currentContainerString); + SharedLocationSelectionDialog dialog = new SharedLocationSelectionDialog(getShell(), + currentContainer, + false, + LaunchConfigurationsMessages.CommonTab_Select_a_location_for_the_launch_configuration_13); + dialog.showClosedProjects(false); + dialog.open(); + Object[] results = dialog.getResult(); + if ((results != null) && (results.length > 0) && (results[0] instanceof IPath)) { + IPath path = (IPath)results[0]; + String containerName = path.toOSString(); + fSharedLocationText.setText(containerName); + } + } + + /** + * gets the container form the specified path + * @param path the path to get the container from + * @return the container for the specified path or null if one cannot be determined + */ + private IContainer getContainer(String path) { + Path containerPath = new Path(path); + return (IContainer) getWorkspaceRoot().findMember(containerPath); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#initializeFrom(org.eclipse.debug.core.ILaunchConfiguration) + */ + @Override + public void initializeFrom(ILaunchConfiguration configuration) { + boolean isShared = !configuration.isLocal(); + fSharedRadioButton.setSelection(isShared); + fLocalRadioButton.setSelection(!isShared); + setSharedEnabled(isShared); + fSharedLocationText.setText(getDefaultSharedConfigLocation(configuration)); + if(isShared) { + String containerName = IInternalDebugCoreConstants.EMPTY_STRING; + IFile file = configuration.getFile(); + if (file != null) { + IContainer parent = file.getParent(); + if (parent != null) { + containerName = parent.getFullPath().toOSString(); + } + } + fSharedLocationText.setText(containerName); + } + updateFavoritesFromConfig(configuration); + } + + + /** + * Updates the favorites selections from the local configuration + * @param config the local configuration + */ + private void updateFavoritesFromConfig(ILaunchConfiguration config) { + fFavoritesTable.setInput(config); + fFavoritesTable.setCheckedElements(new Object[]{}); + try { + List groups = config.getAttribute(IDebugUIConstants.ATTR_FAVORITE_GROUPS, new ArrayList()); + if (groups.isEmpty()) { + // check old attributes for backwards compatible + if (config.getAttribute(IDebugUIConstants.ATTR_DEBUG_FAVORITE, false)) { + groups.add(IDebugUIConstants.ID_DEBUG_LAUNCH_GROUP); + } + if (config.getAttribute(IDebugUIConstants.ATTR_RUN_FAVORITE, false)) { + groups.add(IDebugUIConstants.ID_RUN_LAUNCH_GROUP); + } + } + if (!groups.isEmpty()) { + List list = new ArrayList(); + Iterator iterator = groups.iterator(); + while (iterator.hasNext()) { + String id = (String)iterator.next(); + LaunchGroupExtension extension = getLaunchConfigurationManager().getLaunchGroup(id); + if (extension != null) { + list.add(extension); + } + } + fFavoritesTable.setCheckedElements(list.toArray()); + } + } catch (CoreException e) { + DebugUIPlugin.log(e); + } + } + + /** + * Updates the configuration form the local shared config working copy + * @param config the local shared config working copy + */ + private void updateConfigFromLocalShared(ILaunchConfigurationWorkingCopy config) { + if (isShared()) { + String containerPathString = fSharedLocationText.getText(); + IContainer container = getContainer(containerPathString); + if(container == null) { + //we need to force an attribute to allow the invalid container path to be revertable + config.setAttribute(BAD_CONTAINER, containerPathString); + } + else { + config.setContainer(container); + } + } else { + config.setContainer(null); + } + } + + /** + * Convenience accessor + */ + protected LaunchConfigurationManager getLaunchConfigurationManager() { + return DebugUIPlugin.getDefault().getLaunchConfigurationManager(); + } + + /** + * Update the favorite settings. + * + * NOTE: set to <code>null</code> instead of <code>false</code> for backwards compatibility + * when comparing if content is equal, since 'false' is default + * and will be missing for older configurations. + */ + private void updateConfigFromFavorites(ILaunchConfigurationWorkingCopy config) { + try { + Object[] checked = fFavoritesTable.getCheckedElements(); + boolean debug = config.getAttribute(IDebugUIConstants.ATTR_DEBUG_FAVORITE, false); + boolean run = config.getAttribute(IDebugUIConstants.ATTR_RUN_FAVORITE, false); + if (debug || run) { + // old attributes + List groups = new ArrayList(); + int num = 0; + if (debug) { + groups.add(getLaunchConfigurationManager().getLaunchGroup(IDebugUIConstants.ID_DEBUG_LAUNCH_GROUP)); + num++; + } + if (run) { + num++; + groups.add(getLaunchConfigurationManager().getLaunchGroup(IDebugUIConstants.ID_RUN_LAUNCH_GROUP)); + } + // see if there are any changes + if (num == checked.length) { + boolean different = false; + for (int i = 0; i < checked.length; i++) { + if (!groups.contains(checked[i])) { + different = true; + break; + } + } + if (!different) { + return; + } + } + } + config.setAttribute(IDebugUIConstants.ATTR_DEBUG_FAVORITE, (String)null); + config.setAttribute(IDebugUIConstants.ATTR_RUN_FAVORITE, (String)null); + List groups = null; + for (int i = 0; i < checked.length; i++) { + LaunchGroupExtension group = (LaunchGroupExtension)checked[i]; + if (groups == null) { + groups = new ArrayList(); + } + groups.add(group.getIdentifier()); + } + config.setAttribute(IDebugUIConstants.ATTR_FAVORITE_GROUPS, groups); + } catch (CoreException e) { + DebugUIPlugin.log(e); + } + } + + /** + * Convenience method for getting the workspace root. + */ + private IWorkspaceRoot getWorkspaceRoot() { + return ResourcesPlugin.getWorkspace().getRoot(); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#isValid(org.eclipse.debug.core.ILaunchConfiguration) + */ + @Override + public boolean isValid(ILaunchConfiguration config) { + setMessage(null); + setErrorMessage(null); + + return validateLocalShared(); + } + + /** + * validates the local shared config file location + * @return true if the local shared file exists, false otherwise + */ + private boolean validateLocalShared() { + if (isShared()) { + String path = fSharedLocationText.getText().trim(); + IContainer container = getContainer(path); + if (container == null || container.equals(ResourcesPlugin.getWorkspace().getRoot())) { + setErrorMessage(LaunchConfigurationsMessages.CommonTab_Invalid_shared_configuration_location_14); + return false; + } else if (!container.getProject().isOpen()) { + setErrorMessage(LaunchConfigurationsMessages.CommonTab_Cannot_save_launch_configuration_in_a_closed_project__1); + return false; + } + } + return true; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#setDefaults(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy) + */ + @Override + public void setDefaults(ILaunchConfigurationWorkingCopy config) { + config.setContainer(null); + setAttribute(IDebugUIConstants.ATTR_LAUNCH_IN_BACKGROUND, config, true, true); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#performApply(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy) + */ + @Override + public void performApply(ILaunchConfigurationWorkingCopy configuration) { + updateConfigFromLocalShared(configuration); + updateConfigFromFavorites(configuration); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#getName() + */ + @Override + public String getName() { + return LaunchConfigurationsMessages.CommonTab__Common_15; + } + + /** + * @see org.eclipse.debug.ui.AbstractLaunchConfigurationTab#getId() + * + * @since 3.3 + */ + @Override + public String getId() { + return "org.eclipse.debug.ui.commonTab"; //$NON-NLS-1$ + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#canSave() + */ + @Override + public boolean canSave() { + return validateLocalShared(); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#getImage() + */ + @Override + public Image getImage() { + return DebugUITools.getImage(IInternalDebugUIConstants.IMG_OBJS_COMMON_TAB); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#activated(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy) + */ + @Override + public void activated(ILaunchConfigurationWorkingCopy workingCopy) {} + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#deactivated(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy) + */ + @Override + public void deactivated(ILaunchConfigurationWorkingCopy workingCopy) {} + + /** + * Content provider for the favorites table + */ + class FavoritesContentProvider implements IStructuredContentProvider { + + @Override + public Object[] getElements(Object inputElement) { + ILaunchGroup[] groups = DebugUITools.getLaunchGroups(); + List possibleGroups = new ArrayList(); + ILaunchConfiguration configuration = (ILaunchConfiguration)inputElement; + for (int i = 0; i < groups.length; i++) { + ILaunchGroup extension = groups[i]; + LaunchHistory history = getLaunchConfigurationManager().getLaunchHistory(extension.getIdentifier()); + if (history != null && history.accepts(configuration)) { + possibleGroups.add(extension); + } + } + return possibleGroups.toArray(); + } + + @Override + public void dispose() {} + + @Override + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {} + + } + + /** + * Provides the labels for the favorites table + * + */ + class FavoritesLabelProvider implements ITableLabelProvider { + + private Map fImages = new HashMap(); + + @Override + public Image getColumnImage(Object element, int columnIndex) { + Image image = (Image)fImages.get(element); + if (image == null) { + ImageDescriptor descriptor = ((LaunchGroupExtension)element).getImageDescriptor(); + if (descriptor != null) { + image = descriptor.createImage(); + fImages.put(element, image); + } + } + return image; + } + + @Override + public String getColumnText(Object element, int columnIndex) { + String label = ((LaunchGroupExtension)element).getLabel(); + return DebugUIPlugin.removeAccelerators(label); + } + + @Override + public void addListener(ILabelProviderListener listener) {} + + @Override + public void dispose() { + Iterator images = fImages.values().iterator(); + while (images.hasNext()) { + Image image = (Image)images.next(); + image.dispose(); + } + } + + @Override + public boolean isLabelProperty(Object element, String property) {return false;} + + @Override + public void removeListener(ILabelProviderListener listener) {} + } + +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/GroupCycleHandler.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/GroupCycleHandler.java new file mode 100644 index 000000000..746cc7581 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/GroupCycleHandler.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2016 SSI Schaefer 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: + * SSI Schaefer - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.groups; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.debug.core.IStatusHandler; +import org.eclipse.debug.internal.ui.DebugUIMessages; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.osgi.util.NLS; +import org.eclipse.ui.PlatformUI; + +/** + * Handles the case that a group is nested within itself. + * + * @since 3.12 + */ +public class GroupCycleHandler implements IStatusHandler { + + @Override + public Object handleStatus(IStatus status, final Object source) throws CoreException { + PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { + @Override + public void run() { + MessageDialog.openError(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), DebugUIMessages.GroupLaunch_Error, NLS.bind(DebugUIMessages.GroupLaunch_Cycle, source.toString())); + } + }); + return null; + } + +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/GroupElementLaunchedHandler.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/GroupElementLaunchedHandler.java new file mode 100644 index 000000000..24d276ce9 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/GroupElementLaunchedHandler.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2016 SSI Schaefer 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: + * SSI Schaefer - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.groups; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.IStatusHandler; +import org.eclipse.debug.internal.ui.DebugUIPlugin; + +/** + * Handles additionally required actions when a member of a group has been + * launched + * + * @since 3.12 + */ +public class GroupElementLaunchedHandler implements IStatusHandler { + + @Override + public Object handleStatus(IStatus status, Object source) throws CoreException { + if (source instanceof ILaunch[]) { + ILaunch[] launches = (ILaunch[]) source; + + // Now we need to override the history to make multi-launch + // appear last, if we don't do it last launch would be our + // child's launch which is not correct for repeating the + // experience. + DebugUIPlugin.getDefault().getLaunchConfigurationManager().setRecentLaunch(launches[0]); + } + return null; + } + +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/GroupLaunchConfigurationSelectionDialog.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/GroupLaunchConfigurationSelectionDialog.java new file mode 100644 index 000000000..4c80c80b5 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/GroupLaunchConfigurationSelectionDialog.java @@ -0,0 +1,399 @@ +/******************************************************************************* + * Copyright (c) 2009, 2016 QNX Software Systems 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: + * QNX Software Systems - initial API and implementation + * Freescale Semiconductor + * SSI Schaefer + *******************************************************************************/ +package org.eclipse.debug.internal.ui.groups; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationType; +import org.eclipse.debug.core.ILaunchManager; +import org.eclipse.debug.internal.core.groups.GroupLaunchElement; +import org.eclipse.debug.internal.core.groups.GroupLaunchElement.GroupElementPostLaunchAction; +import org.eclipse.debug.internal.ui.DebugUIMessages; +import org.eclipse.debug.internal.ui.DebugUIPlugin; +import org.eclipse.debug.internal.ui.launchConfigurations.LaunchConfigurationFilteredTree; +import org.eclipse.debug.internal.ui.launchConfigurations.LaunchConfigurationManager; +import org.eclipse.debug.internal.ui.launchConfigurations.LaunchGroupFilter; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.debug.ui.ILaunchGroup; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.TitleAreaDialog; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerFilter; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.ui.dialogs.FilteredTree; +import org.eclipse.ui.dialogs.PatternFilter; + +/** + * Dialog to select launch configuration(s) + */ +class GroupLaunchConfigurationSelectionDialog extends TitleAreaDialog implements ISelectionChangedListener { + private ISelection fSelection; + private ILaunchGroup[] launchGroups; + private String mode; + private GroupElementPostLaunchAction action = GroupElementPostLaunchAction.NONE; + private Object actionParam; + private ViewerFilter emptyTypeFilter; + private IStructuredSelection fInitialSelection; + private ComboControlledStackComposite fStackComposite; + private Label fDelayAmountLabel; + private Text fDelayAmountWidget; // in seconds + private boolean fForEditing; // true if dialog was opened to edit an entry, + // otherwise it was opened to add one + + public GroupLaunchConfigurationSelectionDialog(Shell shell, String initMode, boolean forEditing, ILaunchConfiguration self) { + super(shell); + LaunchConfigurationManager manager = DebugUIPlugin.getDefault().getLaunchConfigurationManager(); + launchGroups = manager.getLaunchGroups(); + mode = initMode; + fForEditing = forEditing; + setShellStyle(getShellStyle() | SWT.RESIZE); + emptyTypeFilter = new ViewerFilter() { + @Override + public boolean select(Viewer viewer, Object parentElement, Object element) { + try { + if (element instanceof ILaunchConfigurationType) { + ILaunchConfigurationType type = (ILaunchConfigurationType) element; + if (type.equals(self.getType())) { + // we're hiding ourselves. if we're the only group, + // don't show the type. + return getLaunchManager().getLaunchConfigurations(type).length > 1; + } + + return getLaunchManager().getLaunchConfigurations(type).length > 0; + } else if (element instanceof ILaunchConfiguration) { + ILaunchConfiguration c = (ILaunchConfiguration) element; + if (c.getName().equals(self.getName()) && c.getType().equals(self.getType())) { + return false; + } + + if (c.getAttribute(IDebugUIConstants.ATTR_PRIVATE, false)) { + return false; + } + + return GroupLaunchConfigurationTabGroup.isValidLaunchReference(c); + } + return true; + } catch (CoreException e) { + return false; + } + } + }; + } + + protected ILaunchManager getLaunchManager() { + return DebugPlugin.getDefault().getLaunchManager(); + } + + @Override + protected Control createContents(Composite parent) { + Control x = super.createContents(parent); + validate(); + setErrorMessage(null); + return x; + } + + @Override + protected Control createDialogArea(Composite parent2) { + Composite comp = (Composite) super.createDialogArea(parent2); + + // title bar + getShell().setText(fForEditing ? DebugUIMessages.GroupLaunchConfigurationSelectionDialog_13 : DebugUIMessages.GroupLaunchConfigurationSelectionDialog_12); + + // dialog message area (not title bar) + setTitle(fForEditing ? DebugUIMessages.GroupLaunchConfigurationSelectionDialog_15 : DebugUIMessages.GroupLaunchConfigurationSelectionDialog_14); + + fStackComposite = new ComboControlledStackComposite(comp, SWT.NONE); + + Map<String, ILaunchGroup> modes = new TreeMap<>(); + for (ILaunchGroup launchGroup : launchGroups) { + if (!modes.containsKey(launchGroup.getMode())) { + modes.put(launchGroup.getMode(), launchGroup); + } + } + modes.put(GroupLaunchElement.MODE_INHERIT, new InheritModeGroup()); + + for (Map.Entry<String, ILaunchGroup> entry : modes.entrySet()) { + ILaunchGroup launchGroup = entry.getValue(); + LaunchConfigurationFilteredTree fTree = new LaunchConfigurationFilteredTree(fStackComposite.getStackParent(), SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER, new PatternFilter(), launchGroup, null); + String lgm = entry.getKey(); + fStackComposite.addItem(lgm, fTree); + fTree.createViewControl(); + ViewerFilter[] filters = fTree.getViewer().getFilters(); + for (ViewerFilter viewerFilter : filters) { + if (viewerFilter instanceof LaunchGroupFilter) { + fTree.getViewer().removeFilter(viewerFilter); + } + } + fTree.getViewer().addFilter(emptyTypeFilter); + fTree.getViewer().addSelectionChangedListener(this); + if (lgm.equals(this.mode)) { + fStackComposite.setSelection(lgm); + } + if (fInitialSelection != null) { + fTree.getViewer().setSelection(fInitialSelection, true); + } + } + fStackComposite.setLabelText(DebugUIMessages.GroupLaunchConfigurationSelectionDialog_4); + fStackComposite.pack(); + Rectangle bounds = fStackComposite.getBounds(); + // adjust size + GridData data = ((GridData) fStackComposite.getLayoutData()); + if (data == null) { + data = new GridData(GridData.FILL_BOTH); + fStackComposite.setLayoutData(data); + } + data.heightHint = Math.max(convertHeightInCharsToPixels(15), bounds.height); + data.widthHint = Math.max(convertWidthInCharsToPixels(40), bounds.width); + fStackComposite.getCombo().addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + mode = ((Combo) e.widget).getText(); + } + }); + + createPostLaunchControl(comp); + return comp; + } + + private void createPostLaunchControl(Composite parent) { + Composite comp = new Composite(parent, SWT.NONE); + comp.setLayout(new GridLayout(4, false)); + comp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + Label label = new Label(comp, SWT.NONE); + label.setText(DebugUIMessages.GroupLaunchConfigurationSelectionDialog_8); + Combo combo = new Combo(comp, SWT.READ_ONLY); + combo.add(GroupElementPostLaunchAction.NONE.getDescription()); + combo.add(GroupElementPostLaunchAction.WAIT_FOR_TERMINATION.getDescription()); + combo.add(GroupElementPostLaunchAction.DELAY.getDescription()); + combo.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + final String actionStr = ((Combo) e.widget).getText(); + action = GroupElementPostLaunchAction.valueOfDescription(actionStr); + showHideDelayAmountWidgets(); + validate(); + } + }); + combo.setText(action.getDescription()); + + fDelayAmountLabel = new Label(comp, SWT.NONE); + fDelayAmountLabel.setText(DebugUIMessages.GroupLaunchConfigurationSelectionDialog_9); + + fDelayAmountWidget = new Text(comp, SWT.SINGLE | SWT.BORDER); + GridData gridData = new GridData(); + gridData.widthHint = convertWidthInCharsToPixels(8); + fDelayAmountWidget.setLayoutData(gridData); + fDelayAmountWidget.addModifyListener(new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { + String text = ((Text) e.widget).getText(); + try { + actionParam = Integer.valueOf(text); + } catch (NumberFormatException exc) { + actionParam = null; + } + validate(); + } + }); + if (actionParam instanceof Integer) { + fDelayAmountWidget.setText(((Integer) actionParam).toString()); + } + + showHideDelayAmountWidgets(); + } + + private void showHideDelayAmountWidgets() { + final boolean visible = action == GroupElementPostLaunchAction.DELAY; + fDelayAmountLabel.setVisible(visible); + fDelayAmountWidget.setVisible(visible); + } + + public ILaunchConfiguration[] getSelectedLaunchConfigurations() { + List<ILaunchConfiguration> configs = new ArrayList<ILaunchConfiguration>(); + if (fSelection != null && !fSelection.isEmpty()) { + for (Iterator<?> iter = ((IStructuredSelection) fSelection).iterator(); iter.hasNext();) { + Object selection = iter.next(); + if (selection instanceof ILaunchConfiguration) { + configs.add((ILaunchConfiguration) selection); + } + } + } + return configs.toArray(new ILaunchConfiguration[configs.size()]); + } + + public String getMode() { + return mode; + } + + public GroupElementPostLaunchAction getAction() { + return action; + } + + public Object getActionParam() { + return actionParam; + } + + public static GroupLaunchConfigurationSelectionDialog createDialog(Shell shell, String initMode, boolean forEditing, ILaunchConfiguration self) { + return new GroupLaunchConfigurationSelectionDialog(shell, initMode, forEditing, self); + } + + @Override + public void selectionChanged(SelectionChangedEvent event) { + + // This listener gets called for a selection change in the launch + // configuration viewer embedded in the dialog. Problem is, there are + // numerous viewers--one for each platform debug ILaunchGroup (run, + // debug, profile). These viewers are stacked, so only one is ever + // visible to the user. During initialization, we get a selection change + // notification for every viewer. We need to ignore all but the one that + // matters--the visible one. + + Tree topTree = null; + final Control topControl = fStackComposite.getTopControl(); + if (topControl instanceof FilteredTree) { + final TreeViewer viewer = ((FilteredTree) topControl).getViewer(); + if (viewer != null) { + topTree = viewer.getTree(); + } + } + if (topTree == null) { + return; + } + + boolean selectionIsForVisibleViewer = false; + final Object src = event.getSource(); + if (src instanceof Viewer) { + final Control viewerControl = ((Viewer) src).getControl(); + if (viewerControl == topTree) { + selectionIsForVisibleViewer = true; + } + } + + if (!selectionIsForVisibleViewer) { + return; + } + + fSelection = event.getSelection(); + validate(); + } + + protected void validate() { + Button ok_button = getButton(IDialogConstants.OK_ID); + boolean isValid = true; + if (getSelectedLaunchConfigurations().length < 1) { + setErrorMessage(DebugUIMessages.GroupLaunchConfigurationSelectionDialog_7); + isValid = false; + } else { + setErrorMessage(null); + } + + if (isValid) { + if (fForEditing) { + // must have only one selection + if (getSelectedLaunchConfigurations().length > 1) { + setErrorMessage(DebugUIMessages.GroupLaunchConfigurationSelectionDialog_11); + isValid = false; + } + } + } + + if (isValid) { + if (action == GroupElementPostLaunchAction.DELAY) { + isValid = (actionParam instanceof Integer) && ((Integer) actionParam > 0); + setErrorMessage(isValid ? null : DebugUIMessages.GroupLaunchConfigurationSelectionDialog_10); + } + } + + if (ok_button != null) { + ok_button.setEnabled(isValid); + } + } + + public void setInitialSelection(GroupLaunchElement el) { + action = el.action; + actionParam = el.actionParam; + fInitialSelection = new StructuredSelection(el.data); + fSelection = fInitialSelection; + } + + /** + * 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 null; + } + + @Override + public String getIdentifier() { + return null; + } + + @Override + public String getCategory() { + return null; + } + + @Override + public String getMode() { + return null; + } + + @Override + public boolean isPublic() { + return false; + } + + } +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/GroupLaunchConfigurationTabGroup.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/GroupLaunchConfigurationTabGroup.java new file mode 100644 index 000000000..b7140238f --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/GroupLaunchConfigurationTabGroup.java @@ -0,0 +1,572 @@ +/******************************************************************************* + * Copyright (c) 2009, 2012, 2016 QNX Software Systems 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: + * QNX Software Systems - initial API and implementation + * Freescale Semiconductor + * SSI Schaefer + *******************************************************************************/ +package org.eclipse.debug.internal.ui.groups; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +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.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.ui.AbstractLaunchConfigurationTab; +import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup; +import org.eclipse.debug.ui.ILaunchConfigurationDialog; +import org.eclipse.debug.ui.ILaunchConfigurationTab; +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 el.mode; + } + + // 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$ + default: + assert false : "new post launch action missing logic here"; //$NON-NLS-1$ + return ""; //$NON-NLS-1$ + } + } + return null; + } + } + + static class CheckStateProvider implements ICheckStateProvider { + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.ICheckStateProvider#isChecked(java.lang.Object) + */ + @Override + public boolean isChecked(Object element) { + if (element instanceof GroupLaunchElement) { + return ((GroupLaunchElement)element).enabled; + } + return false; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.ICheckStateProvider#isGrayed(java.lang.Object) + */ + @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 = new Button(parent, SWT.PUSH); + button.setText(key); + button.setFont(parent.getFont()); + GridData data = new GridData(GridData.FILL_HORIZONTAL); + button.setLayoutData(data); + 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<GroupLaunchElement>(); + + /** + * 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); + 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; + el.name = config.getName(); + el.data = config; + el.mode = dialog.getMode(); + el.action = dialog.getAction(); + el.actionParam = dialog.getActionParam(); + 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$ + el.name = confs[0].getName(); + el.data = confs[0]; + el.mode = dialog.getMode(); + el.action = dialog.getAction(); + el.actionParam = dialog.getActionParam(); + treeViewer.refresh(true); + updateWidgetEnablement(); + updateLaunchConfigurationDialog(); + } + } + @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<Integer>(); + + 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 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 + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.AbstractLaunchConfigurationTab#isValid(org.eclipse.debug.core.ILaunchConfiguration) + */ + @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) { + 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); + } +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/GroupLaunchHandler.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/GroupLaunchHandler.java new file mode 100644 index 000000000..799a34a90 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/GroupLaunchHandler.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2016 SSI Schaefer 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: + * SSI Schaefer - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.groups; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.debug.core.IStatusHandler; +import org.eclipse.debug.internal.core.groups.GroupLaunchConfigurationDelegate; +import org.eclipse.debug.internal.ui.DebugUIPlugin; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.jface.preference.IPreferenceStore; + +/** + * Handles the states whenever a group launch begins, and when one is done + * launching. + * <p> + * The implementation uses some static variables to keep track as + * IStatusHandlers are instantiated from scratch for each state change. + * + * @since 3.12 + */ +public class GroupLaunchHandler implements IStatusHandler { + + private static AtomicInteger launchCounter = new AtomicInteger(0); + private static boolean removeLaunchesState = false; + + @Override + public Object handleStatus(IStatus status, Object source) throws CoreException { + final IPreferenceStore prefStore = DebugUIPlugin.getDefault().getPreferenceStore(); + + switch (status.getCode()) { + case GroupLaunchConfigurationDelegate.CODE_GROUP_LAUNCH_START: + int prevCount = launchCounter.getAndIncrement(); + if (prevCount == 0) { + // Have to temporarily turn off the "remove terminated + // launches when new one created" preference because it does + // not work well for group launches + removeLaunchesState = prefStore.getBoolean(IDebugUIConstants.PREF_AUTO_REMOVE_OLD_LAUNCHES); + prefStore.setValue(IDebugUIConstants.PREF_AUTO_REMOVE_OLD_LAUNCHES, false); + } + break; + case GroupLaunchConfigurationDelegate.CODE_GROUP_LAUNCH_DONE: + int newCount = launchCounter.decrementAndGet(); + if (newCount == 0) { + prefStore.setValue(IDebugUIConstants.PREF_AUTO_REMOVE_OLD_LAUNCHES, removeLaunchesState); + } + break; + default: + // unknown state... + break; + } + return null; + } + +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/UnsupportedModeHandler.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/UnsupportedModeHandler.java new file mode 100644 index 000000000..cc58548d5 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/UnsupportedModeHandler.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2016 SSI Schaefer 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: + * SSI Schaefer - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.groups; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.debug.core.IStatusHandler; +import org.eclipse.debug.internal.ui.DebugUIMessages; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.osgi.util.NLS; +import org.eclipse.ui.PlatformUI; + +/** + * Handles the case where a launch configuration in a group cannot be launched + * in the requested mode. + * + * @since 3.12 + */ +public class UnsupportedModeHandler implements IStatusHandler { + + @Override + public Object handleStatus(IStatus status, Object source) throws CoreException { + if (source instanceof String[]) { + final String[] data = (String[]) source; + PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { + @Override + public void run() { + MessageDialog.openError(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), DebugUIMessages.GroupLaunch_Error, NLS.bind(DebugUIMessages.GroupLaunch_Cannot_launch, data[0], data[1])); + } + }); + } + return null; + } + +} |