/*******************************************************************************
* Copyright (c) 2008, 2016 QNX Software Systems and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* QNX Software Systems - Initial API and implementation
* Ken Ryall (Nokia) - https://bugs.eclipse.org/bugs/show_bug.cgi?id=118894
* IBM Corporation
* Ericsson
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.internal.ui.launching;
import java.util.Map;
import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
import org.eclipse.cdt.debug.ui.ICDebuggerPage;
import org.eclipse.cdt.debug.ui.ICDebuggerPageExtension;
import org.eclipse.cdt.debug.ui.ICDebuggerPageExtension.IContentChangeListener;
import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants;
import org.eclipse.cdt.dsf.gdb.IGdbDebugPreferenceConstants;
import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin;
import org.eclipse.cdt.dsf.gdb.launching.LaunchMessages;
import org.eclipse.cdt.dsf.gdb.service.SessionType;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.ui.ILaunchConfigurationTab;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.swt.SWT;
import org.eclipse.swt.accessibility.AccessibleAdapter;
import org.eclipse.swt.accessibility.AccessibleEvent;
import org.eclipse.swt.custom.ScrolledComposite;
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.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
public class CDebuggerTab extends CLaunchConfigurationTab {
/**
* Tab identifier used for ordering of tabs added using
* the <code>org.eclipse.debug.ui.launchConfigurationTabs</code>
* extension point.
*
* @since 2.0
*/
public static final String TAB_ID = "org.eclipse.cdt.dsf.gdb.launch.debuggerTab"; //$NON-NLS-1$
private final static String LOCAL_DEBUGGER_ID = "gdb";//$NON-NLS-1$
private final static String REMOTE_DEBUGGER_ID = "gdbserver";//$NON-NLS-1$
protected ILaunchConfiguration fLaunchConfiguration;
protected ILaunchConfigurationWorkingCopy fWorkingCopy;
protected String fDebuggerId;
// Dynamic Debugger UI widgets
protected ICDebuggerPage fDynamicTab;
protected Composite fDynamicTabHolder;
private boolean fInitDefaults;
private Combo fDCombo;
private boolean fIsInitializing;
protected boolean fAttachMode;
protected boolean fRemoteMode;
protected boolean fCoreMode;
protected Button fStopInMain;
protected Text fStopInMainSymbol;
private ScrolledComposite fContainer;
private Composite fContents;
private IContentChangeListener fContentListener = new IContentChangeListener() {
@Override
public void contentChanged() {
contentsChanged();
}
};
public CDebuggerTab(SessionType sessionType, boolean attach) {
if (sessionType == SessionType.REMOTE) {
fRemoteMode = true;
} else if (sessionType == SessionType.CORE) {
fCoreMode = true;
}
fAttachMode = attach;
}
@Override
public String getId() {
return TAB_ID;
}
@Override
public void createControl(Composite parent) {
fContainer = new ScrolledComposite(parent, SWT.V_SCROLL | SWT.H_SCROLL);
fContainer.setLayoutData(new GridData(GridData.FILL_BOTH));
fContainer.setLayout(new FillLayout());
fContainer.setExpandHorizontal(true);
fContainer.setExpandVertical(true);
fContents = new Composite(fContainer, SWT.NONE);
setControl(fContainer);
GdbUIPlugin.getDefault().getWorkbench().getHelpSystem().setHelp(getControl(),
ICDTLaunchHelpContextIds.LAUNCH_CONFIGURATION_DIALOG_DEBBUGER_TAB);
int numberOfColumns = fAttachMode ? 2 : 1;
GridLayout layout = new GridLayout(numberOfColumns, false);
fContents.setLayout(layout);
GridData gd = new GridData(GridData.BEGINNING, GridData.CENTER, true, false);
fContents.setLayoutData(gd);
if (fAttachMode) {
createDebuggerCombo(fContents);
}
createOptionsComposite(fContents);
createDebuggerGroup(fContents, 2);
fContainer.setContent(fContents);
}
protected void initDebuggerTypes(String selection) {
if (fAttachMode) {
setInitializeDefault(selection.isEmpty());
if (selection.isEmpty()) {
selection = LOCAL_DEBUGGER_ID;
}
loadDebuggerCombo(new String[] { LOCAL_DEBUGGER_ID, REMOTE_DEBUGGER_ID }, selection);
} else {
if (fRemoteMode) {
setDebuggerId(REMOTE_DEBUGGER_ID);
} else {
setDebuggerId(LOCAL_DEBUGGER_ID);
}
updateComboFromSelection();
}
}
@Override
public void setDefaults(ILaunchConfigurationWorkingCopy config) {
setLaunchConfigurationWorkingCopy(config);
ICDebuggerPage dynamicTab = getDynamicTab();
if (dynamicTab != null) {
dynamicTab.setDefaults(config);
setInitializeDefault(false);
}
if (fAttachMode && fRemoteMode) {
config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE,
IGDBLaunchConfigurationConstants.DEBUGGER_MODE_REMOTE_ATTACH);
config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_ID, REMOTE_DEBUGGER_ID);
} else if (fAttachMode) {
config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE,
ICDTLaunchConfigurationConstants.DEBUGGER_MODE_ATTACH);
config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_ID, LOCAL_DEBUGGER_ID);
} else if (fRemoteMode) {
config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE,
IGDBLaunchConfigurationConstants.DEBUGGER_MODE_REMOTE);
config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_ID, REMOTE_DEBUGGER_ID);
} else if (fCoreMode){
config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE,
ICDTLaunchConfigurationConstants.DEBUGGER_MODE_CORE);
config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_ID, LOCAL_DEBUGGER_ID);
} else {
config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE,
ICDTLaunchConfigurationConstants.DEBUGGER_MODE_RUN);
config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_ID, LOCAL_DEBUGGER_ID);
}
if (!fAttachMode && !fCoreMode) {
IPreferenceStore preferences = GdbUIPlugin.getDefault().getPreferenceStore();
config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN,
preferences.getBoolean(IGdbDebugPreferenceConstants.PREF_DEFAULT_STOP_AT_MAIN));
config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN_SYMBOL,
preferences.getString(IGdbDebugPreferenceConstants.PREF_DEFAULT_STOP_AT_MAIN_SYMBOL));
}
}
@Override
public void initializeFrom(ILaunchConfiguration config) {
setInitializing(true);
setLaunchConfiguration(config);
ICDebuggerPage dynamicTab = getDynamicTab();
if (dynamicTab != null) {
dynamicTab.initializeFrom(config);
}
try {
String id = config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_ID, ""); //$NON-NLS-1$
initDebuggerTypes(id);
initializeCommonControls(config);
} catch (CoreException e) {
}
setInitializing(false);
}
@Override
public void performApply(ILaunchConfigurationWorkingCopy config) {
if (getDebuggerId() != null) {
config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_ID, getDebuggerId());
ICDebuggerPage dynamicTab = getDynamicTab();
if (dynamicTab == null) {
config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_SPECIFIC_ATTRS_MAP,
(Map)null);
} else {
dynamicTab.performApply(config);
}
}
if (fAttachMode && fRemoteMode) {
config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE,
IGDBLaunchConfigurationConstants.DEBUGGER_MODE_REMOTE_ATTACH);
} else if (fAttachMode) {
config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE,
ICDTLaunchConfigurationConstants.DEBUGGER_MODE_ATTACH);
} else if (fRemoteMode) {
config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE,
IGDBLaunchConfigurationConstants.DEBUGGER_MODE_REMOTE);
} else if (fCoreMode){
config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE,
ICDTLaunchConfigurationConstants.DEBUGGER_MODE_CORE);
} else {
config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE,
ICDTLaunchConfigurationConstants.DEBUGGER_MODE_RUN);
}
if (!fAttachMode && !fCoreMode) {
config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN,
fStopInMain.getSelection());
config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN_SYMBOL,
fStopInMainSymbol.getText());
}
}
@Override
public boolean isValid(ILaunchConfiguration config) {
if (!validateDebuggerConfig(config)) {
return false;
}
if (fStopInMain != null && fStopInMainSymbol != null) {
// The "Stop on startup at" field must not be empty
String mainSymbol = fStopInMainSymbol.getText().trim();
if (fStopInMain.getSelection() && mainSymbol.length() == 0) {
setErrorMessage(LaunchMessages.getString("CDebuggerTab.Stop_on_startup_at_can_not_be_empty")); //$NON-NLS-1$
return false;
}
}
setErrorMessage(null);
setMessage(null);
if (getDebuggerId() == null) {
setErrorMessage(LaunchMessages.getString("AbstractCDebuggerTab.No_debugger_available")); //$NON-NLS-1$
return false;
}
ICDebuggerPage dynamicTab = getDynamicTab();
if (dynamicTab != null) {
return dynamicTab.isValid(config);
}
return true;
}
protected boolean validateDebuggerConfig(ILaunchConfiguration config) {
String debuggerType = getDebuggerId();
if (debuggerType == null) {
setErrorMessage(LaunchMessages.getString("CDebuggerTab.No_debugger_available")); //$NON-NLS-1$
return false;
}
// We do not validate platform and CPU compatibility to avoid accidentally disabling
// a valid configuration. It's much better to let an incompatible configuration through
// than to disable a valid one.
return true;
}
/**
* @see org.eclipse.debug.ui.AbstractLaunchConfigurationTab#updateLaunchConfigurationDialog()
*/
protected void update() {
if (!isInitializing()) {
super.updateLaunchConfigurationDialog();
}
}
protected void createOptionsComposite(Composite parent) {
Composite optionsComp = new Composite(parent, SWT.NONE);
int numberOfColumns = (fAttachMode) ? 1 : 3;
GridLayout layout = new GridLayout(numberOfColumns, false);
optionsComp.setLayout(layout);
optionsComp.setLayoutData(new GridData(GridData.BEGINNING, GridData.CENTER, true, false, 1, 1));
if (!fAttachMode && !fCoreMode) {
fStopInMain = createCheckButton(optionsComp, LaunchMessages.getString("CDebuggerTab.Stop_at_main_on_startup")); //$NON-NLS-1$
fStopInMain.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
fStopInMainSymbol.setEnabled(fStopInMain.getSelection());
update();
}
});
fStopInMainSymbol = new Text(optionsComp, SWT.SINGLE | SWT.BORDER);
final GridData gridData = new GridData(GridData.FILL, GridData.CENTER, false, false);
gridData.widthHint = 100;
fStopInMainSymbol.setLayoutData(gridData);
fStopInMainSymbol.addModifyListener(new ModifyListener() {
@Override
public void modifyText(ModifyEvent evt) {
update();
}
});
fStopInMainSymbol.getAccessible().addAccessibleListener(
new AccessibleAdapter() {
@Override
public void getName(AccessibleEvent e) {
e.result = LaunchMessages.getString("CDebuggerTab.Stop_at_main_on_startup"); //$NON-NLS-1$
}
}
);
}
}
@Override
protected Shell getShell() {
return super.getShell();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.debug.ui.ILaunchConfigurationTab#dispose()
*/
@Override
public void dispose() {
ICDebuggerPage debuggerPage = getDynamicTab();
if (debuggerPage != null)
debuggerPage.dispose();
super.dispose();
}
protected void initializeCommonControls(ILaunchConfiguration config) {
try {
if (!fAttachMode && !fCoreMode) {
IPreferenceStore preferences = GdbUIPlugin.getDefault().getPreferenceStore();
fStopInMain.setSelection(config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN,
preferences.getBoolean(IGdbDebugPreferenceConstants.PREF_DEFAULT_STOP_AT_MAIN)));
fStopInMainSymbol.setText(config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN_SYMBOL,
preferences.getString(IGdbDebugPreferenceConstants.PREF_DEFAULT_STOP_AT_MAIN_SYMBOL)));
fStopInMainSymbol.setEnabled(fStopInMain.getSelection());
} else if (fAttachMode) {
// In attach mode, figure out if we are doing a remote connect based on
// the currently chosen debugger.
fRemoteMode = getDebuggerId().equals(REMOTE_DEBUGGER_ID);
}
} catch (CoreException e) {
}
}
protected void setInitializeDefault(boolean init) {
fInitDefaults = init;
}
protected void contentsChanged() {
fContainer.setMinSize(fContents.computeSize(SWT.DEFAULT, SWT.DEFAULT));
}
protected void loadDynamicDebugArea() {
Composite dynamicTabHolder = getDynamicTabHolder();
// Dispose of any current child widgets in the tab holder area
Control[] children = dynamicTabHolder.getChildren();
for (int i = 0; i < children.length; i++) {
children[i].dispose();
}
String debuggerId = getIdForCurrentDebugger();
if (debuggerId == null) {
setDynamicTab(null);
} else {
if (debuggerId.equals(LOCAL_DEBUGGER_ID)) {
if (fCoreMode) {
setDynamicTab(new GdbCoreDebuggerPage());
} else {
setDynamicTab(new GdbDebuggerPage());
}
} else if (debuggerId.equals(REMOTE_DEBUGGER_ID)) {
setDynamicTab(new GdbServerDebuggerPage());
} else {
assert false : "Unknown debugger id"; //$NON-NLS-1$
}
}
setDebuggerId(debuggerId);
ICDebuggerPage debuggerPage = getDynamicTab();
if (debuggerPage == null) {
return;
}
// Ask the dynamic UI to create its Control
debuggerPage.setLaunchConfigurationDialog(getLaunchConfigurationDialog());
debuggerPage.createControl(dynamicTabHolder);
debuggerPage.getControl().setVisible(true);
dynamicTabHolder.layout(true);
contentsChanged();
}
protected void setDebuggerId(String id) {
fDebuggerId = id;
}
protected String getDebuggerId() {
return fDebuggerId;
}
protected ICDebuggerPage getDynamicTab() {
return fDynamicTab;
}
protected void setDynamicTab(ICDebuggerPage tab) {
if (fDynamicTab instanceof ICDebuggerPageExtension)
((ICDebuggerPageExtension) fDynamicTab).removeContentChangeListener(fContentListener);
fDynamicTab = tab;
if (fDynamicTab instanceof ICDebuggerPageExtension)
((ICDebuggerPageExtension) fDynamicTab).addContentChangeListener(fContentListener);
}
protected Composite getDynamicTabHolder() {
return fDynamicTabHolder;
}
protected void setDynamicTabHolder(Composite tabHolder) {
fDynamicTabHolder = tabHolder;
}
protected ILaunchConfigurationWorkingCopy getLaunchConfigurationWorkingCopy() {
return fWorkingCopy;
}
protected void setLaunchConfiguration(ILaunchConfiguration launchConfiguration) {
fLaunchConfiguration = launchConfiguration;
setLaunchConfigurationWorkingCopy(null);
}
protected ILaunchConfiguration getLaunchConfiguration() {
return fLaunchConfiguration;
}
protected void setLaunchConfigurationWorkingCopy(ILaunchConfigurationWorkingCopy workingCopy) {
fWorkingCopy = workingCopy;
}
/**
* Overridden here so that any error message in the dynamic UI gets
* returned.
*
* @see ILaunchConfigurationTab#getErrorMessage()
*/
@Override
public String getErrorMessage() {
ICDebuggerPage tab = getDynamicTab();
if (super.getErrorMessage() != null || tab == null) {
return super.getErrorMessage();
}
return tab.getErrorMessage();
}
/**
* Notification that the user changed the selection of the Debugger.
*/
protected void handleDebuggerChanged() {
loadDynamicDebugArea();
// Always set the newly created area with defaults
ILaunchConfigurationWorkingCopy wc = getLaunchConfigurationWorkingCopy();
if (getDynamicTab() == null) {
// Remove any debug-specific arguments from the configuration.
if (wc == null) {
if (getLaunchConfiguration().isWorkingCopy()) {
wc = (ILaunchConfigurationWorkingCopy) getLaunchConfiguration();
}
}
if (wc != null) {
wc.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_SPECIFIC_ATTRS_MAP,
(Map) null);
}
} else {
if (wc == null) {
try {
if (getLaunchConfiguration().isWorkingCopy()) {
setLaunchConfigurationWorkingCopy((ILaunchConfigurationWorkingCopy) getLaunchConfiguration());
} else {
setLaunchConfigurationWorkingCopy(getLaunchConfiguration().getWorkingCopy());
}
wc = getLaunchConfigurationWorkingCopy();
} catch (CoreException e) {
return;
}
}
if (initDefaults()) {
getDynamicTab().setDefaults(wc);
}
setInitializeDefault(false);
getDynamicTab().initializeFrom(wc);
}
}
@Override
public void activated(ILaunchConfigurationWorkingCopy workingCopy) {
ICDebuggerPage dynamicTab = getDynamicTab();
if (dynamicTab != null) {
dynamicTab.activated(workingCopy);
}
}
protected boolean initDefaults() {
return fInitDefaults;
}
@Override
public Image getImage() {
return LaunchImages.get(LaunchImages.IMG_VIEW_DEBUGGER_TAB);
}
@Override
public String getName() {
return LaunchMessages.getString("AbstractCDebuggerTab.Debugger"); //$NON-NLS-1$
}
protected void createDebuggerCombo(Composite parent) {
Composite comboComp = new Composite(parent, SWT.NONE);
GridLayout layout = new GridLayout(2, false);
comboComp.setLayout(layout);
GridData gd = new GridData(GridData.FILL_HORIZONTAL);
comboComp.setLayoutData(gd);
Label dlabel = new Label(comboComp, SWT.NONE);
dlabel.setText(LaunchMessages.getString("Launch.common.DebuggerColon")); //$NON-NLS-1$
fDCombo = new Combo(comboComp, SWT.READ_ONLY | SWT.DROP_DOWN);
fDCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
fDCombo.addSelectionListener(new SelectionListener() {
@Override
public void widgetSelected(SelectionEvent e) {
if (!isInitializing()) {
setInitializeDefault(true);
updateComboFromSelection();
}
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {
}
});
}
protected void loadDebuggerCombo(String[] names, String current) {
if (fDCombo == null) {
return;
}
fDCombo.removeAll();
int select = -1;
for (int i = 0; i < names.length; i++) {
fDCombo.add(names[i]);
if (names[i].equalsIgnoreCase(current)) {
select = i;
}
}
if (select != -1) {
fDCombo.select(select);
} else {
fDCombo.select(0);
}
updateComboFromSelection();
getControl().getParent().layout(true);
}
protected void createDebuggerGroup(Composite parent, int colspan) {
Group debuggerGroup = new Group(parent, SWT.SHADOW_ETCHED_IN);
debuggerGroup.setText(LaunchMessages.getString("CDebuggerTab.Debugger_Options")); //$NON-NLS-1$
setDynamicTabHolder(debuggerGroup);
GridLayout tabHolderLayout = new GridLayout();
tabHolderLayout.marginHeight = 0;
tabHolderLayout.marginWidth = 0;
tabHolderLayout.numColumns = 1;
debuggerGroup.setLayout(tabHolderLayout);
GridData gd = new GridData(GridData.FILL_BOTH);
gd.horizontalSpan = colspan;
debuggerGroup.setLayoutData(gd);
}
protected void updateComboFromSelection() {
handleDebuggerChanged();
updateLaunchConfigurationDialog();
initializeCommonControls(getLaunchConfiguration());
}
protected boolean isInitializing() {
return fIsInitializing;
}
protected void setInitializing(boolean isInitializing) {
fIsInitializing = isInitializing;
}
protected String getIdForCurrentDebugger() {
if (fDCombo == null) {
return getDebuggerId();
} else {
int selectedIndex = fDCombo.getSelectionIndex();
return fDCombo.getItem(selectedIndex);
}
}
}