blob: 15acced2a7ce1aec63f06a89ca5db61a68f12af1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2015 Ericsson and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Ericsson - Initial API and implementation
* Marc Khouzam (Ericsson) - Updated to allow updating properties
* before creating the tracepoint (Bug 376116)
* Marc Khouzam (Ericsson) - Support for dynamic printf (bug 400628)
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.internal.ui.breakpoints;
import org.eclipse.cdt.debug.core.CDIDebugModel;
import org.eclipse.cdt.debug.core.model.ICAddressBreakpoint;
import org.eclipse.cdt.debug.core.model.ICBreakpoint;
import org.eclipse.cdt.debug.core.model.ICFunctionBreakpoint;
import org.eclipse.cdt.debug.core.model.ICLineBreakpoint;
import org.eclipse.cdt.debug.core.model.ICTracepoint;
import org.eclipse.cdt.debug.internal.ui.breakpoints.BreakpointsMessages;
import org.eclipse.cdt.debug.internal.ui.breakpoints.CBreakpointContext;
import org.eclipse.cdt.debug.internal.ui.breakpoints.CBreakpointPreferenceStore;
import org.eclipse.cdt.debug.ui.breakpoints.ICBreakpointContext;
import org.eclipse.cdt.debug.ui.preferences.ReadOnlyFieldEditor;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.debug.core.model.IDebugElement;
import org.eclipse.debug.core.model.IDebugModelProvider;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.debug.ui.contexts.IDebugContextProvider;
import org.eclipse.jface.preference.BooleanFieldEditor;
import org.eclipse.jface.preference.FieldEditor;
import org.eclipse.jface.preference.FieldEditorPreferencePage;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.IntegerFieldEditor;
import org.eclipse.jface.preference.StringFieldEditor;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IWorkbenchPropertyPage;
import org.eclipse.ui.model.IWorkbenchAdapter;
/**
* The preference page used to present the properties of a GDB tracepoint as preferences.
*/
public class GDBTracepointPropertyPage extends FieldEditorPreferencePage implements IWorkbenchPropertyPage {
class TracepointIntegerFieldEditor extends IntegerFieldEditor {
public TracepointIntegerFieldEditor(String name, String labelText, Composite parent) {
super(name, labelText, parent);
setErrorMessage(Messages.PropertyPage_integer_negative);
}
/**
* @see IntegerFieldEditor#checkState()
*/
@Override
protected boolean checkState() {
Text control = getTextControl();
if (!control.isEnabled()) {
clearErrorMessage();
return true;
}
return super.checkState();
}
/**
* Only store if the text control is enabled
*
* @see FieldEditor#doStore()
*/
@Override
protected void doStore() {
Text text = getTextControl();
if (text.isEnabled()) {
super.doStore();
}
}
/**
* Clears the error message from the message line if the error message is the error message from this field editor.
*/
@Override
protected void clearErrorMessage() {
if (getPage() != null) {
String message = getPage().getErrorMessage();
if (message != null) {
if (getErrorMessage().equals(message)) {
super.clearErrorMessage();
}
} else {
super.clearErrorMessage();
}
}
}
}
class TracepointStringFieldEditor extends StringFieldEditor {
public TracepointStringFieldEditor(String name, String labelText, Composite parent) {
super(name, labelText, parent);
}
/**
* @see StringFieldEditor#checkState()
*/
@Override
protected boolean checkState() {
Text control = getTextControl();
if (!control.isEnabled()) {
clearErrorMessage();
return true;
}
return super.checkState();
}
@Override
protected void doStore() {
Text text = getTextControl();
if (text.isEnabled()) {
super.doStore();
}
}
@Override
protected void doLoad() {
String value = getPreferenceStore().getString(getPreferenceName());
setStringValue(value);
}
/**
* Clears the error message from the message line if the error message is the error message from this field editor.
*/
@Override
protected void clearErrorMessage() {
if (getPage() != null) {
String message = getPage().getErrorMessage();
if (message != null) {
if (getErrorMessage().equals(message)) {
super.clearErrorMessage();
}
} else {
super.clearErrorMessage();
}
}
}
}
class LabelFieldEditor extends ReadOnlyFieldEditor {
private String fValue;
public LabelFieldEditor(Composite parent, String title, String value) {
super(title, title, parent);
fValue = value;
}
@Override
protected void doLoad() {
if (textField != null) {
textField.setText(fValue);
}
}
@Override
protected void doLoadDefault() {
// nothing
}
}
private BooleanFieldEditor fEnabled;
private TracepointStringFieldEditor fCondition;
private Text fIgnoreCountTextControl;
private TracepointIntegerFieldEditor fLineEditor;
private TracepointIntegerFieldEditor fIgnoreCount;
/**
* Indicates if the page currently aims to create
* a breakpoint that already exits.
*/
private boolean fDuplicateBreakpoint;
private Text fPassCountTextControl;
private TracepointIntegerFieldEditor fPassCount;
private IAdaptable fElement;
/**
* The preference store used to interface between the tracepoint and the
* tracepoint preference page. This preference store is initialized only
* when the preference store cannot be retrieved from the preference
* dialog's element.
* @see #getPreferenceStore()
*/
private CBreakpointPreferenceStore fTracepointPreferenceStore;
/**
* Constructor for GDBTracepointPropertyPage.
*
*/
public GDBTracepointPropertyPage() {
super(GRID);
noDefaultAndApplyButton();
}
@Override
protected void createFieldEditors() {
ICTracepoint tracepoint = getTracepoint();
createMainLabel(tracepoint);
createTypeSpecificLabelFieldEditors(tracepoint);
createEnabledField(getFieldEditorParent());
createConditionEditor(getFieldEditorParent());
// GDB does not support ignore count right now
//createIgnoreCountEditor(getFieldEditorParent());
createPassCountEditor(getFieldEditorParent());
}
private void createMainLabel(ICTracepoint tracepoint) {
addField(createLabelEditor(getFieldEditorParent(), Messages.PropertyPage_Class,
getTracepointMainLabel(tracepoint)));
}
private void createTypeSpecificLabelFieldEditors(ICTracepoint tracepoint) {
if (tracepoint instanceof ICFunctionBreakpoint) {
createFunctionEditor(getFieldEditorParent());
} else if (tracepoint instanceof ICAddressBreakpoint) {
String address = getPreferenceStore().getString(ICLineBreakpoint.ADDRESS);
if (address == null || address.trim().length() == 0) {
address = Messages.PropertyPage_NotAvailable;
}
addField(createLabelEditor(getFieldEditorParent(), Messages.PropertyPage_Address, address));
} else { // LineTracepoint
String fileName = getPreferenceStore().getString(ICBreakpoint.SOURCE_HANDLE);
if (fileName != null) {
addField(createLabelEditor(getFieldEditorParent(), Messages.PropertyPage_File, fileName));
}
int lNumber = getPreferenceStore().getInt(IMarker.LINE_NUMBER);
if (lNumber > 0) {
createLineNumberEditor(getFieldEditorParent());
}
}
}
private String getTracepointMainLabel(ICTracepoint tracepoint) {
IWorkbenchAdapter labelProvider = getElement().getAdapter(IWorkbenchAdapter.class);
if (labelProvider != null) {
return labelProvider.getLabel(getElement());
}
// default main label is the label of marker type for the tracepoint
return CDIDebugModel.calculateMarkerType(tracepoint);
}
protected void createFunctionEditor(Composite parent) {
ICTracepoint tracepoint = getTracepoint();
if (tracepoint == null || tracepoint.getMarker() == null) {
TracepointStringFieldEditor expressionEditor = new TracepointStringFieldEditor(ICLineBreakpoint.FUNCTION,
Messages.PropertyPage_FunctionName, parent);
expressionEditor.setErrorMessage(Messages.PropertyPage_function_value_errorMessage);
expressionEditor.setEmptyStringAllowed(false);
addField(expressionEditor);
} else {
String function = getPreferenceStore().getString(ICLineBreakpoint.FUNCTION);
if (function == null) {
function = Messages.PropertyPage_NotAvailable;
}
addField(createLabelEditor(getFieldEditorParent(), Messages.PropertyPage_FunctionName, function));
}
}
protected void createLineNumberEditor(Composite parent) {
String title = Messages.PropertyPage_LineNumber;
fLineEditor = new TracepointIntegerFieldEditor(IMarker.LINE_NUMBER, title, parent);
fLineEditor.setValidRange(1, Integer.MAX_VALUE);
fLineEditor.setErrorMessage(Messages.PropertyPage_lineNumber_errorMessage);
addField(fLineEditor);
}
protected void createEnabledField(Composite parent) {
fEnabled = new BooleanFieldEditor(ICBreakpoint.ENABLED, Messages.PropertyPage_Enabled, parent);
addField(fEnabled);
}
protected void createConditionEditor(Composite parent) {
fCondition = new TracepointStringFieldEditor(ICBreakpoint.CONDITION, Messages.PropertyPage_Condition, parent);
fCondition.setEmptyStringAllowed(true);
fCondition.setErrorMessage(Messages.PropertyPage_InvalidCondition);
addField(fCondition);
}
protected void createIgnoreCountEditor(Composite parent) {
fIgnoreCount = new TracepointIntegerFieldEditor(ICBreakpoint.IGNORE_COUNT, Messages.PropertyPage_IgnoreCount,
parent);
fIgnoreCount.setValidRange(0, Integer.MAX_VALUE);
fIgnoreCountTextControl = fIgnoreCount.getTextControl(parent);
fIgnoreCountTextControl.setEnabled(getPreferenceStore().getInt(ICBreakpoint.IGNORE_COUNT) >= 0);
addField(fIgnoreCount);
}
protected void createPassCountEditor(Composite parent) {
fPassCount = new TracepointIntegerFieldEditor(ICTracepoint.PASS_COUNT,
Messages.TracepointPropertyPage_PassCount, parent);
fPassCount.setValidRange(0, Integer.MAX_VALUE);
fPassCountTextControl = fPassCount.getTextControl(parent);
fPassCountTextControl.setEnabled(getPreferenceStore().getInt(ICTracepoint.PASS_COUNT) >= 0);
addField(fPassCount);
}
protected FieldEditor createLabelEditor(Composite parent, String title, String value) {
return new LabelFieldEditor(parent, title, value);
}
@Override
public boolean isValid() {
// Don't allow to create a duplicate breakpoint
return super.isValid() && !fDuplicateBreakpoint;
}
@Override
public void propertyChange(PropertyChangeEvent event) {
super.propertyChange(event);
ICBreakpoint currentBp = getTracepoint();
if (!(currentBp instanceof ICFunctionBreakpoint) && !(currentBp instanceof ICAddressBreakpoint)) {
// Check for duplication of line tracepoints
if (event.getProperty().equals(FieldEditor.VALUE)) {
if (super.isValid()) {
// For every change, if all the fields are valid
// we then check if we are dealing with a duplicate
// breakpoint.
boolean oldValue = fDuplicateBreakpoint;
fDuplicateBreakpoint = isDuplicateBreakpoint();
if (oldValue != fDuplicateBreakpoint) {
if (fDuplicateBreakpoint) {
setErrorMessage(BreakpointsMessages
.getString("CBreakpointPropertyPage.breakpoint_already_exists_errorMessage")); //$NON-NLS-1$
} else {
setErrorMessage(null);
}
// update container state
if (getContainer() != null) {
getContainer().updateButtons();
}
// update page state
updateApplyButton();
}
}
}
}
}
private boolean isDuplicateBreakpoint() {
String source = getPreferenceStore().getString(ICBreakpoint.SOURCE_HANDLE);
int line = fLineEditor.getIntValue();
// Look for any breakpoint (base class) that has the same source file and line number as what
// is currently being inputed. Careful not to compare with the current tracepoint
// in the case of modifying the properties of an existing tracepoint; in
// that case we of course have this particular tracepoint at this file and line.
ICBreakpoint currentBp = getTracepoint();
IBreakpoint[] breakpoints = DebugPlugin.getDefault().getBreakpointManager().getBreakpoints();
for (IBreakpoint bp : breakpoints) {
if (!bp.equals(currentBp) && bp instanceof ICBreakpoint) {
IMarker marker = bp.getMarker();
if (marker != null) {
String markerFile = marker.getAttribute(ICBreakpoint.SOURCE_HANDLE, ""); //$NON-NLS-1$
int markerLine = marker.getAttribute(IMarker.LINE_NUMBER, -1);
if (source.equals(markerFile) && line == markerLine) {
// Woops, we already have another breakpoint at this file:line
return true;
}
}
}
}
return false;
}
protected ICTracepoint getTracepoint() {
IAdaptable element = getElement();
if (element instanceof ICTracepoint) {
return (ICTracepoint) element;
}
if (element instanceof ICBreakpointContext) {
ICBreakpoint breakpoint = ((ICBreakpointContext) element).getBreakpoint();
if (breakpoint instanceof ICTracepoint) {
return (ICTracepoint) breakpoint;
}
assert false : "Should always have a tracepoint"; //$NON-NLS-1$
}
return element.getAdapter(ICTracepoint.class);
}
protected Object getDebugContext() {
IDebugContextProvider provider = getElement().getAdapter(IDebugContextProvider.class);
if (provider != null) {
ISelection selection = provider.getActiveContext();
if (selection instanceof IStructuredSelection) {
return ((IStructuredSelection) selection).getFirstElement();
}
return null;
}
return DebugUITools.getDebugContext();
}
protected IResource getResource() {
IAdaptable element = getElement();
if (element instanceof ICTracepoint) {
IMarker marker = ((ICTracepoint) element).getMarker();
if (marker != null) {
return marker.getResource();
}
} else if (element instanceof ICBreakpointContext) {
return ((ICBreakpointContext) element).getResource();
}
return null;
}
@Override
public IPreferenceStore getPreferenceStore() {
IAdaptable element = getElement();
if (element instanceof ICBreakpointContext) {
return ((ICBreakpointContext) element).getPreferenceStore();
}
if (fTracepointPreferenceStore == null) {
CBreakpointContext bpContext = element instanceof CBreakpointContext ? (CBreakpointContext) element : null;
fTracepointPreferenceStore = new CBreakpointPreferenceStore(bpContext, null);
}
return fTracepointPreferenceStore;
}
@Override
public boolean performCancel() {
IPreferenceStore store = getPreferenceStore();
if (store instanceof CBreakpointPreferenceStore) {
((CBreakpointPreferenceStore) store).setCanceled(true);
}
return super.performCancel();
}
@Override
public boolean performOk() {
IPreferenceStore store = getPreferenceStore();
if (store instanceof CBreakpointPreferenceStore) {
((CBreakpointPreferenceStore) store).setCanceled(false);
}
return super.performOk();
}
/* (non-Javadoc)
* @see org.eclipse.ui.IWorkbenchPropertyPage#getElement()
*/
@Override
public IAdaptable getElement() {
return fElement;
}
/* (non-Javadoc)
* @see org.eclipse.ui.IWorkbenchPropertyPage#setElement(org.eclipse.core.runtime.IAdaptable)
*/
@Override
public void setElement(IAdaptable element) {
if (element instanceof ICBreakpoint) {
fElement = new CBreakpointContext((ICBreakpoint) element, null);
} else {
fElement = element;
}
}
protected String[] getDebugModelIds() {
String[] debugModelIds = null;
Object debugContext = getDebugContext();
IDebugModelProvider debugModelProvider = (IDebugModelProvider) DebugPlugin.getAdapter(debugContext,
IDebugModelProvider.class);
if (debugModelProvider != null) {
debugModelIds = debugModelProvider.getModelIdentifiers();
} else if (debugContext instanceof IDebugElement) {
debugModelIds = new String[] { ((IDebugElement) debugContext).getModelIdentifier() };
}
return debugModelIds;
}
}