Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarkus Duft2016-05-11 13:52:28 +0000
committerMarkus Duft2016-11-30 09:42:25 +0000
commit97be8772a897b47a48b2d4b6ef33b46811408b7f (patch)
treeef2793a75f11741be425b7d236695df5f0f252d2
parent66da2950cc347953d38ae225f82b5f23f5e960e7 (diff)
downloadeclipse.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>
-rw-r--r--org.eclipse.debug.core/META-INF/MANIFEST.MF1
-rw-r--r--org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DebugCoreMessages.java14
-rw-r--r--org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DebugCoreMessages.properties10
-rw-r--r--org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunch.java253
-rw-r--r--org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunchConfigurationDelegate.java343
-rw-r--r--org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunchElement.java66
-rw-r--r--org.eclipse.debug.core/plugin.properties3
-rw-r--r--org.eclipse.debug.core/plugin.xml19
-rw-r--r--org.eclipse.debug.ui/META-INF/MANIFEST.MF1
-rw-r--r--org.eclipse.debug.ui/icons/full/obj16/lgroup_obj.pngbin0 -> 306 bytes
-rw-r--r--org.eclipse.debug.ui/icons/full/obj16/lgroup_obj@2x.pngbin0 -> 648 bytes
-rw-r--r--org.eclipse.debug.ui/plugin.xml42
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/DebugUIMessages.java31
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/DebugUIMessages.properties32
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/ComboControlledStackComposite.java138
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/CommonTabLite.java603
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/GroupCycleHandler.java39
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/GroupElementLaunchedHandler.java41
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/GroupLaunchConfigurationSelectionDialog.java399
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/GroupLaunchConfigurationTabGroup.java572
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/GroupLaunchHandler.java65
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/UnsupportedModeHandler.java43
22 files changed, 2714 insertions, 1 deletions
diff --git a/org.eclipse.debug.core/META-INF/MANIFEST.MF b/org.eclipse.debug.core/META-INF/MANIFEST.MF
index 0effc659e..e306ac17c 100644
--- a/org.eclipse.debug.core/META-INF/MANIFEST.MF
+++ b/org.eclipse.debug.core/META-INF/MANIFEST.MF
@@ -14,6 +14,7 @@ Export-Package: org.eclipse.debug.core,
org.eclipse.debug.core.sourcelookup.containers,
org.eclipse.debug.internal.core;x-friends:="org.eclipse.debug.ui,org.eclipse.debug.tests,org.eclipse.debug.examples.mixedmode",
org.eclipse.debug.internal.core.commands;x-friends:="org.eclipse.debug.ui",
+ org.eclipse.debug.internal.core.groups,
org.eclipse.debug.internal.core.sourcelookup;x-friends:="org.eclipse.debug.ui",
org.eclipse.debug.internal.core.sourcelookup.containers;x-friends:="org.eclipse.debug.ui",
org.eclipse.debug.internal.core.variables;x-friends:="org.eclipse.debug.ui,org.eclipse.jdt.debug.ui"
diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DebugCoreMessages.java b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DebugCoreMessages.java
index 5e4d8ad30..48bec126b 100644
--- a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DebugCoreMessages.java
+++ b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DebugCoreMessages.java
@@ -32,6 +32,20 @@ public class DebugCoreMessages extends NLS {
public static String DebugPlugin_8;
public static String DebugPlugin_Eclipse_runtime_does_not_support_working_directory_2;
public static String EnvironmentVariableResolver_0;
+ public static String GroupLaunchConfigurationDelegate_Delay;
+ public static String GroupLaunchConfigurationDelegate_Delaying;
+ public static String GroupLaunchConfigurationDelegate_Launching;
+ public static String GroupLaunchConfigurationDelegate_mode_debug;
+
+ public static String GroupLaunchConfigurationDelegate_mode_inherit;
+
+ public static String GroupLaunchConfigurationDelegate_mode_profile;
+
+ public static String GroupLaunchConfigurationDelegate_mode_run;
+
+ public static String GroupLaunchConfigurationDelegate_None;
+ public static String GroupLaunchConfigurationDelegate_Wait_until_terminated;
+ public static String GroupLaunchConfigurationDelegate_Waiting_for_termination;
public static String LaunchConfiguration_0;
public static String LaunchConfiguration_11;
public static String LaunchConfiguration_13;
diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DebugCoreMessages.properties b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DebugCoreMessages.properties
index cf51e242e..0e20ad951 100644
--- a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DebugCoreMessages.properties
+++ b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DebugCoreMessages.properties
@@ -26,6 +26,16 @@ DebugPlugin_6=An exception occurred in asynchronous runnable.
DebugPlugin_7=An exception occurred while filtering debug events.
DebugPlugin_8=An exception occurred while dispatching debug events.
EnvironmentVariableResolver_0=Environment variable not specified
+GroupLaunchConfigurationDelegate_Delay=Delay
+GroupLaunchConfigurationDelegate_Delaying=Delaying next launch by {0} seconds
+GroupLaunchConfigurationDelegate_Launching=Launching ''{0}''
+GroupLaunchConfigurationDelegate_mode_debug=debug
+GroupLaunchConfigurationDelegate_mode_inherit=inherit
+GroupLaunchConfigurationDelegate_mode_profile=profile
+GroupLaunchConfigurationDelegate_mode_run=run
+GroupLaunchConfigurationDelegate_None=None
+GroupLaunchConfigurationDelegate_Wait_until_terminated=Wait until terminated
+GroupLaunchConfigurationDelegate_Waiting_for_termination=Waiting for termination of ''{0}''
SystemPropertyResolver_0=System property not specified
InputStreamMonitor_label=Input Stream Monitor
Launch_terminate_failed=Terminate failed
diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunch.java b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunch.java
new file mode 100644
index 000000000..df314a18a
--- /dev/null
+++ b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunch.java
@@ -0,0 +1,253 @@
+/*******************************************************************************
+ * 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.core.groups;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchesListener2;
+import org.eclipse.debug.core.Launch;
+import org.eclipse.debug.core.model.IProcess;
+import org.eclipse.debug.internal.core.DebugCoreMessages;
+
+/**
+ * A specialization of launch to track sub-launches life-cycle, also terminates
+ * itself when all sub-launches are terminated
+ *
+ * @since 3.11
+ */
+public class GroupLaunch extends Launch implements ILaunchesListener2 {
+
+ /**
+ * Whether this process has been terminated
+ */
+ private boolean fTerminated;
+
+ /**
+ * Keeps track of whether launching has been finished
+ */
+ private boolean fLaunched = false;
+
+ /**
+ * A map of all our sub-launches and the current processes that belong
+ * to each one.
+ */
+ private Map<ILaunch, IProcess[]> subLaunches = new HashMap<ILaunch, IProcess[]>();
+
+ public GroupLaunch(ILaunchConfiguration launchConfiguration, String mode) {
+ super(launchConfiguration, mode, null);
+ getLaunchManager().addLaunchListener((ILaunchesListener2) this);
+ }
+
+ public void markLaunched() {
+ fLaunched = true;
+ }
+
+ /**
+ * Associate the launch
+ *
+ * @param subLaunch
+ */
+ public void addSubLaunch(ILaunch subLaunch) {
+ subLaunches.put(subLaunch, new IProcess[] {});
+ }
+
+ private boolean isChild(ILaunch launch) {
+ for (ILaunch subLaunch : subLaunches.keySet()) {
+ if (subLaunch == launch) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Override default behavior by querying all sub-launches to see if they
+ * are terminated
+ *
+ * @see org.eclipse.debug.core.Launch#isTerminated()
+ */
+ @Override
+ public boolean isTerminated() {
+ if (fTerminated) {
+ return true;
+ }
+
+ if (subLaunches.size() == 0) {
+ return false;
+ }
+
+ for (ILaunch launch : subLaunches.keySet()) {
+ if (!launch.isTerminated()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Override default behavior by querying all sub-launches if they can be
+ * terminated
+ *
+ * @see org.eclipse.debug.core.Launch#canTerminate()
+ */
+ @Override
+ public boolean canTerminate() {
+ if (subLaunches.size() == 0) {
+ return false;
+ }
+
+ for (ILaunch launch : subLaunches.keySet()) {
+ if (launch.canTerminate()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Override default behavior by terminating all sub-launches
+ *
+ * @see org.eclipse.debug.core.Launch#terminate()
+ */
+ @Override
+ public void terminate() throws DebugException {
+ MultiStatus status = new MultiStatus(DebugPlugin.getUniqueIdentifier(), DebugException.REQUEST_FAILED, DebugCoreMessages.Launch_terminate_failed, null);
+
+ // somebody want's to explicitly kill the whole group, which should
+ // immediately terminate and stop launching. So allow termination of the
+ // group when children disappear even if launching has not finished yet.
+ markLaunched();
+
+ for (ILaunch launch : subLaunches.keySet()) {
+ if (launch.canTerminate()) {
+ try {
+ launch.terminate();
+ } catch (DebugException e) {
+ status.merge(e.getStatus());
+ }
+ }
+ }
+
+ if (status.isOK()) {
+ return;
+ }
+
+ IStatus[] children = status.getChildren();
+ if (children.length == 1) {
+ throw new DebugException(children[0]);
+ }
+
+ throw new DebugException(status);
+ }
+
+ /**
+ * Handle terminated sub-launch
+ *
+ * @param launch
+ */
+ private void launchTerminated(ILaunch launch) {
+ if (this == launch) {
+ return;
+ }
+
+ // Remove sub launch, keeping the processes of the terminated launch
+ // to show the association and to keep the console content accessible
+ if (subLaunches.remove(launch) != null) {
+ // terminate ourselves if this is the last sub launch
+ if (subLaunches.size() == 0 && fLaunched) {
+ fTerminated = true;
+ fireTerminate();
+ }
+ }
+ }
+
+ @Override
+ public void launchChanged(ILaunch launch) {
+ if (this == launch) {
+ return;
+ }
+
+ // add/remove processes
+ if (isChild(launch)) {
+ // Remove old processes
+ IProcess[] oldProcesses = subLaunches.get(launch);
+ IProcess[] newProcesses = launch.getProcesses();
+
+ // avoid notifications when processes have not changed.
+ if (!Arrays.equals(oldProcesses, newProcesses)) {
+ for (IProcess oldProcess : oldProcesses) {
+ removeProcess(oldProcess);
+ }
+
+ // Add new processes
+ for (IProcess newProcess : newProcesses) {
+ addProcess(newProcess);
+ }
+
+ // Replace the processes of the changed launch
+ subLaunches.put(launch, newProcesses);
+ }
+ }
+ }
+
+ @Override
+ public void launchRemoved(ILaunch launch) {
+ if (this == launch) {
+ super.launchRemoved(launch);
+ // Remove the processes we got from the sub-launches from this
+ // launch
+ IProcess[] processes = getProcesses();
+ for (IProcess process : processes) {
+ removeProcess(process);
+ }
+
+ getLaunchManager().removeLaunchListener((ILaunchesListener2) this);
+ }
+ }
+
+ @Override
+ public void launchesTerminated(ILaunch[] launches) {
+ for (ILaunch launch : launches) {
+ launchTerminated(launch);
+ }
+ }
+
+ @Override
+ public void launchesAdded(ILaunch[] launches) {
+ for (ILaunch launch : launches) {
+ launchAdded(launch);
+ }
+ }
+
+ @Override
+ public void launchesChanged(ILaunch[] launches) {
+ for (ILaunch launch : launches) {
+ launchChanged(launch);
+ }
+ }
+
+ @Override
+ public void launchesRemoved(ILaunch[] launches) {
+ for (ILaunch launch : launches) {
+ launchRemoved(launch);
+ }
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunchConfigurationDelegate.java b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunchConfigurationDelegate.java
new file mode 100644
index 000000000..8d9f5a7d5
--- /dev/null
+++ b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunchConfigurationDelegate.java
@@ -0,0 +1,343 @@
+/*******************************************************************************
+ * 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.core.groups;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.debug.core.IStatusHandler;
+import org.eclipse.debug.core.model.ILaunchConfigurationDelegate2;
+import org.eclipse.debug.core.model.LaunchConfigurationDelegate;
+import org.eclipse.debug.internal.core.DebugCoreMessages;
+import org.eclipse.debug.internal.core.IInternalDebugCoreConstants;
+import org.eclipse.debug.internal.core.groups.GroupLaunchElement.GroupElementPostLaunchAction;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Group Launch delegate. Launches each configuration in the user selected mode
+ *
+ * @since 3.11
+ */
+public class GroupLaunchConfigurationDelegate extends LaunchConfigurationDelegate implements ILaunchConfigurationDelegate2 {
+ public static final int CODE_GROUP_LAUNCH_START = 233;
+ public static final int CODE_GROUP_LAUNCH_DONE = 234;
+
+ private static final String NAME_PROP = "name"; //$NON-NLS-1$
+ private static final String ENABLED_PROP = "enabled"; //$NON-NLS-1$
+ private static final String MODE_PROP = "mode"; //$NON-NLS-1$
+ private static final String ACTION_PROP = "action"; //$NON-NLS-1$
+ private static final String ACTION_PARAM_PROP = "actionParam"; //$NON-NLS-1$
+ private static final String MULTI_LAUNCH_CONSTANTS_PREFIX = "org.eclipse.debug.core.launchGroup"; //$NON-NLS-1$
+
+ private static final String DEBUG_CORE = "org.eclipse.debug.core"; //$NON-NLS-1$
+
+ private static final Status UNSUPPORTED_MODE = new Status(IStatus.ERROR, DEBUG_CORE, 230, IInternalDebugCoreConstants.EMPTY_STRING, null);
+ private static final Status GROUP_ELEMENT_STARTED = new Status(IStatus.OK, DEBUG_CORE, 231, IInternalDebugCoreConstants.EMPTY_STRING, null);
+ private static final Status GROUP_CYCLE = new Status(IStatus.ERROR, DEBUG_CORE, 232, IInternalDebugCoreConstants.EMPTY_STRING, null);
+
+ private static final Status GROUP_LAUNCH_START = new Status(IStatus.INFO, DEBUG_CORE, CODE_GROUP_LAUNCH_START, IInternalDebugCoreConstants.EMPTY_STRING, null);
+ private static final Status GROUP_LAUNCH_DONE = new Status(IStatus.INFO, DEBUG_CORE, CODE_GROUP_LAUNCH_DONE, IInternalDebugCoreConstants.EMPTY_STRING, null);
+
+ public GroupLaunchConfigurationDelegate() {
+ // nothing
+ }
+
+ @Override
+ public ILaunch getLaunch(ILaunchConfiguration configuration, String mode) throws CoreException {
+ return new GroupLaunch(configuration, mode);
+ }
+
+ @Override
+ public void launch(ILaunchConfiguration groupConfig, String mode, final ILaunch groupLaunch, IProgressMonitor monitor) throws CoreException {
+ final GroupLaunch group = (GroupLaunch) groupLaunch;
+
+ IStatusHandler groupStateHandler = DebugPlugin.getDefault().getStatusHandler(GROUP_LAUNCH_START);
+ groupStateHandler.handleStatus(GROUP_LAUNCH_START, group);
+
+ try {
+ SubMonitor progress = SubMonitor.convert(monitor, NLS.bind(DebugCoreMessages.GroupLaunchConfigurationDelegate_Launching, groupConfig.getName()), 1000);
+
+ List<GroupLaunchElement> launches = createLaunchElements(groupConfig);
+ for (int i = 0; i < launches.size(); ++i) {
+ GroupLaunchElement le = launches.get(i);
+
+ if (!le.enabled) {
+ continue;
+ }
+
+ // find launch; if not found, skip (error?)
+ final ILaunchConfiguration conf = findLaunch(le.name);
+ if (conf == null) {
+ continue;
+ }
+
+ // determine mode for each launch
+ final String localMode;
+ if (!le.mode.equals(GroupLaunchElement.MODE_INHERIT)) {
+ localMode = le.mode;
+ } else {
+ localMode = mode;
+ }
+ if (!conf.supportsMode(localMode)) {
+ IStatusHandler handler = DebugPlugin.getDefault().getStatusHandler(UNSUPPORTED_MODE);
+ handler.handleStatus(UNSUPPORTED_MODE, new String[] {
+ conf.getName(), localMode });
+ continue;
+ }
+
+ if (groupConfig.getName().equals(conf.getName())) {
+ // loop detected. report as appropriate and die.
+ IStatusHandler cycleHandler = DebugPlugin.getDefault().getStatusHandler(GROUP_CYCLE);
+ cycleHandler.handleStatus(GROUP_CYCLE, conf.getName());
+ } else {
+ launchChild(progress.newChild(1000 / launches.size()), group, le, conf, localMode, (i == launches.size() - 1));
+ }
+
+ // in case the group has been terminated while waiting in the
+ // post launch action.
+ if (group.isTerminated()) {
+ break;
+ }
+ }
+
+
+ if (!group.hasChildren()) {
+ DebugPlugin.getDefault().getLaunchManager().removeLaunch(group);
+ }
+ } finally {
+ // safety net - launching is finished also in case of a problem.
+ group.markLaunched();
+ groupStateHandler.handleStatus(GROUP_LAUNCH_DONE, group);
+ monitor.done();
+ }
+ }
+
+ private void launchChild(SubMonitor monitor, final GroupLaunch group, GroupLaunchElement le, final ILaunchConfiguration child, final String localMode, boolean lastConfig) throws CoreException {
+ ILaunch subLaunch = child.launch(localMode, monitor);
+ group.addSubLaunch(subLaunch);
+
+ // Now that we added the launch in our list, we have already
+ // received the real launchChanged event, and did not know
+ // it was part of our list
+ // So, fake another event now.
+ group.launchChanged(subLaunch);
+
+ // give handler a chance to perform additional actions after
+ // launching each of the members.
+ IStatusHandler postLaunchHandler = DebugPlugin.getDefault().getStatusHandler(GROUP_ELEMENT_STARTED);
+ postLaunchHandler.handleStatus(GROUP_ELEMENT_STARTED, new ILaunch[] {
+ group, subLaunch });
+
+ // if this is the last child, mark the group as "launching finished", so
+ // that from now on the last terminating child will also terminate the
+ // group.
+ if (lastConfig) {
+ group.markLaunched();
+ }
+
+ postLaunchAction(subLaunch, le, monitor);
+ }
+
+ private void postLaunchAction(ILaunch subLaunch, GroupLaunchElement le, IProgressMonitor monitor) {
+ switch (le.action) {
+ case NONE:
+ return;
+ case WAIT_FOR_TERMINATION:
+ monitor.subTask(NLS.bind(DebugCoreMessages.GroupLaunchConfigurationDelegate_Waiting_for_termination, subLaunch.getLaunchConfiguration().getName()));
+ while (!subLaunch.isTerminated() && !monitor.isCanceled()) {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ break;
+ }
+ }
+ monitor.subTask(""); //$NON-NLS-1$
+ break;
+ case DELAY:
+ Integer waitSecs = (Integer) le.actionParam;
+ if (waitSecs != null) {
+ monitor.subTask(NLS.bind(DebugCoreMessages.GroupLaunchConfigurationDelegate_Delaying, waitSecs.toString()));
+ try {
+ Thread.sleep(waitSecs * 1000); // param is milliseconds
+ } catch (InterruptedException e) {
+ // ok
+ }
+ }
+ break;
+
+ default:
+ assert false : "new post launch action type is missing logic"; //$NON-NLS-1$
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see
+ * org.eclipse.debug.core.model.LaunchConfigurationDelegate#buildProjects(
+ * org.eclipse.core.resources.IProject[],
+ * org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ protected void buildProjects(IProject[] projects, IProgressMonitor monitor) throws CoreException {
+ // do nothing, project can be rebuild for each launch individually
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see
+ * org.eclipse.debug.core.model.LaunchConfigurationDelegate#buildForLaunch(
+ * org.eclipse.debug.core.ILaunchConfiguration, java.lang.String,
+ * org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public boolean buildForLaunch(ILaunchConfiguration configuration, String mode, IProgressMonitor monitor) throws CoreException {
+ // not build for this one
+ return false;
+ }
+
+ protected static ILaunchConfiguration findLaunch(String name) throws CoreException {
+ ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
+ ILaunchConfiguration[] launchConfigurations = launchManager.getLaunchConfigurations();
+ for (int i = 0; i < launchConfigurations.length; i++) {
+ ILaunchConfiguration lConf = launchConfigurations[i];
+ if (lConf.getName().equals(name)) {
+ return lConf;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * (Re-)reads all {@link GroupLaunchElement}s from a
+ * {@link ILaunchConfiguration}s attributes.
+ *
+ * @param configuration the launch configuration; this must be of the type
+ * handled by this delegate
+ * @return all {@link GroupLaunchElement}s found in the given
+ * {@link ILaunchConfiguration}s attributes. Never
+ * <code>null</code>.
+ */
+ public static List<GroupLaunchElement> createLaunchElements(ILaunchConfiguration configuration) {
+ List<GroupLaunchElement> result = new ArrayList<>();
+ try {
+ Map<?, ?> attrs = configuration.getAttributes();
+ for (Iterator<?> iterator = attrs.keySet().iterator(); iterator.hasNext();) {
+ String attr = (String) iterator.next();
+ try {
+ if (attr.startsWith(GroupLaunchConfigurationDelegate.MULTI_LAUNCH_CONSTANTS_PREFIX)) {
+ String prop = attr.substring(GroupLaunchConfigurationDelegate.MULTI_LAUNCH_CONSTANTS_PREFIX.length() + 1);
+ int k = prop.indexOf('.');
+ String num = prop.substring(0, k);
+ int index = Integer.parseInt(num);
+ String name = prop.substring(k + 1);
+ if (name.equals(NAME_PROP)) {
+ GroupLaunchElement el = new GroupLaunchElement();
+ el.index = index;
+ el.name = (String) attrs.get(attr);
+
+ Object actionParam = null;
+ String actionStr = (String) attrs.get(getProp(index, ACTION_PROP));
+
+ GroupElementPostLaunchAction action;
+ try {
+ action = GroupElementPostLaunchAction.valueOf(actionStr);
+ } catch (Exception e) {
+ action = GroupElementPostLaunchAction.NONE;
+ }
+ if (action == GroupElementPostLaunchAction.DELAY) {
+ try {
+ actionParam = Integer.parseInt((String) attrs.get(getProp(index, ACTION_PARAM_PROP)));
+ } catch (NumberFormatException exc) {
+ DebugPlugin.log(exc);
+ }
+ }
+ el.action = action;
+ el.actionParam = actionParam;
+ el.mode = (String) attrs.get(getProp(index, MODE_PROP));
+ el.enabled = (Boolean) attrs.get(getProp(index, ENABLED_PROP));
+ try {
+ el.data = findLaunch(el.name);
+ } catch (Exception e) {
+ el.data = null;
+ }
+ while (index >= result.size()) {
+ result.add(null);
+ }
+ result.set(index, el);
+
+ }
+ }
+ } catch (Exception e) {
+ DebugPlugin.log(e);
+ }
+ }
+ } catch (CoreException e) {
+ DebugPlugin.log(e);
+ }
+ return result;
+ }
+
+ public static void storeLaunchElements(ILaunchConfigurationWorkingCopy configuration, List<GroupLaunchElement> input) {
+ int i = 0;
+ removeLaunchElements(configuration);
+ for (GroupLaunchElement el : input) {
+ if (el == null) {
+ continue;
+ }
+ configuration.setAttribute(GroupLaunchConfigurationDelegate.getProp(i, NAME_PROP), el.name);
+ configuration.setAttribute(GroupLaunchConfigurationDelegate.getProp(i, ACTION_PROP), el.action.toString());
+ // note: the saving of the action param will need to be enhanced if
+ // ever an action type is introduced that uses something that can't
+ // be reconstructed from its toString()
+ configuration.setAttribute(GroupLaunchConfigurationDelegate.getProp(i, ACTION_PARAM_PROP), el.actionParam != null ? el.actionParam.toString() : null);
+ configuration.setAttribute(GroupLaunchConfigurationDelegate.getProp(i, MODE_PROP), el.mode);
+ configuration.setAttribute(GroupLaunchConfigurationDelegate.getProp(i, ENABLED_PROP), el.enabled);
+ i++;
+ }
+ }
+
+ public static void removeLaunchElements(ILaunchConfigurationWorkingCopy configuration) {
+ try {
+ Map<?, ?> attrs = configuration.getAttributes();
+ for (Iterator<?> iterator = attrs.keySet().iterator(); iterator.hasNext();) {
+ String attr = (String) iterator.next();
+ try {
+ if (attr.startsWith(GroupLaunchConfigurationDelegate.MULTI_LAUNCH_CONSTANTS_PREFIX)) {
+ configuration.removeAttribute(attr);
+ }
+ } catch (Exception e) {
+ DebugPlugin.log(e);
+ }
+ }
+ } catch (CoreException e) {
+ DebugPlugin.log(e);
+ }
+ }
+
+ public static String getProp(int index, String string) {
+ return GroupLaunchConfigurationDelegate.MULTI_LAUNCH_CONSTANTS_PREFIX + "." + index + "." + string; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+}
diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunchElement.java b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunchElement.java
new file mode 100644
index 000000000..310a8d368
--- /dev/null
+++ b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunchElement.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * 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.core.groups;
+
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.internal.core.DebugCoreMessages;
+
+/**
+ * Represents a single child element of a launch group.
+ *
+ * @since 3.11
+ */
+public class GroupLaunchElement {
+ public static final String MODE_INHERIT = "inherit"; //$NON-NLS-1$
+
+ /**
+ * Describes the possible post-launch actions for each
+ * {@link GroupLaunchElement}.
+ * <p>
+ * These actions get performed after the associated
+ * {@link GroupLaunchElement} has been launched, before the next one is
+ * launched (or launching is finished).
+ */
+ public static enum GroupElementPostLaunchAction {
+ NONE(DebugCoreMessages.GroupLaunchConfigurationDelegate_None), //
+ WAIT_FOR_TERMINATION(DebugCoreMessages.GroupLaunchConfigurationDelegate_Wait_until_terminated), //
+ DELAY(DebugCoreMessages.GroupLaunchConfigurationDelegate_Delay);
+
+ private final String description;
+
+ private GroupElementPostLaunchAction(String description) {
+ this.description = description;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public static GroupLaunchElement.GroupElementPostLaunchAction valueOfDescription(String desc) {
+ for (GroupLaunchElement.GroupElementPostLaunchAction e : values()) {
+ if (e.description.equals(desc)) {
+ return e;
+ }
+ }
+ return NONE;
+ }
+ }
+
+ public int index;
+ public boolean enabled = true;
+ public String mode = MODE_INHERIT;
+ public GroupLaunchElement.GroupElementPostLaunchAction action = GroupElementPostLaunchAction.NONE;
+ public Object actionParam;
+ public String name;
+ public ILaunchConfiguration data;
+} \ No newline at end of file
diff --git a/org.eclipse.debug.core/plugin.properties b/org.eclipse.debug.core/plugin.properties
index 18642a943..92558a352 100644
--- a/org.eclipse.debug.core/plugin.properties
+++ b/org.eclipse.debug.core/plugin.properties
@@ -69,3 +69,6 @@ current_date.description=Returns the current system time formatted as yyyyMMdd_H
LineBreakpoint.name = Line Breakpoint
Breakpoint.name = Breakpoint
+
+GroupLaunch.name=Launch Group
+GroupLaunch.description=Launch several other configurations sequentially
diff --git a/org.eclipse.debug.core/plugin.xml b/org.eclipse.debug.core/plugin.xml
index 009a27b60..bd793e3f8 100644
--- a/org.eclipse.debug.core/plugin.xml
+++ b/org.eclipse.debug.core/plugin.xml
@@ -258,4 +258,23 @@
id="org.eclipse.debug.core.propertyTesters.launchable">
</propertyTester>
</extension>
+ <extension
+ point="org.eclipse.debug.core.launchDelegates">
+ <launchDelegate
+ delegate="org.eclipse.debug.internal.core.groups.GroupLaunchConfigurationDelegate"
+ delegateDescription="%GroupLaunch.description"
+ id="org.eclipse.debug.core.groups.GroupLaunchConfigurationDelegate"
+ modes="run,debug,profile"
+ name="%GroupLaunch.name"
+ type="org.eclipse.debug.core.groups.GroupLaunchConfigurationType">
+ </launchDelegate>
+ </extension>
+ <extension
+ point="org.eclipse.debug.core.launchConfigurationTypes">
+ <launchConfigurationType
+ id="org.eclipse.debug.core.groups.GroupLaunchConfigurationType"
+ name="%GroupLaunch.name"
+ public="true">
+ </launchConfigurationType>
+ </extension>
</plugin>
diff --git a/org.eclipse.debug.ui/META-INF/MANIFEST.MF b/org.eclipse.debug.ui/META-INF/MANIFEST.MF
index ec1a16b66..46ccbf586 100644
--- a/org.eclipse.debug.ui/META-INF/MANIFEST.MF
+++ b/org.eclipse.debug.ui/META-INF/MANIFEST.MF
@@ -29,6 +29,7 @@ Export-Package: org.eclipse.debug.internal.ui;
org.eclipse.debug.internal.ui.contextlaunching;x-internal:=true,
org.eclipse.debug.internal.ui.contexts;x-internal:=true,
org.eclipse.debug.internal.ui.elements.adapters;x-friends:="org.eclipse.jdt.debug.ui",
+ org.eclipse.debug.internal.ui.groups;x-friends:="org.eclipse.debug.ui",
org.eclipse.debug.internal.ui.importexport.breakpoints;x-internal:=true,
org.eclipse.debug.internal.ui.importexport.launchconfigurations;x-internal:=true,
org.eclipse.debug.internal.ui.launchConfigurations;x-friends:="org.eclipse.debug.tests",
diff --git a/org.eclipse.debug.ui/icons/full/obj16/lgroup_obj.png b/org.eclipse.debug.ui/icons/full/obj16/lgroup_obj.png
new file mode 100644
index 000000000..e393ca352
--- /dev/null
+++ b/org.eclipse.debug.ui/icons/full/obj16/lgroup_obj.png
Binary files differ
diff --git a/org.eclipse.debug.ui/icons/full/obj16/lgroup_obj@2x.png b/org.eclipse.debug.ui/icons/full/obj16/lgroup_obj@2x.png
new file mode 100644
index 000000000..d3c7238da
--- /dev/null
+++ b/org.eclipse.debug.ui/icons/full/obj16/lgroup_obj@2x.png
Binary files differ
diff --git a/org.eclipse.debug.ui/plugin.xml b/org.eclipse.debug.ui/plugin.xml
index d18ca4cbe..a2e624dc3 100644
--- a/org.eclipse.debug.ui/plugin.xml
+++ b/org.eclipse.debug.ui/plugin.xml
@@ -1660,7 +1660,31 @@
code="227"
id="org.eclipse.debug.ui.statusHandler.duplicateLaunchDelegatesDetected"
plugin="org.eclipse.debug.core">
- </statusHandler>
+ </statusHandler>
+ <statusHandler
+ class="org.eclipse.debug.internal.ui.groups.UnsupportedModeHandler"
+ code="230"
+ id="org.eclipse.debug.ui.groups.UnsupportedModeHandler"
+ plugin="org.eclipse.debug.core">
+ </statusHandler>
+ <statusHandler
+ class="org.eclipse.debug.internal.ui.groups.GroupElementLaunchedHandler"
+ code="231"
+ id="org.eclipse.debug.ui.groups.GroupElementLaunchedHandler"
+ plugin="org.eclipse.debug.core">
+ </statusHandler>
+ <statusHandler
+ class="org.eclipse.debug.internal.ui.groups.GroupCycleHandler"
+ code="232"
+ id="org.eclipse.debug.ui.groups.GroupCycleHandler"
+ plugin="org.eclipse.debug.core">
+ </statusHandler>
+ <statusHandler
+ class="org.eclipse.debug.internal.ui.groups.GroupLaunchHandler"
+ code="233"
+ id="org.eclipse.debug.ui.groups.GroupLaunchHandler"
+ plugin="org.eclipse.debug.core">
+ </statusHandler>
</extension>
<extension
point="org.eclipse.debug.ui.launchGroups">
@@ -3220,4 +3244,20 @@ M4 = Platform-specific fourth key
</themeid>
</stylesheet>
</extension>
+ <extension
+ point="org.eclipse.debug.ui.launchConfigurationTypeImages">
+ <launchConfigurationTypeImage
+ configTypeID="org.eclipse.debug.core.groups.GroupLaunchConfigurationType"
+ icon="icons/full/obj16/lgroup_obj.png"
+ id="org.eclipse.debug.ui.groups.GroupLaunchConfigurationTypeImage">
+ </launchConfigurationTypeImage>
+ </extension>
+ <extension
+ point="org.eclipse.debug.ui.launchConfigurationTabGroups">
+ <launchConfigurationTabGroup
+ class="org.eclipse.debug.internal.ui.groups.GroupLaunchConfigurationTabGroup"
+ id="org.eclipse.debug.ui.groups.GroupLaunchConfigurationTabGroup"
+ type="org.eclipse.debug.core.groups.GroupLaunchConfigurationType">
+ </launchConfigurationTabGroup>
+ </extension>
</plugin>
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;
+ }
+
+}

Back to the top