org.eclipse.debug.internal.ui.launchConfigurations.LaunchConfigurationTabGroupViewer.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.debug.internal.ui.launchConfigurations.LaunchConfigurationTabGroupViewer.java

Source

/*******************************************************************************
 * Copyright (c) 2000, 2010 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
 *******************************************************************************/
package org.eclipse.debug.internal.ui.launchConfigurations;

import com.ibm.icu.text.MessageFormat;

import java.lang.reflect.InvocationTargetException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

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.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;

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.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.ui.IWorkbenchPreferenceConstants;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.progress.WorkbenchJob;

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.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.ILaunchConfigurationTabGroup;

/**
 * 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.
     *
     * @return the composite used for launch configuration editing
     */
    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() {
            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() {
            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) {
                                Set modes = (Set) 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);
        fApplyButton = SWTFactory.createPushButton(buttonComp,
                LaunchConfigurationsMessages.LaunchConfigurationDialog__Apply_17, null,
                GridData.HORIZONTAL_ALIGN_END);
        fApplyButton.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent evt) {
                handleApplyPressed();
            }
        });

        fRevertButton = SWTFactory.createPushButton(buttonComp,
                LaunchConfigurationsMessages.LaunchConfigurationDialog_Revert_2, null,
                GridData.HORIZONTAL_ALIGN_END);
        fRevertButton.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent evt) {
                handleRevertPressed();
            }
        });
        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() {
            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
     */
    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() {
                public void widgetSelected(SelectionEvent event) {
                    if (!fInitializingTabs) {
                        handleTabSelected();
                        refresh();
                    }
                }
            });
        }
    }

    /**
     * Returns the apply button
     */
    protected Button getApplyButton() {
        return fApplyButton;
    }

    /**
     * Returns the revert button
     */
    protected Button getRevertButton() {
        return fRevertButton;
    }

    /**
     * Sets the current name
     */
    public void setName(String name) {
        if (getWorkingCopy() != null) {
            if (name == null) {
                fNameWidget.setText(IInternalDebugCoreConstants.EMPTY_STRING);
            } else {
                fNameWidget.setText(name.trim());
            }
            refreshStatus();
        }
    }

    /**
     * @see org.eclipse.jface.viewers.Viewer#getControl()
     */
    public Control getControl() {
        return fViewerControl;
    }

    /**
     * Returns the shell this viewer is contained in.
     */
    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 String[] { 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 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 getCurrentModeSet() {
        Set set = new HashSet();
        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 dirty = isDirty() && canSave();
        fApplyButton.setEnabled(dirty);
        fRevertButton.setEnabled(dirty);
    }

    /* (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() {
                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() {
            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
     * @param config2
     * @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 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
     */
    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 String[] { 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.
     */
    private void showInstanceTabsFor(ILaunchConfigurationType configType) {
        // try to keep on same tab
        Class 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.
     *
     * @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() {
            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$
            public IStatus runInUIThread(IProgressMonitor monitor) {
                if (!getControl().isDisposed()) {
                    refreshStatus();
                }
                return Status.OK_STATUS;
            }

            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
     */
    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>.
      */
    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>
     */
    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;
    }

    /**
     * @see ILaunchConfigurationDialog#canLaunch()
     */
    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 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.
     */
    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 modes = getCurrentModeSet();
            List names = LaunchConfigurationPresentationManager.getDefault().getLaunchModeNames(modes);
            return MessageFormat.format(LaunchConfigurationsMessages.LaunchConfigurationTabGroupViewer_14,
                    new String[] { 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;
    }

    /**
     * Verify that the launch configuration name is valid.
     */
    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 reservednames = ((LaunchConfigurationsDialog) getLaunchConfigurationDialog())
                        .getReservedNameSet();
                if (mgr.isExistingLaunchConfigurationName(currentName)
                        || (reservednames != null ? reservednames.contains(currentName) : false)) {
                    throw new CoreException(new Status(IStatus.ERROR, DebugUIPlugin.getUniqueIdentifier(), 0,
                            LaunchConfigurationsMessages.LaunchConfigurationDialog_Launch_configuration_already_exists_with_this_name_12,
                            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() {
                        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
     */
    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();
        }
    }

}