| /******************************************************************************* |
| * Copyright (c) 2006, 2009 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.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; |
| |
| 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) |
| */ |
| 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 = (IWorkbenchAdapter) 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() |
| */ |
| 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) |
| */ |
| 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$ |
| 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; |
| |
| /** |
| * 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) |
| */ |
| 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() { |
| public void documentAboutToBeChanged(DocumentEvent event) {} |
| public void documentChanged(DocumentEvent event) { |
| updateSelectionDependentActions(); |
| } |
| }); |
| |
| // Add the selection listener so selection dependent actions get updated. |
| fSourceViewer.getSelectionProvider().addSelectionChangedListener(new ISelectionChangedListener() { |
| public void selectionChanged(SelectionChangedEvent event) { |
| updateSelectionDependentActions(); |
| } |
| }); |
| |
| // Add a focus listener to update actions when details area gains focus |
| fSourceViewer.getControl().addFocusListener(new FocusAdapter() { |
| 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); |
| |
| } |
| |
| 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(); |
| |
| } |
| }); |
| |
| // 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 = (IHandlerService) 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. |
| */ |
| protected void createDetailContextMenu(Control menuControl) { |
| MenuManager menuMgr= new MenuManager(); |
| menuMgr.setRemoveAllWhenShown(true); |
| menuMgr.addMenuListener(new IMenuListener() { |
| 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) |
| */ |
| 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() |
| */ |
| 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() |
| */ |
| 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 = (IHandlerService) 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() |
| */ |
| public String getDescription() { |
| return DESCRIPTION; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.ui.IDetailPane#getID() |
| */ |
| public String getID() { |
| return ID; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.ui.IDetailPane#getName() |
| */ |
| public String getName() { |
| return NAME; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class) |
| */ |
| public Object getAdapter(Class required) { |
| if (IFindReplaceTarget.class.equals(required)) { |
| return fSourceViewer.getFindReplaceTarget(); |
| } |
| if (ITextViewer.class.equals(required)) { |
| return fSourceViewer; |
| } |
| return null; |
| } |
| |
| /** |
| * Lazily instantiate and return a Document for the detail pane text viewer. |
| */ |
| 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() { |
| |
| public void keyPressed(KeyEvent e) { |
| fStatusLineItem.setText(getCursorPosition()); |
| } |
| |
| public void keyReleased(KeyEvent e) { |
| } |
| |
| public void mouseDoubleClick(MouseEvent e) { |
| } |
| |
| public void mouseDown(MouseEvent e) { |
| } |
| |
| 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) |
| */ |
| 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() |
| */ |
| public boolean canPerformFind() { |
| return fTarget.canPerformFind(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jface.text.IFindReplaceTarget#findAndSelect(int, java.lang.String, boolean, boolean, boolean) |
| */ |
| 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() |
| */ |
| public Point getSelection() { |
| return fTarget.getSelection(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jface.text.IFindReplaceTarget#getSelectionText() |
| */ |
| public String getSelectionText() { |
| return fTarget.getSelectionText(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jface.text.IFindReplaceTarget#isEditable() |
| */ |
| public boolean isEditable() { |
| return fTarget.isEditable(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jface.text.IFindReplaceTarget#replaceSelection(java.lang.String) |
| */ |
| public void replaceSelection(String text) { |
| fTarget.replaceSelection(text); |
| } |
| } |
| |
| /* |
| * @see org.eclipse.debug.ui.IDetailPane2#getSelectionProvider() |
| */ |
| public ISelectionProvider getSelectionProvider() { |
| return fSourceViewer.getSelectionProvider(); |
| } |
| } |