diff options
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 Binary files differnew file mode 100644 index 000000000..e393ca352 --- /dev/null +++ b/org.eclipse.debug.ui/icons/full/obj16/lgroup_obj.png 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 Binary files differnew file mode 100644 index 000000000..d3c7238da --- /dev/null +++ b/org.eclipse.debug.ui/icons/full/obj16/lgroup_obj@2x.png 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; + } + +} |