/******************************************************************************* * Copyright (c) 2006, 2015 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 * QNX Software Systems - Mikhail Khodjaiants - Registers View (Bug 53640) * Wind River - Anton Leherbauer - Fix selection provider (Bug 254442) * Patrick Chuong (Texas Instruments) - Improve usability of the breakpoint view (Bug 238956) *******************************************************************************/ package org.eclipse.debug.internal.ui.views.variables.details; import java.util.Iterator; import java.util.ResourceBundle; import org.eclipse.core.commands.operations.IUndoContext; import org.eclipse.core.resources.IMarker; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.model.IBreakpoint; import org.eclipse.debug.core.model.IDebugElement; import org.eclipse.debug.core.model.IExpression; import org.eclipse.debug.core.model.IValue; import org.eclipse.debug.core.model.IVariable; import org.eclipse.debug.internal.core.IInternalDebugCoreConstants; import org.eclipse.debug.internal.ui.DebugPluginImages; import org.eclipse.debug.internal.ui.DebugUIPlugin; import org.eclipse.debug.internal.ui.IDebugHelpContextIds; import org.eclipse.debug.internal.ui.LazyModelPresentation; import org.eclipse.debug.internal.ui.VariablesViewModelPresentation; import org.eclipse.debug.internal.ui.actions.variables.details.DetailPaneAssignValueAction; import org.eclipse.debug.internal.ui.actions.variables.details.DetailPaneMaxLengthAction; import org.eclipse.debug.internal.ui.actions.variables.details.DetailPaneWordWrapAction; import org.eclipse.debug.internal.ui.breakpoints.provisional.IBreakpointContainer; import org.eclipse.debug.internal.ui.preferences.IDebugPreferenceConstants; import org.eclipse.debug.internal.ui.views.variables.IndexedValuePartition; import org.eclipse.debug.ui.IDebugModelPresentation; import org.eclipse.debug.ui.IDebugUIConstants; import org.eclipse.debug.ui.IDebugView; import org.eclipse.debug.ui.IDetailPane2; import org.eclipse.debug.ui.IValueDetailListener; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IStatusLineManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.action.StatusLineContributionItem; import org.eclipse.jface.commands.ActionHandler; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentListener; import org.eclipse.jface.text.IFindReplaceTarget; import org.eclipse.jface.text.ITextOperationTarget; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.IUndoManager; import org.eclipse.jface.text.IUndoManagerExtension; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.text.source.SourceViewer; import org.eclipse.jface.text.source.SourceViewerConfiguration; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.FocusAdapter; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseListener; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Menu; import org.eclipse.ui.IWorkbenchActionConstants; import org.eclipse.ui.IWorkbenchCommandConstants; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.actions.ActionFactory; import org.eclipse.ui.console.actions.TextViewerAction; import org.eclipse.ui.handlers.IHandlerActivation; import org.eclipse.ui.handlers.IHandlerService; import org.eclipse.ui.model.IWorkbenchAdapter; import org.eclipse.ui.operations.OperationHistoryActionHandler; import org.eclipse.ui.operations.RedoActionHandler; import org.eclipse.ui.operations.UndoActionHandler; import org.eclipse.ui.progress.WorkbenchJob; import org.eclipse.ui.texteditor.FindReplaceAction; import org.eclipse.ui.texteditor.IAbstractTextEditorHelpContextIds; import org.eclipse.ui.texteditor.ITextEditorActionConstants; import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds; import com.ibm.icu.text.MessageFormat; /** * This detail pane uses a source viewer to display detailed information about the current * selection. It incorporates a large number of actions into its context menu. It is the * default detail pane. * * @see DefaultDetailPaneFactory * @since 3.3 * */ public class DefaultDetailPane extends AbstractDetailPane implements IDetailPane2, IAdaptable, IPropertyChangeListener{ /** * These are the IDs for the actions in the context menu */ protected static final String DETAIL_COPY_ACTION = ActionFactory.COPY.getId() + ".SourceDetailPane"; //$NON-NLS-1$ protected static final String DETAIL_SELECT_ALL_ACTION = IDebugView.SELECT_ALL_ACTION + ".SourceDetailPane"; //$NON-NLS-1$ protected static final String DETAIL_PASTE_ACTION = ActionFactory.PASTE.getId(); protected static final String DETAIL_CUT_ACTION = ActionFactory.CUT.getId(); protected static final String DETAIL_FIND_REPLACE_TEXT_ACTION = "FindReplaceText"; //$NON-NLS-1$ protected static final String DETAIL_CONTENT_ASSIST_ACTION = "ContentAssist"; //$NON-NLS-1$ protected static final String DETAIL_ASSIGN_VALUE_ACTION = "AssignValue"; //$NON-NLS-1$ protected static final String DETAIL_WORD_WRAP_ACTION = IDebugPreferenceConstants.PREF_DETAIL_PANE_WORD_WRAP; protected static final String DETAIL_MAX_LENGTH_ACTION = "MaxLength"; //$NON-NLS-1$ /** * The ID, name and description of this pane are stored in constants so that the class * does not have to be instantiated to access them. */ public static final String ID = DetailMessages.DefaultDetailPane_0; public static final String NAME = DetailMessages.DefaultDetailPane_1; public static final String DESCRIPTION = DetailMessages.DefaultDetailPane_57; /** * Data structure for the position label value. */ private static class PositionLabelValue { public int fValue; @Override public String toString() { return String.valueOf(fValue); } } /** * Internal interface for a cursor listener. I.e. aggregation * of mouse and key listener. * @since 3.0 */ interface ICursorListener extends MouseListener, KeyListener { } /** * Job to compute the details for a selection */ class DetailJob extends Job implements IValueDetailListener { private IStructuredSelection fElements; private IDebugModelPresentation fModel; private boolean fFirst = true; // whether a result was collected private boolean fComputed = false; private IProgressMonitor fMonitor; public DetailJob(IStructuredSelection elements, IDebugModelPresentation model) { super("compute variable details"); //$NON-NLS-1$ setSystem(true); fElements = elements; fModel = model; } /* (non-Javadoc) * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) */ @Override protected IStatus run(IProgressMonitor monitor) { fMonitor = monitor; Iterator<?> iterator = fElements.iterator(); String message = null; while (iterator.hasNext()) { if (monitor.isCanceled()) { break; } Object element = iterator.next(); IValue val = null; if (element instanceof IVariable) { try { val = ((IVariable)element).getValue(); } catch (DebugException e) { detailComputed(null, e.getStatus().getMessage()); } } else if (element instanceof IExpression) { val = ((IExpression)element).getValue(); } else if (element instanceof IBreakpoint) { IBreakpoint bp = (IBreakpoint) element; message = bp.getMarker().getAttribute(IMarker.MESSAGE, ""); //$NON-NLS-1$ } else if (element instanceof IBreakpointContainer) { IBreakpointContainer c = (IBreakpointContainer) element; IAdaptable category = c.getCategory(); if (category != null) { IWorkbenchAdapter adapter = category.getAdapter(IWorkbenchAdapter.class); if (adapter != null) { message = adapter.getLabel(category); } else { message = c.getOrganizer().getLabel(); } } } // When selecting a index partition, clear the pane if (val instanceof IndexedValuePartition) { detailComputed(null, IInternalDebugCoreConstants.EMPTY_STRING); val = null; } if (element instanceof String) { message = (String) element; } if (val != null && !monitor.isCanceled()) { fModel.computeDetail(val, this); synchronized (this) { try { // wait for a max of 30 seconds for result, then cancel wait(30000); if (!fComputed) { fMonitor.setCanceled(true); } } catch (InterruptedException e) { break; } } } } // If no details were computed for the selected variable, clear the pane // or use the message, if the variable was a java.lang.String if (!fComputed){ if (message == null) { detailComputed(null,IInternalDebugCoreConstants.EMPTY_STRING); } else { detailComputed(null, message); } } return Status.OK_STATUS; } /* (non-Javadoc) * @see org.eclipse.core.runtime.jobs.Job#canceling() */ @Override protected void canceling() { super.canceling(); synchronized (this) { notifyAll(); } } /* (non-Javadoc) * @see org.eclipse.debug.ui.IValueDetailListener#detailComputed(org.eclipse.debug.core.model.IValue, java.lang.String) */ @Override public void detailComputed(IValue value, final String result) { synchronized (this) { fComputed = true; } String valueString = result; if (valueString == null){ try{ valueString = value.getValueString(); } catch (DebugException e){ valueString = e.getMessage(); } } final String detail = (valueString != null) ? valueString : DetailMessages.DefaultDetailPane_3; if (!fMonitor.isCanceled()) { WorkbenchJob append = new WorkbenchJob("append details") { //$NON-NLS-1$ @Override public IStatus runInUIThread(IProgressMonitor monitor) { if (!fMonitor.isCanceled()) { String insert = detail; int length = 0; if (!fFirst) { length = getDetailDocument().getLength(); } if (length > 0) { insert = "\n" + result; //$NON-NLS-1$ } try { int max = DebugUIPlugin.getDefault().getPreferenceStore().getInt(IDebugUIConstants.PREF_MAX_DETAIL_LENGTH); if (max > 0 && insert.length() > max) { insert = insert.substring(0, max) + "..."; //$NON-NLS-1$ } if (fFirst) { getDetailDocument().set(insert); fFirst = false; } else { getDetailDocument().replace(length, 0,insert); } } catch (BadLocationException e) { DebugUIPlugin.log(e); } } return Status.OK_STATUS; } }; append.setSystem(true); append.schedule(); } synchronized (this) { notifyAll(); } } } /** * The model presentation used to produce the string details for a * selected variable. */ private VariablesViewModelPresentation fModelPresentation; private String fDebugModelIdentifier; /** * Controls the status line while the details area has focus. * Displays the current cursor position in the text (line:character). */ private StatusLineContributionItem fStatusLineItem; /** * The source viewer in which the computed string detail * of selected variables will be displayed. */ private SourceViewer fSourceViewer; /** * The last selection displayed in the source viewer. */ private IStructuredSelection fLastDisplayed = null; /** * Flag used to track whether source viewer has focus. It helps avoid * resetting global actions incorrectly. */ private boolean fHasFocus = false; /** * Variables used to create the detailed information for a selection */ private IDocument fDetailDocument; private DetailJob fDetailJob = null; private final String fPositionLabelPattern = DetailMessages.DefaultDetailPane_56; private final PositionLabelValue fLineLabel = new PositionLabelValue(); private final PositionLabelValue fColumnLabel = new PositionLabelValue(); private final Object[] fPositionLabelPatternArguments = new Object[] { fLineLabel, fColumnLabel }; private ICursorListener fCursorListener; /** * Handler activation object so that we can use the global content assist command * and properly deactivate it later. */ private IHandlerActivation fContentAssistActivation; /* (non-Javadoc) * @see org.eclipse.debug.ui.IDetailPane#createControl(org.eclipse.swt.widgets.Composite) */ @Override public Control createControl(Composite parent) { fModelPresentation = new VariablesViewModelPresentation(); createSourceViewer(parent); if (isInView()){ createViewSpecificComponents(); createActions(); DebugUIPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(this); JFaceResources.getFontRegistry().addListener(this); } return fSourceViewer.getControl(); } /** * Creates the source viewer in the given parent composite * * @param parent Parent composite to create the source viewer in */ private void createSourceViewer(Composite parent) { // Create & configure a SourceViewer fSourceViewer = new SourceViewer(parent, null, SWT.V_SCROLL | SWT.H_SCROLL); fSourceViewer.setDocument(getDetailDocument()); fSourceViewer.getTextWidget().setFont(JFaceResources.getFont(IDebugUIConstants.PREF_DETAIL_PANE_FONT)); fSourceViewer.getTextWidget().setWordWrap(DebugUIPlugin.getDefault().getPreferenceStore().getBoolean(IDebugPreferenceConstants.PREF_DETAIL_PANE_WORD_WRAP)); fSourceViewer.setEditable(false); PlatformUI.getWorkbench().getHelpSystem().setHelp(fSourceViewer.getTextWidget(), IDebugHelpContextIds.DETAIL_PANE); Control control = fSourceViewer.getControl(); GridData gd = new GridData(GridData.FILL_BOTH); control.setLayoutData(gd); } /** * Creates listeners and other components that should only be added to the * source viewer when this detail pane is inside a view. */ private void createViewSpecificComponents(){ // Add a document listener so actions get updated when the document changes getDetailDocument().addDocumentListener(new IDocumentListener() { @Override public void documentAboutToBeChanged(DocumentEvent event) {} @Override public void documentChanged(DocumentEvent event) { updateSelectionDependentActions(); } }); // Add the selection listener so selection dependent actions get updated. fSourceViewer.getSelectionProvider().addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { updateSelectionDependentActions(); } }); // Add a focus listener to update actions when details area gains focus fSourceViewer.getControl().addFocusListener(new FocusAdapter() { @Override public void focusGained(FocusEvent e) { setGlobalAction(IDebugView.SELECT_ALL_ACTION, getAction(DETAIL_SELECT_ALL_ACTION)); setGlobalAction(IDebugView.CUT_ACTION, getAction(DETAIL_CUT_ACTION)); setGlobalAction(IDebugView.COPY_ACTION, getAction(DETAIL_COPY_ACTION)); setGlobalAction(IDebugView.PASTE_ACTION, getAction(DETAIL_PASTE_ACTION)); setGlobalAction(IDebugView.FIND_ACTION, getAction(DETAIL_FIND_REPLACE_TEXT_ACTION)); IAction action = getAction(DETAIL_ASSIGN_VALUE_ACTION); setGlobalAction(action.getActionDefinitionId(), action); action = getAction(DETAIL_CONTENT_ASSIST_ACTION); setGlobalAction(action.getActionDefinitionId(),action); getViewSite().getActionBars().updateActionBars(); updateAction(DETAIL_FIND_REPLACE_TEXT_ACTION); fHasFocus = true; } @Override public void focusLost(FocusEvent e) { setGlobalAction(IDebugView.SELECT_ALL_ACTION, null); setGlobalAction(IDebugView.CUT_ACTION, null); setGlobalAction(IDebugView.COPY_ACTION, null); setGlobalAction(IDebugView.PASTE_ACTION, null); setGlobalAction(IDebugView.FIND_ACTION, null); setGlobalAction(getAction(DETAIL_ASSIGN_VALUE_ACTION).getActionDefinitionId(), null); setGlobalAction(getAction(DETAIL_CONTENT_ASSIST_ACTION).getActionDefinitionId(), null); getViewSite().getActionBars().updateActionBars(); fHasFocus = false; } }); // disposed controls don't get a FocusOut event, make sure all actions // have been deactivated fSourceViewer.getControl().addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(DisposeEvent e) { if (fHasFocus) { setGlobalAction(IDebugView.SELECT_ALL_ACTION, null); setGlobalAction(IDebugView.CUT_ACTION, null); setGlobalAction(IDebugView.COPY_ACTION, null); setGlobalAction(IDebugView.PASTE_ACTION, null); setGlobalAction(IDebugView.FIND_ACTION, null); setGlobalAction(getAction(DETAIL_ASSIGN_VALUE_ACTION) .getActionDefinitionId(), null); setGlobalAction(getAction(DETAIL_CONTENT_ASSIST_ACTION) .getActionDefinitionId(), null); } } }); // Create a status line item displaying the current cursor location fStatusLineItem = new StatusLineContributionItem("ModeContributionItem"); //$NON-NLS-1$ IStatusLineManager manager= getViewSite().getActionBars().getStatusLineManager(); manager.add(fStatusLineItem); fSourceViewer.getTextWidget().addMouseListener(getCursorListener()); fSourceViewer.getTextWidget().addKeyListener(getCursorListener()); // Add a context menu to the detail area createDetailContextMenu(fSourceViewer.getTextWidget()); } /** * Creates the actions to add to the context menu */ private void createActions() { TextViewerAction textAction= new TextViewerAction(fSourceViewer, ISourceViewer.CONTENTASSIST_PROPOSALS); textAction.setActionDefinitionId(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS); textAction.configureAction(DetailMessages.DefaultDetailPane_Co_ntent_Assist_3, IInternalDebugCoreConstants.EMPTY_STRING,IInternalDebugCoreConstants.EMPTY_STRING); textAction.setImageDescriptor(DebugPluginImages.getImageDescriptor(IDebugUIConstants.IMG_ELCL_CONTENT_ASSIST)); textAction.setHoverImageDescriptor(DebugPluginImages.getImageDescriptor(IDebugUIConstants.IMG_LCL_CONTENT_ASSIST)); textAction.setDisabledImageDescriptor(DebugPluginImages.getImageDescriptor(IDebugUIConstants.IMG_DLCL_CONTENT_ASSIST)); PlatformUI.getWorkbench().getHelpSystem().setHelp(textAction, IDebugHelpContextIds.DETAIL_PANE_CONTENT_ASSIST_ACTION); ActionHandler actionHandler = new ActionHandler(textAction); IHandlerService handlerService = getViewSite().getService(IHandlerService.class); fContentAssistActivation = handlerService.activateHandler(textAction.getActionDefinitionId(), actionHandler); setAction(DETAIL_CONTENT_ASSIST_ACTION, textAction); textAction= new TextViewerAction(fSourceViewer, ITextOperationTarget.SELECT_ALL); textAction.configureAction(DetailMessages.DefaultDetailPane_Select__All_5, IInternalDebugCoreConstants.EMPTY_STRING,IInternalDebugCoreConstants.EMPTY_STRING); textAction.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_SELECT_ALL); PlatformUI.getWorkbench().getHelpSystem().setHelp(textAction, IDebugHelpContextIds.DETAIL_PANE_SELECT_ALL_ACTION); setAction(DETAIL_SELECT_ALL_ACTION, textAction); textAction= new TextViewerAction(fSourceViewer, ITextOperationTarget.COPY); textAction.configureAction(DetailMessages.DefaultDetailPane__Copy_8, IInternalDebugCoreConstants.EMPTY_STRING,IInternalDebugCoreConstants.EMPTY_STRING); textAction.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_COPY); PlatformUI.getWorkbench().getHelpSystem().setHelp(textAction, IDebugHelpContextIds.DETAIL_PANE_COPY_ACTION); setAction(DETAIL_COPY_ACTION, textAction); textAction= new TextViewerAction(fSourceViewer, ITextOperationTarget.CUT); textAction.configureAction(DetailMessages.DefaultDetailPane_Cu_t_11, IInternalDebugCoreConstants.EMPTY_STRING,IInternalDebugCoreConstants.EMPTY_STRING); textAction.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_CUT); PlatformUI.getWorkbench().getHelpSystem().setHelp(textAction, IDebugHelpContextIds.DETAIL_PANE_CUT_ACTION); setAction(DETAIL_CUT_ACTION, textAction); textAction= new TextViewerAction(fSourceViewer, ITextOperationTarget.PASTE); textAction.configureAction(DetailMessages.DefaultDetailPane__Paste_14, IInternalDebugCoreConstants.EMPTY_STRING,IInternalDebugCoreConstants.EMPTY_STRING); textAction.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_PASTE); PlatformUI.getWorkbench().getHelpSystem().setHelp(textAction, IDebugHelpContextIds.DETAIL_PANE_PASTE_ACTION); setAction(ActionFactory.PASTE.getId(), textAction); setSelectionDependantAction(DETAIL_COPY_ACTION); setSelectionDependantAction(DETAIL_CUT_ACTION); setSelectionDependantAction(DETAIL_PASTE_ACTION); // TODO: Still using "old" resource access, find/replace won't work in popup dialogs ResourceBundle bundle= ResourceBundle.getBundle("org.eclipse.debug.internal.ui.views.variables.VariablesViewResourceBundleMessages"); //$NON-NLS-1$ IAction action = new FindReplaceAction(bundle, "find_replace_action_", getWorkbenchPartSite().getShell(), new FindReplaceTargetWrapper(fSourceViewer.getFindReplaceTarget())); //$NON-NLS-1$ action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_FIND_AND_REPLACE); PlatformUI.getWorkbench().getHelpSystem().setHelp(action, IDebugHelpContextIds.DETAIL_PANE_FIND_REPLACE_ACTION); setAction(DETAIL_FIND_REPLACE_TEXT_ACTION, action); updateSelectionDependentActions(); action = new DetailPaneWordWrapAction(fSourceViewer); setAction(DETAIL_WORD_WRAP_ACTION, action); action = new DetailPaneMaxLengthAction(fSourceViewer.getControl().getShell()); setAction(DETAIL_MAX_LENGTH_ACTION,action); action = new DetailPaneAssignValueAction(fSourceViewer,getViewSite()); setAction(DETAIL_ASSIGN_VALUE_ACTION, action); } /** * Create the context menu particular to the detail pane. Note that anyone * wishing to contribute an action to this menu must use * <code>IDebugUIConstants.VARIABLE_VIEW_DETAIL_ID</code> as the * <code>targetID</code> in the extension XML. * @param menuControl the control to create the context menu on */ protected void createDetailContextMenu(Control menuControl) { MenuManager menuMgr= new MenuManager(); menuMgr.setRemoveAllWhenShown(true); menuMgr.addMenuListener(new IMenuListener() { @Override public void menuAboutToShow(IMenuManager mgr) { fillDetailContextMenu(mgr); } }); Menu menu= menuMgr.createContextMenu(menuControl); menuControl.setMenu(menu); getViewSite().registerContextMenu(IDebugUIConstants.VARIABLE_VIEW_DETAIL_ID, menuMgr, fSourceViewer.getSelectionProvider()); } /** * Adds items to the detail pane's context menu including any extension defined * actions. * * @param menu The menu to add the item to. */ protected void fillDetailContextMenu(IMenuManager menu) { menu.add(new Separator(IDebugUIConstants.VARIABLE_GROUP)); if (isInView()){ menu.add(getAction(DETAIL_ASSIGN_VALUE_ACTION)); menu.add(getAction(DETAIL_CONTENT_ASSIST_ACTION)); } menu.add(new Separator()); menu.add(getAction(DETAIL_CUT_ACTION)); menu.add(getAction(DETAIL_COPY_ACTION)); menu.add(getAction(DETAIL_PASTE_ACTION)); menu.add(getAction(DETAIL_SELECT_ALL_ACTION)); menu.add(new Separator("FIND")); //$NON-NLS-1$ if (isInView()){ menu.add(getAction(DETAIL_FIND_REPLACE_TEXT_ACTION)); } menu.add(new Separator()); menu.add(getAction(DETAIL_WORD_WRAP_ACTION)); menu.add(getAction(DETAIL_MAX_LENGTH_ACTION)); menu.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); } /* (non-Javadoc) * @see org.eclipse.debug.ui.IDetailPane#display(org.eclipse.jface.viewers.IStructuredSelection) */ @Override public void display(IStructuredSelection selection) { if (selection == null){ clearSourceViewer(); return; } fLastDisplayed = selection; if (isInView()){ fSourceViewer.setEditable(true); } if (selection.isEmpty()){ clearSourceViewer(); return; } Object firstElement = selection.getFirstElement(); if (firstElement != null && firstElement instanceof IDebugElement) { String modelID = ((IDebugElement)firstElement).getModelIdentifier(); setDebugModel(modelID); } if (isInView()){ IAction assignAction = getAction(DETAIL_ASSIGN_VALUE_ACTION); if (assignAction instanceof DetailPaneAssignValueAction){ ((DetailPaneAssignValueAction)assignAction).updateCurrentVariable(selection); } } synchronized (this) { if (fDetailJob != null) { fDetailJob.cancel(); } fDetailJob = new DetailJob(selection,fModelPresentation); fDetailJob.schedule(); } } /* (non-Javadoc) * @see org.eclipse.debug.ui.IDetailPane#setFocus() */ @Override public boolean setFocus(){ if (fSourceViewer != null){ fSourceViewer.getTextWidget().setFocus(); return true; } return false; } /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.views.variables.details.AbstractDetailPane#dispose() */ @Override public void dispose(){ if (fDetailJob != null) { fDetailJob.cancel(); } if (fModelPresentation != null) { fModelPresentation.dispose(); } fDebugModelIdentifier = null; // Setting this to null makes sure the source viewer is reconfigured with the model presentation after disposal if (fSourceViewer != null && fSourceViewer.getControl() != null) { fSourceViewer.getControl().dispose(); } if (isInView()){ IAction action = getAction(DETAIL_ASSIGN_VALUE_ACTION); if (action != null){ ((DetailPaneAssignValueAction)action).dispose(); } if (fContentAssistActivation != null){ IHandlerService service = getViewSite().getService(IHandlerService.class); service.deactivateHandler(fContentAssistActivation); fContentAssistActivation = null; } disposeUndoRedoAction(ITextEditorActionConstants.UNDO); disposeUndoRedoAction(ITextEditorActionConstants.REDO); getViewSite().getActionBars().getStatusLineManager().remove(fStatusLineItem); DebugUIPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(this); JFaceResources.getFontRegistry().removeListener(this); } super.dispose(); } /* (non-Javadoc) * @see org.eclipse.debug.ui.IDetailPane#getDescription() */ @Override public String getDescription() { return DESCRIPTION; } /* (non-Javadoc) * @see org.eclipse.debug.ui.IDetailPane#getID() */ @Override public String getID() { return ID; } /* (non-Javadoc) * @see org.eclipse.debug.ui.IDetailPane#getName() */ @Override public String getName() { return NAME; } /* (non-Javadoc) * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class) */ @SuppressWarnings("unchecked") @Override public <T> T getAdapter(Class<T> required) { if (IFindReplaceTarget.class.equals(required)) { return (T) fSourceViewer.getFindReplaceTarget(); } if (ITextViewer.class.equals(required)) { return (T) fSourceViewer; } return null; } /** * Lazily instantiate and return a Document for the detail pane text viewer. * @return the singleton {@link Document} for this detail pane */ protected IDocument getDetailDocument() { if (fDetailDocument == null) { fDetailDocument = new Document(); } return fDetailDocument; } /** * Clears the source viewer, removes all text. */ protected void clearSourceViewer(){ if (fDetailJob != null) { fDetailJob.cancel(); } fLastDisplayed = null; fDetailDocument.set(IInternalDebugCoreConstants.EMPTY_STRING); fSourceViewer.setEditable(false); } /** * Configures the details viewer for the debug model * currently being displayed */ protected void configureDetailsViewer() { LazyModelPresentation mp = (LazyModelPresentation)fModelPresentation.getPresentation(getDebugModel()); SourceViewerConfiguration svc = null; if (mp != null) { try { svc = mp.newDetailsViewerConfiguration(); } catch (CoreException e) { DebugUIPlugin.errorDialog(fSourceViewer.getControl().getShell(), DetailMessages.DefaultDetailPane_Error_1, DetailMessages.DefaultDetailPane_2, e); } } if (svc == null) { svc = new SourceViewerConfiguration(); fSourceViewer.setEditable(false); } fSourceViewer.unconfigure(); fSourceViewer.configure(svc); //update actions that depend on the configuration of the source viewer if (isInView()){ updateAction(DETAIL_ASSIGN_VALUE_ACTION); updateAction(DETAIL_CONTENT_ASSIST_ACTION); } if (isInView()){ createUndoRedoActions(); } } /** * @return The formatted string describing cursor position */ protected String getCursorPosition() { if (fSourceViewer == null) { return IInternalDebugCoreConstants.EMPTY_STRING; } StyledText styledText= fSourceViewer.getTextWidget(); int caret= styledText.getCaretOffset(); IDocument document= fSourceViewer.getDocument(); if (document == null) { return IInternalDebugCoreConstants.EMPTY_STRING; } try { int line= document.getLineOfOffset(caret); int lineOffset= document.getLineOffset(line); int tabWidth= styledText.getTabs(); int column= 0; for (int i= lineOffset; i < caret; i++) { if ('\t' == document.getChar(i)) { column += tabWidth - (tabWidth == 0 ? 0 : column % tabWidth); } else { column++; } } fLineLabel.fValue= line + 1; fColumnLabel.fValue= column + 1; return MessageFormat.format(fPositionLabelPattern, fPositionLabelPatternArguments); } catch (BadLocationException x) { return IInternalDebugCoreConstants.EMPTY_STRING; } } /** * Returns this view's "cursor" listener to be installed on the view's * associated details viewer. This listener is listening to key and mouse button events. * It triggers the updating of the status line. * * @return the listener */ private ICursorListener getCursorListener() { if (fCursorListener == null) { fCursorListener= new ICursorListener() { @Override public void keyPressed(KeyEvent e) { fStatusLineItem.setText(getCursorPosition()); } @Override public void keyReleased(KeyEvent e) { } @Override public void mouseDoubleClick(MouseEvent e) { } @Override public void mouseDown(MouseEvent e) { } @Override public void mouseUp(MouseEvent e) { fStatusLineItem.setText(getCursorPosition()); } }; } return fCursorListener; } /** * Returns the identifier of the debug model being displayed * in this view, or <code>null</code> if none. * * @return debug model identifier */ protected String getDebugModel() { return fDebugModelIdentifier; } /** * Sets the identifier of the debug model being displayed * in this view, or <code>null</code> if none. * * @param id debug model identifier of the type of debug * elements being displayed in this view */ protected void setDebugModel(String id) { if (id != fDebugModelIdentifier) { fDebugModelIdentifier = id; configureDetailsViewer(); } } /** * Creates this editor's undo/re-do actions. * <p> * Subclasses may override or extend.</p> * * @since 3.2 */ protected void createUndoRedoActions() { disposeUndoRedoAction(ITextEditorActionConstants.UNDO); disposeUndoRedoAction(ITextEditorActionConstants.REDO); IUndoContext undoContext= getUndoContext(); if (undoContext != null) { // Use actions provided by global undo/re-do // Create the undo action OperationHistoryActionHandler undoAction= new UndoActionHandler(getViewSite(), undoContext); PlatformUI.getWorkbench().getHelpSystem().setHelp(undoAction, IAbstractTextEditorHelpContextIds.UNDO_ACTION); undoAction.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_UNDO); setAction(ITextEditorActionConstants.UNDO, undoAction); setGlobalAction(ITextEditorActionConstants.UNDO, undoAction); // Create the re-do action. OperationHistoryActionHandler redoAction= new RedoActionHandler(getViewSite(), undoContext); PlatformUI.getWorkbench().getHelpSystem().setHelp(redoAction, IAbstractTextEditorHelpContextIds.REDO_ACTION); redoAction.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_REDO); setAction(ITextEditorActionConstants.REDO, redoAction); setGlobalAction(ITextEditorActionConstants.REDO, redoAction); getViewSite().getActionBars().updateActionBars(); } } /** * Disposes of the action with the specified ID * * @param actionId the ID of the action to disposed */ protected void disposeUndoRedoAction(String actionId) { OperationHistoryActionHandler action = (OperationHistoryActionHandler) getAction(actionId); if (action != null) { action.dispose(); setAction(actionId, null); } } /** * Returns this editor's viewer's undo manager undo context. * * @return the undo context or <code>null</code> if not available * @since 3.2 */ private IUndoContext getUndoContext() { IUndoManager undoManager= fSourceViewer.getUndoManager(); if (undoManager instanceof IUndoManagerExtension) { return ((IUndoManagerExtension)undoManager).getUndoContext(); } return null; } /* (non-Javadoc) * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent) */ @Override public void propertyChange(PropertyChangeEvent event) { String propertyName= event.getProperty(); if (propertyName.equals(IDebugUIConstants.PREF_DETAIL_PANE_FONT)) { fSourceViewer.getTextWidget().setFont(JFaceResources.getFont(IDebugUIConstants.PREF_DETAIL_PANE_FONT)); } else if (propertyName.equals(IDebugUIConstants.PREF_MAX_DETAIL_LENGTH)) { display(fLastDisplayed); } else if (propertyName.equals(IDebugPreferenceConstants.PREF_DETAIL_PANE_WORD_WRAP)) { fSourceViewer.getTextWidget().setWordWrap(DebugUIPlugin.getDefault().getPreferenceStore().getBoolean(IDebugPreferenceConstants.PREF_DETAIL_PANE_WORD_WRAP)); getAction(DETAIL_WORD_WRAP_ACTION).setChecked(DebugUIPlugin.getDefault().getPreferenceStore().getBoolean(IDebugPreferenceConstants.PREF_DETAIL_PANE_WORD_WRAP)); } } /** * Wrapper class that wraps around an IFindReplaceTarget. Allows the detail pane to scroll * to text selected by the find/replace action. The source viewer treats the text as a single * line, even when the text is wrapped onto several lines so the viewer will not scroll properly * on it's own. See bug 178106. */ class FindReplaceTargetWrapper implements IFindReplaceTarget{ private IFindReplaceTarget fTarget; /** * Constructor * * @param target find/replace target this class will wrap around. */ public FindReplaceTargetWrapper(IFindReplaceTarget target){ fTarget = target; } /* (non-Javadoc) * @see org.eclipse.jface.text.IFindReplaceTarget#canPerformFind() */ @Override public boolean canPerformFind() { return fTarget.canPerformFind(); } /* (non-Javadoc) * @see org.eclipse.jface.text.IFindReplaceTarget#findAndSelect(int, java.lang.String, boolean, boolean, boolean) */ @Override public int findAndSelect(int widgetOffset, String findString, boolean searchForward, boolean caseSensitive, boolean wholeWord) { int position = fTarget.findAndSelect(widgetOffset, findString, searchForward, caseSensitive, wholeWord); // Explicitly tell the widget to show the selection because the viewer thinks the text is all on one line, even if wrapping is turned on. if (fSourceViewer != null){ StyledText text = fSourceViewer.getTextWidget(); if(text != null && !text.isDisposed()) { text.showSelection(); } } return position; } /* (non-Javadoc) * @see org.eclipse.jface.text.IFindReplaceTarget#getSelection() */ @Override public Point getSelection() { return fTarget.getSelection(); } /* (non-Javadoc) * @see org.eclipse.jface.text.IFindReplaceTarget#getSelectionText() */ @Override public String getSelectionText() { return fTarget.getSelectionText(); } /* (non-Javadoc) * @see org.eclipse.jface.text.IFindReplaceTarget#isEditable() */ @Override public boolean isEditable() { return fTarget.isEditable(); } /* (non-Javadoc) * @see org.eclipse.jface.text.IFindReplaceTarget#replaceSelection(java.lang.String) */ @Override public void replaceSelection(String text) { fTarget.replaceSelection(text); } } /* * @see org.eclipse.debug.ui.IDetailPane2#getSelectionProvider() */ @Override public ISelectionProvider getSelectionProvider() { return fSourceViewer.getSelectionProvider(); } }