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