/******************************************************************************* * Copyright (c) 2000, 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 * Mohamed Hussein (Mentor Graphics) - Added getWarningMessage (Bug 386673) * Robert Roth - Used consistent apply button location (Bug 432832) *******************************************************************************/ package org.eclipse.debug.internal.ui.launchConfigurations; import java.lang.reflect.InvocationTargetException; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.ResourcesPlugin; 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.jobs.Job; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationType; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.core.ILaunchDelegate; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.internal.core.IInternalDebugCoreConstants; import org.eclipse.debug.internal.core.LaunchConfigurationWorkingCopy; import org.eclipse.debug.internal.core.LaunchManager; import org.eclipse.debug.internal.ui.DebugUIPlugin; import org.eclipse.debug.internal.ui.IInternalDebugUIConstants; import org.eclipse.debug.internal.ui.SWTFactory; import org.eclipse.debug.ui.DebugUITools; import org.eclipse.debug.ui.ILaunchConfigurationDialog; import org.eclipse.debug.ui.ILaunchConfigurationTab; import org.eclipse.debug.ui.ILaunchConfigurationTab2; import org.eclipse.debug.ui.ILaunchConfigurationTabGroup; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.resource.ColorRegistry; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.BusyIndicator; import org.eclipse.swt.custom.CTabFolder; import org.eclipse.swt.custom.CTabItem; import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.custom.StackLayout; import org.eclipse.swt.custom.ViewForm; 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.Color; import org.eclipse.swt.graphics.Font; 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.Label; import org.eclipse.swt.widgets.Link; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.IWorkbenchPreferenceConstants; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.progress.WorkbenchJob; import com.ibm.icu.text.MessageFormat; /** * A viewer that displays tabs for a launch configuration, with apply and revert * buttons. */ public class LaunchConfigurationTabGroupViewer { /** * Containing launch dialog */ private ILaunchConfigurationDialog fDialog; /** * The launch configuration (original) being edited */ private ILaunchConfiguration fOriginal; /** * The working copy of the original */ private ILaunchConfigurationWorkingCopy fWorkingCopy; /** * This view's control, which contains a composite area of controls */ private Composite fViewerControl; /** * Name text widget */ private Text fNameWidget; /** * Composite containing the launch config tab widgets */ private Composite fGroupComposite; /** * Tab folder */ private CTabFolder fTabFolder; /** * The current tab group being displayed */ private ILaunchConfigurationTabGroup fTabGroup; /** * Index of the active tab */ private int fCurrentTabIndex = -1; /** * Apply & Revert buttons */ private Button fApplyButton; private Button fRevertButton; /** * Whether tabs are currently being disposed or initialized */ private boolean fDisposingTabs = false; private boolean fInitializingTabs = false; /** * The description of the currently selected launch configuration or * launch configuration type or <code>null</code> if none. */ private String fDescription = null; /** * A place holder for switching between the tabs for a config and the getting started tab * @since 3.2 */ private Composite fTabPlaceHolder = null; /** * A link to allow users to select a valid set of launch options for the specified mode * @since 3.3 */ private Link fOptionsLink = null; /** * A label to indicate that the user needs to select an a launcher. * @since 3.5 */ private Label fOptionsErrorLabel = null; /** * A new composite replacing the perspectives tab * @since 3.2 */ private Composite fGettingStarted = null; private ViewForm fViewform; /** * Job to update the dialog after a delay. */ private Job fRefreshJob; /** * Constructs a viewer in the given composite, contained by the given * launch configuration dialog. * * @param parent composite containing this viewer * @param dialog containing launch configuration dialog */ public LaunchConfigurationTabGroupViewer(Composite parent, ILaunchConfigurationDialog dialog) { super(); fDialog = dialog; createControl(parent); } /** * Cleanup */ public void dispose() { disposeTabGroup(); } /** * Dispose the active tab group, if any. */ protected void disposeTabGroup() { if (fTabGroup != null) { fTabGroup.dispose(); fTabGroup = null; } } /** * Creates this viewer's control This area displays the name of the launch * configuration currently being edited, as well as a tab folder of tabs * that are applicable to the launch configuration. * @param parent the parent {@link Composite} * */ private void createControl(Composite parent) { fViewerControl = parent; fViewform = new ViewForm(parent, SWT.FLAT | SWT.BORDER); GridLayout layout = new GridLayout(1, false); layout.horizontalSpacing = 0; layout.verticalSpacing = 0; fViewform.setLayout(layout); GridData gd = new GridData(GridData.FILL_BOTH); fViewform.setLayoutData(gd); fViewform.setTopLeft(null); Composite mainComp = SWTFactory.createComposite(fViewform, fViewform.getFont(), 1, 1, 1, 0, 0); fViewform.setContent(mainComp); fTabPlaceHolder = SWTFactory.createComposite(mainComp, 1, 1, GridData.FILL_BOTH); fTabPlaceHolder.setLayout(new StackLayout()); fGettingStarted = SWTFactory.createComposite(fTabPlaceHolder, 1, 1, GridData.FILL_BOTH); createGettingStarted(fGettingStarted); fGroupComposite = SWTFactory.createComposite(fTabPlaceHolder, fTabPlaceHolder.getFont(), 2, 2, GridData.FILL_BOTH, 5, 5); SWTFactory.createLabel(fGroupComposite, LaunchConfigurationsMessages.LaunchConfigurationDialog__Name__16, 1); fNameWidget = new Text(fGroupComposite, SWT.SINGLE | SWT.BORDER); fNameWidget.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); fNameWidget.addModifyListener(new ModifyListener() { @Override public void modifyText(ModifyEvent e) { if(!fInitializingTabs) { handleNameModified(); } } } ); createTabFolder(fGroupComposite); Composite blComp = SWTFactory.createComposite(mainComp, mainComp.getFont(), 2, 1, GridData.FILL_HORIZONTAL); Composite linkComp = SWTFactory.createComposite(blComp, blComp.getFont(), 2, 1, GridData.FILL_HORIZONTAL); //a link for launch options fOptionsErrorLabel = new Label(linkComp, SWT.NONE); gd = new GridData(); fOptionsErrorLabel.setLayoutData(gd); fOptionsLink = new Link(linkComp, SWT.WRAP); fOptionsLink.setFont(linkComp.getFont()); gd = new GridData(SWT.LEFT); gd.grabExcessHorizontalSpace = true; fOptionsLink.setLayoutData(gd); fOptionsLink.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { //collect the options available try { if(!canLaunchWithModes()) { SelectLaunchModesDialog sld = new SelectLaunchModesDialog(getShell(), getLaunchConfigurationDialog().getMode(), getWorkingCopy()); if(sld.open() == IDialogConstants.OK_ID) { //set the options to the config Object[] res = sld.getResult(); if(res != null) { @SuppressWarnings("unchecked") Set<String> modes = (Set<String>) res[0]; modes.remove(getLaunchConfigurationDialog().getMode()); ILaunchConfigurationWorkingCopy wc = getWorkingCopy(); wc.setModes(modes); refreshStatus(); } } } else if(hasMultipleDelegates()) { SelectLaunchersDialog sldd = new SelectLaunchersDialog(getShell(), getWorkingCopy().getType().getDelegates(getCurrentModeSet()), getWorkingCopy(), getLaunchConfigurationDialog().getMode()); if(sldd.open() == IDialogConstants.OK_ID) { displayInstanceTabs(true); refreshStatus(); } } } catch (CoreException ex) {} } }); fOptionsLink.setVisible(false); Composite buttonComp = SWTFactory.createComposite(blComp, 2, 1, GridData.HORIZONTAL_ALIGN_END); fRevertButton = SWTFactory.createPushButton(buttonComp, LaunchConfigurationsMessages.LaunchConfigurationDialog_Revert_2, null, GridData.HORIZONTAL_ALIGN_END); fRevertButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent evt) { handleRevertPressed(); } }); fApplyButton = SWTFactory.createPushButton(buttonComp, LaunchConfigurationsMessages.LaunchConfigurationDialog__Apply_17, null,GridData.HORIZONTAL_ALIGN_END); fApplyButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent evt) { handleApplyPressed(); } }); Dialog.applyDialogFont(parent); } /** * Creates some help text for the tab group launch types * @param parent the parent composite * @since 3.2 */ private void createGettingStarted(Composite parent) { Font font = parent.getFont(); GridData gd = null; int width = parent.getBounds().width - 30; SWTFactory.createWrapLabel(parent, LaunchConfigurationsMessages.LaunchConfigurationTabGroupViewer_1, 1, width); SWTFactory.createWrapCLabel(parent, LaunchConfigurationsMessages.LaunchConfigurationTabGroupViewer_2, DebugUITools.getImage(IInternalDebugUIConstants.IMG_ELCL_NEW_CONFIG), 1, width); SWTFactory.createWrapCLabel(parent, LaunchConfigurationsMessages.LaunchConfigurationTabGroupViewer_6, DebugUITools.getImage(IInternalDebugUIConstants.IMG_ELCL_DUPLICATE_CONFIG), 1, width); SWTFactory.createWrapCLabel(parent, LaunchConfigurationsMessages.LaunchConfigurationTabGroupViewer_4, DebugUITools.getImage(IInternalDebugUIConstants.IMG_ELCL_DELETE_CONFIG), 1, width); SWTFactory.createWrapCLabel(parent, LaunchConfigurationsMessages.LaunchConfigurationTabGroupViewer_8, DebugUITools.getImage(IInternalDebugUIConstants.IMG_ELCL_FILTER_CONFIGS), 1, width); SWTFactory.createWrapCLabel(parent, LaunchConfigurationsMessages.LaunchConfigurationTabGroupViewer_3, DebugUITools.getImage(IInternalDebugUIConstants.IMG_OVR_TRANSPARENT), 1, width); SWTFactory.createHorizontalSpacer(parent, 2); Link link = new Link(parent, SWT.LEFT | SWT.WRAP); link.setText(LaunchConfigurationsMessages.LaunchConfigurationTabGroupViewer_5); link.setFont(font); gd = new GridData(GridData.FILL_HORIZONTAL); gd.widthHint = width; link.setLayoutData(gd); link.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { SWTFactory.showPreferencePage("org.eclipse.debug.ui.PerspectivePreferencePage"); //$NON-NLS-1$ } }); } /** * Creates the tab folder for displaying config instances * @param parent the parent {@link Composite} */ private void createTabFolder(Composite parent) { if (fTabFolder == null) { ColorRegistry reg = JFaceResources.getColorRegistry(); Color c1 = reg.get("org.eclipse.ui.workbench.ACTIVE_TAB_BG_START"), //$NON-NLS-1$ c2 = reg.get("org.eclipse.ui.workbench.ACTIVE_TAB_BG_END"); //$NON-NLS-1$ fTabFolder = new CTabFolder(parent, SWT.NO_REDRAW_RESIZE | SWT.FLAT); fTabFolder.setSelectionBackground(new Color[] {c1, c2}, new int[] {100}, true); fTabFolder.setSelectionForeground(reg.get("org.eclipse.ui.workbench.ACTIVE_TAB_TEXT_COLOR")); //$NON-NLS-1$ fTabFolder.setSimple(PlatformUI.getPreferenceStore().getBoolean(IWorkbenchPreferenceConstants.SHOW_TRADITIONAL_STYLE_TABS)); fTabFolder.setBorderVisible(true); GridData gd = new GridData(GridData.FILL_BOTH); gd.horizontalSpan = 2; fTabFolder.setLayoutData(gd); fTabFolder.setFont(parent.getFont()); fTabFolder.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent event) { if (!fInitializingTabs) { handleTabSelected(); refresh(); } } }); } } /** * Returns the apply button * @return the 'Apply' button */ protected Button getApplyButton() { return fApplyButton; } /** * Returns the revert button * @return the 'Revert' button */ protected Button getRevertButton() { return fRevertButton; } /** * Sets the current name * @param name the new name to set */ public void setName(String name) { if (getWorkingCopy() != null) { if (name == null) { fNameWidget.setText(IInternalDebugCoreConstants.EMPTY_STRING); } else { fNameWidget.setText(name.trim()); } refreshStatus(); } } /** * @return the backing viewer control */ public Control getControl() { return fViewerControl; } /** * Returns the shell this viewer is contained in. * @return the current dialog shell */ private Shell getShell() { return getControl().getShell(); } /** * Returns the current input to the viewer. Input will * be one of {@link ILaunchConfiguration} or {@link ILaunchConfigurationType} * * @return returns the current input */ public Object getInput() { return getConfiguration(); } /* (non-Javadoc) * @see org.eclipse.jface.viewers.Viewer#refresh() */ public void refresh() { if (fInitializingTabs) { return; } ILaunchConfigurationTab[] tabs = getTabs(); if (tabs != null) { // update the working copy from the active tab boolean newwc = !getWorkingCopy().isDirty(); ILaunchConfigurationTab tab = getActiveTab(); if (tab != null) { tab.performApply(getWorkingCopy()); } if((fOriginal instanceof ILaunchConfigurationWorkingCopy) && newwc) { try { ILaunchConfigurationWorkingCopy copy = getWorkingCopy(); if(copy != null) { copy.doSave(); } } catch (CoreException e) {DebugUIPlugin.log(e);} } updateButtons(); // update error ticks CTabItem item = null; boolean error = false; Image image = null; for (int i = 0; i < tabs.length; i++) { item = fTabFolder.getItem(i); image = tabs[i].getImage(); item.setImage(image); if(!tabs[i].isValid(getWorkingCopy())) { error = tabs[i].getErrorMessage() != null; if(error) { item.setImage(DebugUIPlugin.getDefault().getLaunchConfigurationManager().getErrorTabImage(tabs[i])); } } } showLink(); getLaunchConfigurationDialog().updateMessage(); } } /** * Shows the link for either multiple launch delegates or bad launch mode combinations * * @since 3.3 */ private void showLink() { String text = null; if(!canLaunchWithModes()) { text = LaunchConfigurationsMessages.LaunchConfigurationTabGroupViewer_13; } else if(hasMultipleDelegates()) { ILaunchDelegate delegate = getPreferredDelegate(); if(delegate != null) { String name = delegate.getName(); if(name == null) { text = LaunchConfigurationsMessages.LaunchConfigurationTabGroupViewer_15; } else { text = MessageFormat.format(LaunchConfigurationsMessages.LaunchConfigurationTabGroupViewer_16, new Object[] { name }); } } else { text = LaunchConfigurationsMessages.LaunchConfigurationTabGroupViewer_17; } } if(text != null) { fOptionsLink.setText(text); } fOptionsLink.setVisible(!canLaunchWithModes() || hasMultipleDelegates()); if (hasDuplicateDelegates()) { fOptionsErrorLabel.setImage(JFaceResources.getImage(Dialog.DLG_IMG_MESSAGE_ERROR)); } else { fOptionsErrorLabel.setImage(null); } fViewform.layout(true, true); } /** * Returns the preferred launch delegate for the current launch configuration and mode set * @return the preferred launch delegate * * @since 3.3 */ protected ILaunchDelegate getPreferredDelegate() { ILaunchDelegate preferred = null; ILaunchConfigurationWorkingCopy config = getWorkingCopy(); if(config != null) { try { Set<String> modes = getCurrentModeSet(); preferred = config.getPreferredDelegate(modes); if(preferred == null) { preferred = config.getType().getPreferredDelegate(modes); } } catch(CoreException ce) {DebugUIPlugin.log(ce);} } return preferred; } /** * Returns the listing of modes for the current config * @return the listing of modes for the current config * @since 3.3 */ private Set<String> getCurrentModeSet() { Set<String> set = new HashSet<String>(); ILaunchConfigurationWorkingCopy config = getWorkingCopy(); if(config != null) { try { set.addAll(config.getModes()); set.add(getLaunchConfigurationDialog().getMode()); } catch(CoreException ce) {DebugUIPlugin.log(ce);} } return set; } /** * @return returns the configuration input * * @since 3.6 */ ILaunchConfiguration getConfiguration() { if(fOriginal == null) { return getWorkingCopy(); } return fOriginal; } /** * updates the button states */ private void updateButtons() { boolean isDirty = isDirty(); fApplyButton.setEnabled(isDirty && canSave()); fRevertButton.setEnabled(isDirty); } /* (non-Javadoc) * @see org.eclipse.jface.viewers.Viewer#setInput(java.lang.Object) */ public void setInput(final Object input) { if(DebugUIPlugin.getStandardDisplay().getThread().equals(Thread.currentThread())) { setInput0(input); } else { DebugUIPlugin.getStandardDisplay().syncExec(new Runnable() { @Override public void run() { setInput0(input); } }); } } /** * Sets the input to the tab group viewer * @param input the new input * @since 3.3 */ private void setInput0(Object input) { if (input == null) { if (getConfiguration() == null) { return; } inputChanged(input); } else { if (!input.equals(getConfiguration())) { inputChanged(input); } } } /** * The input has changed to the given object, possibly <code>null</code>. * * @param input the new input, possibly <code>null</code> */ protected void inputChanged(Object input) { final Object finput = input; Runnable r = new Runnable() { @Override public void run() { try { fViewform.setRedraw(false); if (finput instanceof ILaunchConfiguration) { ILaunchConfiguration configuration = (ILaunchConfiguration)finput; boolean refreshtabs = !delegatesEqual(fWorkingCopy, configuration); fOriginal = configuration; fWorkingCopy = configuration.getWorkingCopy(); displayInstanceTabs(refreshtabs); } else if (finput instanceof ILaunchConfigurationType) { fDescription = getDescription((ILaunchConfigurationType)finput); setNoInput(); } else { setNoInput(); } } catch (CoreException ce) { errorDialog(ce); setNoInput(); } finally { refreshStatus(); fViewform.setRedraw(true); } } }; BusyIndicator.showWhile(getShell().getDisplay(), r); } /** * Sets the tab group viewer to have no input, this is the case when null is passed as an input type * Setting no input is equivalent to resetting all items, clearing any messages and showing the 'getting started' pane * @since 3.2 */ private void setNoInput() { fOriginal = null; fWorkingCopy = null; disposeExistingTabs(); updateButtons(); updateVisibleControls(false); ILaunchConfigurationDialog lcd = getLaunchConfigurationDialog(); if(lcd instanceof LaunchConfigurationsDialog) { if(((LaunchConfigurationsDialog)lcd).isTreeSelectionEmpty()) { fDescription = IInternalDebugCoreConstants.EMPTY_STRING; } } } /** * Returns if the two configurations are using the same <code>ILaunchDelegate</code> or not * @param config1 the config to compare to * @param config2 the config to compare * @return true if the configurations are using the same <code>ILaunchDelegate</code> or false if they are not * @since 3.3 */ protected boolean delegatesEqual(ILaunchConfiguration config1, ILaunchConfiguration config2) { try { if(config1 == null || config2 == null) { return false; } if (config1.getType().equals(config2.getType())) { Set<String> modes = getCurrentModeSet(); ILaunchDelegate d1 = config1.getPreferredDelegate(modes); if(d1 == null) { d1 = config1.getType().getPreferredDelegate(modes); } ILaunchDelegate d2 = config2.getPreferredDelegate(modes); if(d2 == null) { d2 = config2.getType().getPreferredDelegate(modes); } if (d1 == null) { return d2 == null; } else { return d1.equals(d2); } } } catch(CoreException ce) {DebugUIPlugin.log(ce);} return false; } /** * Updates the visibility of controls based on the status provided * @param visible the visibility status to be applied to the controls */ private void updateVisibleControls(boolean visible) { fApplyButton.setVisible(visible); fRevertButton.setVisible(visible); fOptionsLink.setVisible(visible); if(visible) { ((StackLayout)fTabPlaceHolder.getLayout()).topControl = fGroupComposite; } else { ((StackLayout)fTabPlaceHolder.getLayout()).topControl = fGettingStarted; } fTabPlaceHolder.layout(true, true); } /** * sets the current widget focus to the 'Name' widget */ protected void setFocusOnName() { fNameWidget.setFocus(); } /** * Displays tabs for the current working copy * @param redrawTabs if the tabs should be redrawn */ protected void displayInstanceTabs(boolean redrawTabs) { // Turn on initializing flag to ignore message updates fInitializingTabs = true; ILaunchConfigurationType type = null; try { type = getWorkingCopy().getType(); } catch (CoreException e) { errorDialog(e); fInitializingTabs = false; return; } if(redrawTabs) { showInstanceTabsFor(type); } // show the name area updateVisibleControls(true); // Retrieve the current tab group. If there is none, clean up and leave ILaunchConfigurationTabGroup tabGroup = getTabGroup(); if (tabGroup == null) { IStatus status = new Status(IStatus.ERROR, DebugUIPlugin.getUniqueIdentifier(), 0, MessageFormat.format(LaunchConfigurationsMessages.LaunchConfigurationTabGroupViewer_No_tabs_defined_for_launch_configuration_type__0__1, new Object[] { type.getName() }), null); CoreException e = new CoreException(status); errorDialog(e); fInitializingTabs = false; return; } // Update the tabs with the new working copy tabGroup.initializeFrom(getWorkingCopy()); // Update the name field fNameWidget.setText(getWorkingCopy().getName()); fCurrentTabIndex = fTabFolder.getSelectionIndex(); // Turn off initializing flag to update message fInitializingTabs = false; if (!fViewform.isVisible()) { fViewform.setVisible(true); } } /** * Populate the tabs in the configuration edit area to be appropriate to the current * launch configuration type. * @param configType the type to show tabs for */ private void showInstanceTabsFor(ILaunchConfigurationType configType) { // try to keep on same tab Class<? extends ILaunchConfigurationTab> tabKind = null; if (getActiveTab() != null) { tabKind = getActiveTab().getClass(); } // Build the new tabs ILaunchConfigurationTabGroup group = null; try { group = createGroup(); } catch (CoreException ce) { DebugUIPlugin.errorDialog(getShell(), LaunchConfigurationsMessages.LaunchConfigurationDialog_Error_19, LaunchConfigurationsMessages.LaunchConfigurationDialog_Exception_occurred_creating_launch_configuration_tabs_27,ce); // return; } disposeExistingTabs(); fTabGroup = group; ILaunchConfigurationTab[] tabs = getTabs(); CTabItem tab = null; String name = IInternalDebugCoreConstants.EMPTY_STRING; Control control = null; for (int i = 0; i < tabs.length; i++) { tab = new CTabItem(fTabFolder, SWT.BORDER); name = tabs[i].getName(); if (name == null) { name = LaunchConfigurationsMessages.LaunchConfigurationDialog_unspecified_28; } tab.setText(name); tab.setImage(tabs[i].getImage()); ScrolledComposite sc = new ScrolledComposite(tab.getParent(), SWT.V_SCROLL | SWT.H_SCROLL); sc.setFont(tab.getParent().getFont()); sc.setExpandHorizontal(true); sc.setExpandVertical(true); sc.setShowFocusedControl(true); tabs[i].createControl(sc); control = tabs[i].getControl(); if (control != null) { sc.setContent(control); sc.setMinSize(control.computeSize(SWT.DEFAULT, SWT.DEFAULT)); tab.setControl(control.getParent()); } } //set the default tab as the first one if (tabs.length > 0) { setActiveTab(tabs[0]); } // select same tab as before, if possible for (int i = 0; i < tabs.length; i++) { if (tabs[i].getClass().equals(tabKind)) { setActiveTab(tabs[i]); break; } } fDescription = getDescription(configType); } /** * Returns the description of the given configuration type * in the current mode or <code>null</code> if none. * * @param configType the config type * @return the description of the given configuration type or <code>null</code> */ private String getDescription(ILaunchConfigurationType configType) { String description = null; if(configType != null) { String mode = fDialog.getMode(); description = LaunchConfigurationPresentationManager.getDefault().getDescription(configType, mode); } if (description == null) { description = IInternalDebugCoreConstants.EMPTY_STRING; } return description; } /** * Returns tab group for the given type of launch configuration. * Tabs are initialized to be contained in this dialog. * @return the new {@link ILaunchConfigurationTabGroup} * * @exception CoreException if unable to instantiate a tab group */ protected ILaunchConfigurationTabGroup createGroup() throws CoreException { // Use a final Object array to store the tab group and any exception that // results from the Runnable final Object[] finalArray = new Object[2]; Runnable runnable = new Runnable() { @Override public void run() { ILaunchConfigurationTabGroup tabGroup = null; try { tabGroup = LaunchConfigurationPresentationManager.getDefault().getTabGroup(getWorkingCopy(), getLaunchConfigurationDialog().getMode()); finalArray[0] = tabGroup; } catch (CoreException ce) { finalArray[1] = ce; return; } tabGroup.createTabs(getLaunchConfigurationDialog(), getLaunchConfigurationDialog().getMode()); ILaunchConfigurationTab[] tabs = tabGroup.getTabs(); for (int i = 0; i < tabs.length; i++) { tabs[i].setLaunchConfigurationDialog(getLaunchConfigurationDialog()); } } }; // Creating the tabs can result in plug-in loading, so we show the busy cursor BusyIndicator.showWhile(getControl().getDisplay(), runnable); // Re-throw any CoreException if there was one if (finalArray[1] != null) { throw (CoreException)finalArray[1]; } // Otherwise return the tab group return (ILaunchConfigurationTabGroup)finalArray[0]; } /** * Returns the tabs currently being displayed, or * <code>null</code> if none. * * @return currently displayed tabs, or <code>null</code> */ public ILaunchConfigurationTab[] getTabs() { if (getTabGroup() != null) { return getTabGroup().getTabs(); } return null; } /** * Returns the currently active <code>ILaunchConfigurationTab</code> * being displayed, or <code>null</code> if there is none. * * @return currently active <code>ILaunchConfigurationTab</code>, or <code>null</code>. */ public ILaunchConfigurationTab getActiveTab() { ILaunchConfigurationTab[] tabs = getTabs(); if (fTabFolder != null && tabs != null) { int pageIndex = fTabFolder.getSelectionIndex(); if (pageIndex >= 0) { return tabs[pageIndex]; } } return null; } /** * Returns whether the launch configuration being edited is dirty (i.e. * needs saving) * * @return whether the launch configuration being edited needs saving */ public boolean isDirty() { ILaunchConfigurationWorkingCopy workingCopy = getWorkingCopy(); if (workingCopy == null) { return false; } if(workingCopy.getParent() != null) { return !workingCopy.getParent().contentsEqual(workingCopy); } // Working copy hasn't been saved if (workingCopy.getOriginal() == null) { return true; } return fOriginal != null && !fOriginal.contentsEqual(workingCopy); } /** * Returns the job to update the launch configuration dialog. * * @return update job */ private Job getUpdateJob() { if (fRefreshJob == null) { fRefreshJob = createUpdateJob(); fRefreshJob.setSystem(true); } return fRefreshJob; } /** * Schedules the update job to run for this tab based on this tab's delay. * * @since 3.6 */ protected void scheduleUpdateJob() { Job job = getUpdateJob(); job.cancel(); // cancel existing job job.schedule(getUpdateJobDelay()); } /** * Return the time delay that should be used when scheduling the * update job. Subclasses may override. * * @return a time delay in milliseconds before the job should run * @since 3.6 */ protected long getUpdateJobDelay() { return 200; } /** * Creates and returns a job used to update the launch configuration dialog * for this tab. Subclasses may override. * * @return job to update the launch dialog for this tab * @since 3.6 */ protected Job createUpdateJob() { return new WorkbenchJob(getControl().getDisplay(), "Update LCD") { //$NON-NLS-1$ @Override public IStatus runInUIThread(IProgressMonitor monitor) { if (!getControl().isDisposed()) { refreshStatus(); } return Status.OK_STATUS; } @Override public boolean shouldRun() { return !getControl().isDisposed(); } }; } /** * Update apply & revert buttons, as well as buttons and message on the * launch config dialog. */ protected void refreshStatus() { if (!fInitializingTabs) { LaunchConfigurationsDialog lcd = (LaunchConfigurationsDialog) getLaunchConfigurationDialog(); lcd.refreshStatus(); } } /** * Returns the containing launch dialog * @return the current {@link ILaunchConfigurationDialog} */ protected ILaunchConfigurationDialog getLaunchConfigurationDialog() { return fDialog; } /** * Returns the original launch configuration being edited, possibly * <code>null</code>. * * @return ILaunchConfiguration */ protected ILaunchConfiguration getOriginal() { return fOriginal; } /** * Returns the working copy used to edit the original, possibly * <code>null</code>. * @return the backing {@link ILaunchConfigurationWorkingCopy} */ protected ILaunchConfigurationWorkingCopy getWorkingCopy() { return fWorkingCopy; } /** * Return whether the current configuration can be saved. * <p> * Note this is NOT the same thing as the config simply being valid. It * is possible to save a config that does not validate. This method * determines whether the config can be saved without causing a serious * error. For example, a shared config that has no specified location would * cause this method to return <code>false</code>. * </p> * @return if the dialog can save in its current state */ public boolean canSave() { if (fInitializingTabs) { return false; } // First make sure that name doesn't prevent saving the config try { verifyName(); } catch (CoreException ce) { return false; } // Next, make sure none of the tabs object to saving the config ILaunchConfigurationTab[] tabs = getTabs(); if (tabs == null) { return false; } for (int i = 0; i < tabs.length; i++) { if (!tabs[i].canSave()) { return false; } } return true; } /** * @return if the dialog can launch in its current state */ public boolean canLaunch() { if(fInitializingTabs) { return false; } if (getWorkingCopy() == null) { return false; } try { verifyName(); } catch (CoreException e) { return false; } ILaunchConfigurationTab[] tabs = getTabs(); if (tabs == null) { return false; } for (int i = 0; i < tabs.length; i++) { if (!tabs[i].isValid(getWorkingCopy())) { return false; } } return true; } /** * Determines if the tab groups that is currently visible can launch with the currently selected * set of options. * * @return true if the dialog can launch with the given set of modes, false otherwise * * @since 3.3 */ public boolean canLaunchWithModes() { if(fInitializingTabs) { return false; } //check if selected options exist and that the selected combination can be launched try { ILaunchConfigurationWorkingCopy wc = getWorkingCopy(); if(wc != null) { return wc.getType().supportsModeCombination(getCurrentModeSet()); } } catch (CoreException e) { } return true; } /** * Returns if the type currently showing in the tab group viewer has duplicate launch delegates for the given set of modes. * * The given set of modes comprises the current mode that the launch dialog was opened in as well as any modes that have been set on the launch * configuration. * @return the true if there are duplicates, false otherwise * * @since 3.3 */ public boolean hasDuplicateDelegates() { if(fInitializingTabs) { return false; } ILaunchConfiguration config = getWorkingCopy(); if(config != null) { if(hasMultipleDelegates()) { return getPreferredDelegate() == null; } } return false; } /** * Determines if the currently showing launch configuration has multiple launch delegates for the same mode set, but does not care * if there has been a default selected yet or not * @return true if the current launch configuration has multiple launch delegates, false otherwise */ private boolean hasMultipleDelegates() { ILaunchConfiguration config = getWorkingCopy(); if(config != null) { try { Set<String> modes = getCurrentModeSet(); ILaunchDelegate[] delegates = LaunchConfigurationManager.filterLaunchDelegates(config.getType(), modes); return delegates.length > 1; } catch (CoreException ce) {DebugUIPlugin.log(ce);} } return false; } /** * Returns the current error message or <code>null</code> if none. * @return the error message for the tab */ public String getErrorMesssage() { if (fInitializingTabs) { return null; } if (getWorkingCopy() == null) { return null; } try { verifyName(); } catch (CoreException ce) { return ce.getStatus().getMessage(); } if(hasDuplicateDelegates()) { return LaunchConfigurationsMessages.LaunchConfigurationTabGroupViewer_18; } String message = null; ILaunchConfigurationTab activeTab = getActiveTab(); if (activeTab == null) { return null; } message = activeTab.getErrorMessage(); if (message != null) { return message; } ILaunchConfigurationTab[] allTabs = getTabs(); for (int i = 0; i < allTabs.length; i++) { ILaunchConfigurationTab tab = allTabs[i]; if (tab == activeTab) { continue; } message = tab.getErrorMessage(); if (message != null) { StringBuffer temp= new StringBuffer(); temp.append('['); temp.append(DebugUIPlugin.removeAccelerators(tab.getName())); temp.append("]: "); //$NON-NLS-1$ temp.append(message); return temp.toString(); } } if(!canLaunchWithModes()) { Set<String> modes = getCurrentModeSet(); List<String> names = LaunchConfigurationPresentationManager.getDefault().getLaunchModeNames(modes); return MessageFormat.format(LaunchConfigurationsMessages.LaunchConfigurationTabGroupViewer_14, new Object[] { names.toString() }); } return null; } /** * Returns the current message or <code>null</code> if none. * @return Returns an appropriate message for display to user. The message returned will be: * The message defined by the visible tab, * or The tab group description for the particular launch mode, * or The generic tab group description, * or <code>null</code> if no message is defined */ public String getMessage() { if (fInitializingTabs) { return null; } String message = fDescription; ILaunchConfigurationTab tab = getActiveTab(); if (tab != null) { String tabMessage = tab.getMessage(); if (tabMessage != null) { message = tabMessage; } } return message; } /** * Returns the current warning message or <code>null</code> if none. * @return Returns an appropriate warning message for display to user. The message returned will be: * The warning message defined by the visible tab * or <code>null</code> if no message is defined */ public String getWarningMessage() { if (fInitializingTabs) { return null; } String message = null; ILaunchConfigurationTab tab = getActiveTab(); if (tab instanceof ILaunchConfigurationTab2) { String tabMessage = ((ILaunchConfigurationTab2)tab).getWarningMessage(); if (tabMessage != null) { message = tabMessage; } } return message; } /** * Verify that the launch configuration name is valid. * @throws CoreException if a name conflict occurs */ protected void verifyName() throws CoreException { if (fNameWidget.isVisible()) { ILaunchManager mgr = DebugPlugin.getDefault().getLaunchManager(); String currentName = fNameWidget.getText().trim(); // If there is no name, complain if (currentName.length() < 1) { throw new CoreException(new Status(IStatus.ERROR, DebugUIPlugin.getUniqueIdentifier(), 0, LaunchConfigurationsMessages.LaunchConfigurationDialog_Name_required_for_launch_configuration_11, null)); } try { mgr.isValidLaunchConfigurationName(currentName); } catch(IllegalArgumentException iae) { throw new CoreException(new Status(IStatus.ERROR, DebugUIPlugin.getUniqueIdentifier(), 0, iae.getMessage(), null)); } // Otherwise, if there's already a config with the same name, complain if (fOriginal != null && !fOriginal.getName().equals(currentName)) { Set<String> reservednames = ((LaunchConfigurationsDialog) getLaunchConfigurationDialog()).getReservedNameSet(); if (mgr.isExistingLaunchConfigurationName(currentName) || (reservednames != null ? reservednames.contains(currentName) : false)) { ILaunchConfiguration config = ((LaunchManager)mgr).findLaunchConfiguration(currentName); // config can be null if name matches to unsaved // configuration created in Properties dialog String configTypeName = config != null ? config.getType().getName() : fOriginal.getType().getName(); throw new CoreException(new Status(IStatus.ERROR, DebugUIPlugin.getUniqueIdentifier(), 0, NLS.bind(LaunchConfigurationsMessages.LaunchConfigurationDialog_Launch_configuration_already_exists_with_this_name_12, configTypeName), null)); } } } } /** * Remove the existing tabs that are showing */ private void disposeExistingTabs() { fDisposingTabs = true; fTabFolder.dispose(); fTabFolder = null; createTabFolder(fGroupComposite); disposeTabGroup(); fDisposingTabs = false; } /** * Returns the current tab group * * @return the current tab group, or <code>null</code> if none */ public ILaunchConfigurationTabGroup getTabGroup() { return fTabGroup; } /** * Notification that a tab has been selected * * Disallow tab changing when the current tab is invalid. * Update the config from the tab being left, and refresh * the tab being entered. */ protected void handleTabSelected() { if (fDisposingTabs || fInitializingTabs) { return; } ILaunchConfigurationTab[] tabs = getTabs(); if (fCurrentTabIndex == fTabFolder.getSelectionIndex() || tabs == null || tabs.length == 0 || fCurrentTabIndex > (tabs.length - 1)) { return; } if (fCurrentTabIndex != -1) { ILaunchConfigurationTab tab = tabs[fCurrentTabIndex]; ILaunchConfigurationWorkingCopy wc = getWorkingCopy(); if (wc != null) { tab.deactivated(wc); getActiveTab().activated(wc); } } fCurrentTabIndex = fTabFolder.getSelectionIndex(); } /** * Notification the name field has been modified */ protected void handleNameModified() { getWorkingCopy().rename(fNameWidget.getText().trim()); scheduleUpdateJob(); } /** * Notification that the 'Apply' button has been pressed. * * @return the saved launch configuration or <code>null</code> if not saved */ protected ILaunchConfiguration handleApplyPressed() { if(fOriginal != null && fOriginal.isReadOnly()) { IStatus status = ResourcesPlugin.getWorkspace().validateEdit(new IFile[] {fOriginal.getFile()}, fViewerControl.getShell()); if(!status.isOK()) { return null; } } Exception exception = null; final ILaunchConfiguration[] saved = new ILaunchConfiguration[1]; try { // update launch config fInitializingTabs = true; // trim name String trimmed = fNameWidget.getText().trim(); fNameWidget.setText(trimmed); if(fWorkingCopy == null) { fWorkingCopy = fOriginal.getWorkingCopy(); } fWorkingCopy.rename(trimmed); getTabGroup().performApply(fWorkingCopy); if (isDirty()) { if(!fWorkingCopy.isLocal()) { IRunnableWithProgress runnable = new IRunnableWithProgress() { @Override public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { try { saved[0] = ((LaunchConfigurationWorkingCopy)fWorkingCopy).doSave(monitor); } catch (CoreException e) {DebugUIPlugin.log(e);} } }; getLaunchConfigurationDialog().run(true, false, runnable); } else { saved[0] = fWorkingCopy.doSave(); } } updateButtons(); fInitializingTabs = false; } catch (CoreException e) {exception = e;} catch (InvocationTargetException e) {exception = e;} catch (InterruptedException e) {exception = e;} if(exception != null) { DebugUIPlugin.errorDialog(getShell(), LaunchConfigurationsMessages.LaunchConfigurationDialog_Launch_Configuration_Error_46, LaunchConfigurationsMessages.LaunchConfigurationDialog_Exception_occurred_while_saving_launch_configuration_47, exception); // return null; } else { return saved[0]; } } /** * Notification that the 'Revert' button has been pressed */ protected void handleRevertPressed() { try { if(fTabGroup != null) { fTabGroup.initializeFrom(fOriginal); fNameWidget.setText(fOriginal.getName()); fWorkingCopy = fOriginal.getWorkingCopy(); refreshStatus(); } } catch (CoreException e) {DebugUIPlugin.log(e);} } /** * Show an error dialog on the given exception. * * @param exception the exception to display */ protected void errorDialog(CoreException exception) { ErrorDialog.openError(getShell(), null, null, exception.getStatus()); } /** * Sets the displayed tab to the given tab. Has no effect if the specified * tab is not one of the tabs being displayed in the dialog currently. * * @param tab the tab to display/activate */ public void setActiveTab(ILaunchConfigurationTab tab) { ILaunchConfigurationTab[] tabs = getTabs(); if(tabs != null) { for (int i = 0; i < tabs.length; i++) { if (tabs[i].getClass().equals(tab.getClass())) { setActiveTab(i); return; } } } } /** * Sets the displayed tab to the tab with the given index. Has no effect if * the specified index is not within the limits of the tabs returned by * <code>getTabs()</code>. * * @param index the index of the tab to display */ public void setActiveTab(int index) { ILaunchConfigurationTab[] tabs = getTabs(); if (index >= 0 && index < tabs.length) { fTabFolder.setSelection(index); handleTabSelected(); } } }