diff --git a/debug/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/launchConfigurations/LaunchConfigurationsDialog.java b/debug/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/launchConfigurations/LaunchConfigurationsDialog.java index a5386fae947..d7542db15a8 100644 --- a/debug/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/launchConfigurations/LaunchConfigurationsDialog.java +++ b/debug/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/launchConfigurations/LaunchConfigurationsDialog.java @@ -253,6 +253,23 @@ public static void setCurrentlyVisibleLaunchConfigurationDialog(ILaunchConfigura */ private boolean fSettingInput = false; + /** + * Set while an unsaved-changes prompt triggered by a tree selection change is + * pending or showing + */ + private boolean fHandlingSelectionChange; + + /** + * Whether a mouse button is currently pressed in the launch configuration tree. + */ + private boolean fTreeMouseDown; + + /** + * Work to run as soon as the mouse button is released in the launch + * configuration tree. + */ + private Runnable fRunAfterTreeMouseUp; + /** * Constructs a new launch configuration dialog on the given * parent shell. @@ -608,6 +625,23 @@ else if(status == IDialogConstants.NO_ID) { getLinkPrototypeAction().setConfirmationRequestor(requestor); getUnlinkPrototypeAction().setConfirmationRequestor(requestor); getResetWithPrototypeValuesAction().setConfirmationRequestor(requestor); + viewer.getControl().addListener(SWT.MouseDown, e -> { + // a new press means a pending prompt must not wait for a release we never saw + Runnable pending = fRunAfterTreeMouseUp; + fRunAfterTreeMouseUp = null; + if (pending != null) { + e.display.asyncExec(pending); + } + fTreeMouseDown = true; + }); + viewer.getControl().addListener(SWT.MouseUp, e -> { + fTreeMouseDown = false; + Runnable pending = fRunAfterTreeMouseUp; + fRunAfterTreeMouseUp = null; + if (pending != null) { + e.display.asyncExec(pending); + } + }); ((StructuredViewer) viewer).addPostSelectionChangedListener(event -> { handleLaunchConfigurationSelectionChanged(event); getNewAction().setEnabled(getNewAction().isEnabled()); @@ -1018,12 +1052,13 @@ protected void handleClosePressed() { * @param event selection changed event */ protected void handleLaunchConfigurationSelectionChanged(SelectionChangedEvent event) { + if (fHandlingSelectionChange) { + // a prompt for the previous change is pending/showing, or we are restoring the old selection + return; + } Object input = fTabViewer.getInput(); - Object newInput = null; IStructuredSelection selection = event.getStructuredSelection(); - if (selection.size() == 1) { - newInput = selection.getFirstElement(); - } + Object newInput = selection.size() == 1 ? selection.getFirstElement() : null; if (!isEqual(input, newInput)) { ILaunchConfiguration original = fTabViewer.getOriginal(); if (original != null && newInput == null && getLaunchManager().getMovedTo(original) != null) { @@ -1037,49 +1072,86 @@ protected void handleLaunchConfigurationSelectionChanged(SelectionChangedEvent e if (newInput instanceof ILaunchConfiguration) { renamed = getLaunchManager().getMovedFrom((ILaunchConfiguration)newInput) != null; } - try { - fSettingInput = true; - if (fTabViewer.canSave() && fTabViewer.isDirty() && !deleted && !renamed) { - if(fLaunchConfigurationView != null) { - fLaunchConfigurationView.setAutoSelect(false); - } - int ret = showUnsavedChangesDialog(); - if(ret == IDialogConstants.YES_ID) { - fTabViewer.handleApplyPressed(); - ILaunchConfigurationTab[] tabs = getTabs(); - if (tabs != null) { - for (ILaunchConfigurationTab tab : tabs) { - tab.postApply(); - } + if (fTabViewer.canSave() && fTabViewer.isDirty() && !deleted && !renamed) { + fHandlingSelectionChange = true; + Runnable prompt = () -> { + try { + Shell shell = getShell(); + if (shell != null && !shell.isDisposed()) { + promptAndSwitchInput(input, newInput); } - fTabViewer.setInput(newInput); - } - else if(ret == IDialogConstants.NO_ID) { - fTabViewer.handleRevertPressed(); - fTabViewer.setInput(newInput); - } - else { - fLaunchConfigurationView.getViewer().setSelection(new StructuredSelection(input)); + } finally { + fHandlingSelectionChange = false; } - fLaunchConfigurationView.setAutoSelect(true); + }; + if (fTreeMouseDown) { + fRunAfterTreeMouseUp = prompt; + } else { + prompt.run(); } - else { - fTabViewer.setInput(newInput); - if(fTabViewer.isDirty()) { - fTabViewer.handleApplyPressed(); - } + return; + } + try { + fSettingInput = true; + fTabViewer.setInput(newInput); + if(fTabViewer.isDirty()) { + fTabViewer.handleApplyPressed(); } } finally { fSettingInput = false; updateButtons(); updateMessage(); } - if(getShell() != null && getShell().isVisible()) { + Shell shell = getShell(); + if (shell != null && shell.isVisible()) { resize(); } } } + /** + * Asks how to handle unsaved changes before switching inputs, or restores the + * selection if cancelled. + * + * @param oldInput input currently shown in the tab viewer + * @param newInput newly selected input + */ + private void promptAndSwitchInput(Object oldInput, Object newInput) { + if (fLaunchConfigurationView != null) { + fLaunchConfigurationView.setAutoSelect(false); + } + try { + fSettingInput = true; + int ret = showUnsavedChangesDialog(); + if (ret == IDialogConstants.YES_ID) { + fTabViewer.handleApplyPressed(); + ILaunchConfigurationTab[] tabs = getTabs(); + if (tabs != null) { + for (ILaunchConfigurationTab tab : tabs) { + tab.postApply(); + } + } + fTabViewer.setInput(newInput); + } else if (ret == IDialogConstants.NO_ID) { + fTabViewer.handleRevertPressed(); + fTabViewer.setInput(newInput); + } else if (fLaunchConfigurationView != null && oldInput != null) { + fLaunchConfigurationView.getViewer().setSelection(new StructuredSelection(oldInput), true); + } + } finally { + fSettingInput = false; + if (fLaunchConfigurationView != null) { + fLaunchConfigurationView.setAutoSelect(true); + } + updateButtons(); + updateMessage(); + } + Shell shell = getShell(); + if (shell != null && !shell.isDisposed() && shell.isVisible()) { + resize(); + } + } + /** * Notification the 'launch' button has been pressed. Save and launch. */