blob: 0b840932ff4e9064dad88b9581b9dfbce6e2e93a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2017 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.preference.IPreferenceStore;
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.WhitespaceCharacterPainter;
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.editors.text.EditorsUI;
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.AbstractTextEditor;
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$
}
int topIndex = -1;
StyledText styledText = fSourceViewer.getTextWidget();
Point selection = null;
// keep scrolling state in details pane in case
// of longer string over multiple lines
if (containsLineDelimiter(insert)) {
int ti = styledText.getTopIndex();
if (ti == 0) {
int caretOffset = styledText.getCaretOffset();
String topStr = styledText.getText().substring(0, caretOffset);
if (insert.length() > caretOffset) {
String newTopStr = insert.substring(0, caretOffset);
// only when string from start to
// caretOffset has not changed
if (topStr.equals(newTopStr)) {
topIndex = ti;
selection = styledText.getSelection();
}
}
} else if (ti > 0) {
int topOffset = styledText.getOffsetAtLine(ti);
String topStr = styledText.getText().substring(0, topOffset);
if (insert.length() > topOffset) {
String newTopStr = insert.substring(0, topOffset);
// only when string from start to
// topIndex has not changed
if (topStr.equals(newTopStr)) {
topIndex = ti;
selection = styledText.getSelection();
}
}
}
}
IDocument detailDoc = getDetailDocument();
if (fFirst) {
if (insert.equals(detailDoc.get())) {
topIndex = -1; // source not modified,
// no need to set the
// document
} else {
detailDoc.set(insert);
}
fFirst = false;
} else {
detailDoc.replace(length, 0, insert);
}
if (topIndex > -1) {
styledText.setTopIndex(topIndex);
if (selection != null) {
styledText.setSelection(selection);
}
}
} catch (BadLocationException e) {
DebugUIPlugin.log(e);
}
}
return Status.OK_STATUS;
}
private boolean containsLineDelimiter(String str) {
if (str.contains("\n") || str.contains("\r")) { //$NON-NLS-1$ //$NON-NLS-2$
return true;
}
return false;
}
};
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;
private IPropertyChangeListener fPreferenceStorePropertyChangeListener;
private WhitespaceCharacterPainter fWhiteSpacePainter;
/* (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);
toggleWhitespaceCharacterPainter();
installWhitespacePreferenceListener();
}
private void installWhitespacePreferenceListener() {
IPreferenceStore store = EditorsUI.getPreferenceStore();
fPreferenceStorePropertyChangeListener = new IPropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent event) {
String property = event.getProperty();
if (AbstractTextEditor.PREFERENCE_SHOW_WHITESPACE_CHARACTERS.equals(property)) {
toggleWhitespaceCharacterPainter();
}
}
};
store.addPropertyChangeListener(fPreferenceStorePropertyChangeListener);
}
private void removePreferenceListener() {
if (fPreferenceStorePropertyChangeListener != null) {
IPreferenceStore store = EditorsUI.getPreferenceStore();
store.removePropertyChangeListener(fPreferenceStorePropertyChangeListener);
fPreferenceStorePropertyChangeListener = null;
}
}
private void disposeWhitespaceCharacterPainter() {
if (fWhiteSpacePainter != null) {
fSourceViewer.removePainter(fWhiteSpacePainter);
fWhiteSpacePainter.dispose();
fWhiteSpacePainter = null;
}
}
private void toggleWhitespaceCharacterPainter() {
if (fSourceViewer == null || fSourceViewer.getTextWidget() == null) {
return;
}
IPreferenceStore store = EditorsUI.getPreferenceStore();
boolean checked = store.getBoolean(AbstractTextEditor.PREFERENCE_SHOW_WHITESPACE_CHARACTERS);
if (checked) {
fWhiteSpacePainter = new WhitespaceCharacterPainter(fSourceViewer);
fSourceViewer.addPainter(fWhiteSpacePainter);
} else {
if (fWhiteSpacePainter != null) {
fSourceViewer.removePainter(fWhiteSpacePainter);
fWhiteSpacePainter = null;
}
}
}
/**
* 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) {
removePreferenceListener();
disposeWhitespaceCharacterPainter();
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();
}
}