/******************************************************************************* | |
* Copyright (c) 2009, 2010 Wind River Systems, Inc. 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: | |
* Ted R Williams (Wind River Systems, Inc.) - initial implementation | |
* Ted R Williams (Mentor Graphics, Inc.) - address space enhancements | |
* Patrick Chuong (Texas Instruments) - Pin and Clone Supports (331781) | |
*******************************************************************************/ | |
package org.eclipse.cdt.debug.ui.memory.memorybrowser; | |
import java.lang.reflect.Type; | |
import java.math.BigInteger; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.HashMap; | |
import java.util.List; | |
import java.util.Map; | |
import org.eclipse.cdt.debug.core.model.provisional.IMemoryRenderingViewportProvider; | |
import org.eclipse.cdt.debug.core.model.provisional.IMemorySpaceAwareMemoryBlockRetrieval; | |
import org.eclipse.cdt.debug.internal.core.CRequest; | |
import org.eclipse.cdt.debug.ui.memory.memorybrowser.api.IMemoryBrowser; | |
import org.eclipse.cdt.debug.ui.provisional.IRepositionableMemoryRendering2; | |
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.SafeRunner; | |
import org.eclipse.core.runtime.Status; | |
import org.eclipse.debug.core.DebugEvent; | |
import org.eclipse.debug.core.DebugException; | |
import org.eclipse.debug.core.DebugPlugin; | |
import org.eclipse.debug.core.IDebugEventSetListener; | |
import org.eclipse.debug.core.ILaunch; | |
import org.eclipse.debug.core.model.IDebugTarget; | |
import org.eclipse.debug.core.model.IMemoryBlock; | |
import org.eclipse.debug.core.model.IMemoryBlockExtension; | |
import org.eclipse.debug.core.model.IMemoryBlockRetrieval; | |
import org.eclipse.debug.core.model.IMemoryBlockRetrievalExtension; | |
import org.eclipse.debug.core.model.MemoryByte; | |
import org.eclipse.debug.internal.ui.memory.MemoryRenderingManager; | |
import org.eclipse.debug.ui.DebugUITools; | |
import org.eclipse.debug.ui.contexts.DebugContextEvent; | |
import org.eclipse.debug.ui.contexts.IDebugContextListener; | |
import org.eclipse.debug.ui.contexts.IDebugContextService; | |
import org.eclipse.debug.ui.memory.IMemoryRendering; | |
import org.eclipse.debug.ui.memory.IMemoryRenderingContainer; | |
import org.eclipse.debug.ui.memory.IMemoryRenderingSite; | |
import org.eclipse.debug.ui.memory.IMemoryRenderingSynchronizationService; | |
import org.eclipse.debug.ui.memory.IMemoryRenderingType; | |
import org.eclipse.debug.ui.memory.IRepositionableMemoryRendering; | |
import org.eclipse.jface.action.Action; | |
import org.eclipse.jface.action.IAction; | |
import org.eclipse.jface.action.IMenuListener; | |
import org.eclipse.jface.action.IMenuManager; | |
import org.eclipse.jface.action.IToolBarManager; | |
import org.eclipse.jface.action.MenuManager; | |
import org.eclipse.jface.action.Separator; | |
import org.eclipse.jface.dialogs.IDialogConstants; | |
import org.eclipse.jface.preference.IPreferenceStore; | |
import org.eclipse.jface.resource.ColorRegistry; | |
import org.eclipse.jface.resource.JFaceResources; | |
import org.eclipse.jface.util.IPropertyChangeListener; | |
import org.eclipse.jface.util.PropertyChangeEvent; | |
import org.eclipse.jface.util.SafeRunnable; | |
import org.eclipse.jface.viewers.IBasicPropertyConstants; | |
import org.eclipse.jface.viewers.ILabelDecorator; | |
import org.eclipse.jface.viewers.ISelection; | |
import org.eclipse.jface.viewers.ISelectionChangedListener; | |
import org.eclipse.jface.viewers.ISelectionProvider; | |
import org.eclipse.jface.viewers.SelectionChangedEvent; | |
import org.eclipse.jface.viewers.StructuredSelection; | |
import org.eclipse.swt.SWT; | |
import org.eclipse.swt.custom.CTabFolder; | |
import org.eclipse.swt.custom.CTabFolder2Adapter; | |
import org.eclipse.swt.custom.CTabFolderEvent; | |
import org.eclipse.swt.custom.CTabItem; | |
import org.eclipse.swt.custom.StackLayout; | |
import org.eclipse.swt.dnd.DND; | |
import org.eclipse.swt.dnd.DropTarget; | |
import org.eclipse.swt.dnd.DropTargetAdapter; | |
import org.eclipse.swt.dnd.DropTargetEvent; | |
import org.eclipse.swt.dnd.TextTransfer; | |
import org.eclipse.swt.dnd.Transfer; | |
import org.eclipse.swt.events.DisposeEvent; | |
import org.eclipse.swt.events.DisposeListener; | |
import org.eclipse.swt.events.SelectionAdapter; | |
import org.eclipse.swt.events.SelectionEvent; | |
import org.eclipse.swt.events.SelectionListener; | |
import org.eclipse.swt.graphics.Color; | |
import org.eclipse.swt.layout.FormAttachment; | |
import org.eclipse.swt.layout.FormData; | |
import org.eclipse.swt.layout.FormLayout; | |
import org.eclipse.swt.widgets.Combo; | |
import org.eclipse.swt.widgets.Composite; | |
import org.eclipse.swt.widgets.Control; | |
import org.eclipse.swt.widgets.Display; | |
import org.eclipse.swt.widgets.Label; | |
import org.eclipse.swt.widgets.Menu; | |
import org.eclipse.ui.IActionBars; | |
import org.eclipse.ui.IViewSite; | |
import org.eclipse.ui.IWorkbenchActionConstants; | |
import org.eclipse.ui.IWorkbenchPreferenceConstants; | |
import org.eclipse.ui.PlatformUI; | |
import org.eclipse.ui.part.ViewPart; | |
import org.eclipse.ui.progress.UIJob; | |
import org.eclipse.ui.progress.WorkbenchJob; | |
/** | |
* A lightweight rendering container. | |
* | |
* Debug model requirements: | |
* IMemoryBlockExtension (IMemoryBlock not supported) | |
* IMemoryBlockRetrievalExtension | |
* <p> | |
* Rendering requirements: | |
* IRepositionableMemoryRendering | |
* | |
*/ | |
@SuppressWarnings("restriction") /* Debug Platform's Flexibile hierarchy is still provisional */ | |
public class MemoryBrowser extends ViewPart implements IDebugContextListener, IMemoryRenderingSite, IDebugEventSetListener, IMemoryBrowser | |
{ | |
public static final String ID = "org.eclipse.cdt.debug.ui.memory.memorybrowser.MemoryBrowser"; //$NON-NLS-1$ | |
protected StackLayout fStackLayout; | |
private Composite fRenderingsComposite; | |
private GoToAddressBarWidget fGotoAddressBar; | |
private DropTarget fDropTarget; | |
private Control fGotoAddressBarControl; | |
private Combo fGotoMemorySpaceControl; | |
private Label fUnsupportedLabel; | |
private Composite fMainComposite; | |
private String defaultRenderingTypeId = null; | |
private IMemoryRendering fActiveRendering; | |
/** | |
* Every memory retrieval object is given its own tab folder. Typically all | |
* elements of a "process" (process, threads, frames) have the same | |
* retrieval object. | |
*/ | |
private Map<IMemoryBlockRetrieval,CTabFolder> fContextFolders = new HashMap<IMemoryBlockRetrieval,CTabFolder> (); | |
private List<IMemoryRenderingContainer> fCurrentContainers = new ArrayList<IMemoryRenderingContainer>(); | |
private final static String KEY_CONTEXT = "CONTEXT"; //$NON-NLS-1$ | |
private final static String KEY_CONTAINER = "CONTAINER"; //$NON-NLS-1$ | |
private final static String KEY_RENDERING_TYPE = "RENDERING_TYPE"; //$NON-NLS-1$ | |
/** | |
* Property we attach to a CTabItem to track the retrieval object we use to | |
* create memory blocks on the tab's behalf. Value is an | |
* {@link IMemoryBlockRetrieval} | |
*/ | |
private final static String KEY_RETRIEVAL = "RETRIEVAL"; //$NON-NLS-1$ | |
/** | |
* Property we attach to a CTabItem to track the memory space it's | |
* associated with. Value is a memory space ID (String), or null if n/a | |
*/ | |
private final static String KEY_MEMORY_SPACE = "MEMORY_SPACE"; //$NON-NLS-1$ | |
/** | |
* Property we attach to a CTabItem to track what renderings have been | |
* created on its behalf. There will be more than one rendering if the | |
* backend supports memory spaces, there is more than one such space, and | |
* the user has viewed memory in multiple memory spaces within that tab. | |
* The value is a map of memory-space-ID==>IMemoryRendering. | |
*/ | |
private final static String KEY_RENDERINGS = "RENDERINGS"; //$NON-NLS-1$ | |
/** | |
* Property we attach to a CTabItem to track the active rendering in the | |
* tab. The value is an IMemoryRendering. | |
*/ | |
private final static String KEY_RENDERING = "RENDERING"; //$NON-NLS-1$ | |
/** | |
* Property we attach to a CTabItem to track what memory blocks have been | |
* created on its behalf. There can be multiple when dealing with memory | |
* spaces, for the same reasons there can be multiple renderings. There is a | |
* 1:1:1 association between rendering, block and memory space. The value is | |
* a list of IMemoryBlockExtension | |
*/ | |
private final static String KEY_MEMORY_BLOCKS = "MEMORY_BLOCKS"; //$NON-NLS-1$ | |
/** | |
* Property we attach to a CTabItem to track the expression we used to | |
* create memory blocks on the tab's behalf. Value is a String. | |
*/ | |
private final static String KEY_EXPRESSION = "EXPRESSION"; //$NON-NLS-1$ | |
/** | |
* Property we attach to a CTabItem to track the address associated with | |
* KEY_EXPRESSION. Value is a BigInteger | |
*/ | |
private final static String KEY_EXPRESSION_ADDRESS = "EXPRESSION_ADDRESS"; //$NON-NLS-1$ | |
public static final String PREF_DEFAULT_RENDERING = "org.eclipse.cdt.debug.ui.memory.memorybrowser.defaultRendering"; //$NON-NLS-1$ | |
/** | |
* The text we use in the combobox to represent no memory space specification | |
*/ | |
private static final String NA_MEMORY_SPACE_ID = " -----"; //$NON-NLS-1$ | |
public MemoryBrowser() { | |
} | |
public Control getControl() { | |
return fMainComposite; | |
} | |
/* | |
* Handles DnD from other views. | |
*/ | |
private class MemoryDropAdapter extends DropTargetAdapter { | |
/* | |
* @see org.eclipse.swt.dnd.DropTargetListener#drop(org.eclipse.swt.dnd.DropTargetEvent) | |
*/ | |
@Override | |
public void drop(final DropTargetEvent event) { | |
if (TextTransfer.getInstance().isSupportedType( event.currentDataType )) { | |
if ( event.data instanceof String ) { | |
fGotoAddressBar.setExpressionText( (String) event.data ); | |
performGo(false); | |
} | |
} | |
} | |
/* | |
* @see org.eclipse.swt.dnd.DropTargetListener#dragEnter(org.eclipse.swt.dnd.DropTargetEvent) | |
*/ | |
@Override | |
public void dragEnter(DropTargetEvent event) { | |
event.detail = DND.DROP_COPY; | |
event.feedback = DND.FEEDBACK_NONE; | |
} | |
} | |
@Override | |
public void createPartControl(Composite parent) { | |
// set default rendering type. use the traditional rendering if available. fallback on first registered type. | |
// this should eventually be configurable via a preference page. | |
boolean isDefaultRenderingAvailable = false; | |
IPreferenceStore store = MemoryBrowserPlugin.getDefault().getPreferenceStore(); | |
String defaultRendering = store.getString(PREF_DEFAULT_RENDERING); | |
if(defaultRendering == null || defaultRendering.trim().length() == 0) | |
{ | |
defaultRendering = "org.eclipse.cdt.debug.ui.memory.traditional.TraditionalRendering"; //$NON-NLS-1$ | |
} | |
IMemoryRenderingType[] types = getRenderingTypes(); | |
for(final IMemoryRenderingType type : types) | |
{ | |
if(type.getId().equals(defaultRendering)) | |
{ | |
isDefaultRenderingAvailable = true; | |
break; | |
} | |
} | |
if(isDefaultRenderingAvailable) | |
defaultRenderingTypeId = defaultRendering; | |
else if(types.length > 0) | |
defaultRenderingTypeId = types[0].getId(); | |
getSite().setSelectionProvider(new SelectionProviderAdapter()); | |
fMainComposite = new Composite(parent, SWT.NONE); | |
FormLayout layout = new FormLayout(); | |
layout.spacing = 0; | |
fMainComposite.setLayout(layout); | |
fGotoMemorySpaceControl = new Combo(fMainComposite, SWT.DROP_DOWN | SWT.READ_ONLY); | |
fGotoAddressBar = new GoToAddressBarWidget(); | |
fGotoAddressBarControl = fGotoAddressBar.createControl(fMainComposite); | |
if (fDropTarget == null) { | |
fDropTarget = new DropTarget(fGotoAddressBarControl, DND.DROP_COPY | DND.DROP_DEFAULT); | |
fDropTarget.setTransfer( new Transfer[] { TextTransfer.getInstance() }); | |
fDropTarget.addDropListener(new MemoryDropAdapter()); | |
} | |
fGotoAddressBar.getButton(IDialogConstants.OK_ID).addSelectionListener(new SelectionAdapter() { | |
@Override | |
public void widgetSelected(SelectionEvent e) { | |
performGo(false); | |
} | |
}); | |
fGotoAddressBar.getButton(GoToAddressBarWidget.ID_GO_NEW_TAB).addSelectionListener(new SelectionAdapter() { | |
@Override | |
public void widgetSelected(SelectionEvent e) { | |
performGo(true); | |
} | |
}); | |
fGotoAddressBar.getExpressionWidget().addSelectionListener(new SelectionAdapter() { | |
@Override | |
public void widgetDefaultSelected(SelectionEvent e) { | |
performGo(false); | |
} | |
@Override | |
public void widgetSelected(SelectionEvent e) { | |
performGo(false); | |
} | |
}); | |
fGotoMemorySpaceControl.addSelectionListener(new SelectionAdapter() { | |
@Override | |
public void widgetSelected(SelectionEvent e) { | |
if(fGotoMemorySpaceControl.getItemCount() >= 2) { | |
final CTabFolder activeFolder = (CTabFolder) fStackLayout.topControl; | |
if (activeFolder != null) { | |
final Object context = activeFolder.getData(KEY_CONTEXT); | |
fGotoAddressBar.loadSavedExpressions(context, fGotoMemorySpaceControl.getText()); | |
} | |
} | |
} | |
}); | |
FormData data = new FormData(); | |
data.top = new FormAttachment(0); | |
data.left = new FormAttachment(fGotoMemorySpaceControl); | |
data.right = new FormAttachment(100); | |
fGotoAddressBarControl.setLayoutData(data); | |
fRenderingsComposite = new Composite(fMainComposite, SWT.NONE); | |
data = new FormData(); | |
data.top = new FormAttachment(fGotoAddressBarControl); | |
data.left = new FormAttachment(0); | |
data.right = new FormAttachment(100); | |
data.bottom = new FormAttachment(100); | |
fRenderingsComposite.setLayoutData(data); | |
fStackLayout = new StackLayout(); | |
fRenderingsComposite.setLayout(fStackLayout); | |
fUnsupportedLabel = new Label(fRenderingsComposite, SWT.NONE); | |
fUnsupportedLabel.setText(""); //$NON-NLS-1$ | |
handleUnsupportedSelection(); | |
PlatformUI.getWorkbench().getHelpSystem().setHelp(fMainComposite, MemoryBrowserPlugin.PLUGIN_ID); | |
makeActions(); | |
hookContextMenu(); | |
contributeToActionBars(); | |
Object selection = null; | |
IDebugContextService contextService = | |
DebugUITools.getDebugContextManager().getContextService(getSite().getWorkbenchWindow()); | |
if (isBug145635Patched()) { | |
String presentationContextId = getPresentationContextId(); | |
contextService.addDebugContextListener(this, presentationContextId); | |
selection = contextService.getActiveContext(presentationContextId); | |
} else { | |
DebugUITools.addPartDebugContextListener(getSite(), this); | |
selection = contextService.getActiveContext(getSite().getId(), ((IViewSite)getSite()).getSecondaryId()); | |
} | |
DebugPlugin.getDefault().addDebugEventListener(this); | |
if(selection instanceof StructuredSelection) | |
handleDebugContextChanged(((StructuredSelection) selection).getFirstElement()); | |
} | |
private boolean isBug145635Patched() { | |
Type[] managerTypes = DebugUITools.getDebugContextManager().getClass().getGenericInterfaces(); | |
for (int i = 0; i < managerTypes.length; i++) { | |
if (managerTypes[i] instanceof Class<?>) { | |
Class<?> clazz = (Class<?>)managerTypes[i]; | |
if ("org.eclipse.debug.ui.contexts.IBug145635Marker".equals(clazz.getName()) ) { //$NON-NLS-1$ | |
return true; | |
} | |
} | |
} | |
return false; | |
} | |
/** | |
* Clears the expression history for the active tab | |
*/ | |
public void clearExpressionHistoryForActiveTab() { | |
final CTabFolder activeFolder = (CTabFolder) fStackLayout.topControl; | |
if (activeFolder != null) { | |
final Object context = activeFolder.getData(KEY_CONTEXT); | |
fGotoAddressBar.clearExpressionHistory(context); | |
} | |
} | |
/** | |
* Returns the presentation context id for this view. Used to support the | |
* pin and clone feature patch from bug 145635. | |
* | |
* @return context id | |
*/ | |
private String getPresentationContextId() { | |
IViewSite site = (IViewSite)getSite(); | |
return site.getId() + (site.getSecondaryId() != null ? (":" + site.getSecondaryId()) : ""); //$NON-NLS-1$ //$NON-NLS-2$ | |
} | |
@Override | |
public void dispose() { | |
if (fDropTarget != null) { | |
fDropTarget.dispose(); | |
fDropTarget = null; | |
} | |
DebugPlugin.getDefault().removeDebugEventListener(this); | |
IDebugContextService contextService = | |
DebugUITools.getDebugContextManager().getContextService(getSite().getWorkbenchWindow()); | |
if (isBug145635Patched()) { | |
String presentationContextId = getPresentationContextId(); | |
contextService.removeDebugContextListener(this, presentationContextId); | |
} else { | |
DebugUITools.removePartDebugContextListener(getSite(), this); | |
} | |
super.dispose(); | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.debug.core.IDebugEventSetListener#handleDebugEvents(org.eclipse.debug.core.DebugEvent[]) | |
*/ | |
public void handleDebugEvents(DebugEvent[] events) { | |
for (DebugEvent event: events) { | |
Object source = event.getSource(); | |
if (event.getKind() == DebugEvent.TERMINATE && source instanceof IMemoryBlockRetrieval) { | |
releaseTabFolder((IMemoryBlockRetrieval)source); | |
} | |
} | |
} | |
public IMemoryRenderingContainer getContainer(String id) { | |
return null; | |
} | |
public IMemoryRenderingContainer[] getMemoryRenderingContainers() { | |
IMemoryRenderingContainer[] containerList = new IMemoryRenderingContainer[fCurrentContainers.size()]; | |
for ( int idx = 0 ; idx < fCurrentContainers.size() ; idx ++ ) { | |
containerList[ idx ] = fCurrentContainers.get( idx ); | |
} | |
return containerList; | |
} | |
public IMemoryRenderingSynchronizationService getSynchronizationService() { | |
return null; | |
} | |
private void handleUnsupportedSelection() { | |
fStackLayout.topControl = fUnsupportedLabel; | |
if(!fGotoAddressBarControl.isDisposed()) { | |
fGotoAddressBarControl.setVisible(false); | |
} | |
if(!fGotoMemorySpaceControl.isDisposed()) { | |
fGotoMemorySpaceControl.setVisible(false); | |
} | |
} | |
private void performGo(boolean inNewTab) { | |
// Index zero is the 'auto' (n/a) memory space entry, unless the backend | |
// said we need to force a memory space selection | |
String memorySpace = null; | |
if (fGotoMemorySpaceControl.isVisible()) { | |
memorySpace = fGotoMemorySpaceControl.getText(); | |
if (memorySpace.equals(NA_MEMORY_SPACE_ID)) { | |
memorySpace = null; | |
} | |
assert (memorySpace == null) || (memorySpace.length() > 0); | |
} | |
String expression = fGotoAddressBar.getExpressionText(); | |
if (expression.length() > 0) { | |
final CTabFolder activeFolder = (CTabFolder) fStackLayout.topControl; | |
Object context = null; | |
if (activeFolder != null) { | |
context = activeFolder.getData(KEY_CONTEXT); | |
} | |
fGotoAddressBar.addExpressionToHistory(context, expression, memorySpace); | |
performGo(inNewTab, expression, memorySpace); | |
} | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.cdt.debug.ui.memory.memorybrowser.api.IMemoryBrowser#getActiveRetrieval() | |
*/ | |
public IMemoryBlockRetrieval getActiveRetrieval() { | |
final CTabFolder activeFolder = (CTabFolder) fStackLayout.topControl; | |
if (activeFolder == null) | |
return null; | |
return (IMemoryBlockRetrieval) activeFolder.getData(KEY_RETRIEVAL); | |
} | |
public void performGo(boolean inNewTab, final String expression, final String memorySpaceId) { | |
final CTabFolder activeFolder = (CTabFolder) fStackLayout.topControl; | |
if (activeFolder != null) { | |
final IMemoryBlockRetrieval retrieval = (IMemoryBlockRetrieval) activeFolder.getData(KEY_RETRIEVAL); | |
final Object context = activeFolder.getData(KEY_CONTEXT); | |
IMemoryRendering rendering = null; | |
CTabItem item = activeFolder.getSelection(); | |
if (inNewTab || item == null) | |
{ | |
try { | |
item = createTab(activeFolder, activeFolder.getSelectionIndex() + 1); | |
rendering = populateTabWithRendering(item, retrieval, context, memorySpaceId, expression); | |
fContextFolders.put(retrieval, activeFolder); | |
activeFolder.setSelection(item); | |
getSite().getSelectionProvider().setSelection(new StructuredSelection(item.getData(KEY_RENDERING))); | |
handleTabActivated(item); | |
} catch (DebugException e1) { | |
fGotoAddressBar.handleExpressionStatus(new Status(Status.ERROR, MemoryBrowserPlugin.PLUGIN_ID, | |
Messages.getString("MemoryBrowser.FailedToGoToAddressTitle"), e1)); //$NON-NLS-1$ | |
if (item != null) { | |
item.dispose(); | |
} | |
return; | |
} | |
} else { | |
// Tab is already in place. However, the user may have selected | |
// a different memory space. If so, that requires us to switch | |
// out the rendering in the tab with either a new one or an | |
// existing one already associated with that memory space. | |
String oldMemorySpaceId = (String)activeFolder.getSelection().getData(KEY_MEMORY_SPACE); | |
assert oldMemorySpaceId == null || !oldMemorySpaceId.equals(NA_MEMORY_SPACE_ID) : "should be null reference or an explicit, valid memory space ID (not including '----')"; //$NON-NLS-1$ | |
if ((oldMemorySpaceId != null && !oldMemorySpaceId.equals(memorySpaceId)) | |
|| (oldMemorySpaceId == null && memorySpaceId != null)) { | |
try { | |
rendering = populateTabWithRendering(item, retrieval, context, memorySpaceId, expression); | |
activeFolder.setSelection(item); | |
getSite().getSelectionProvider().setSelection(new StructuredSelection(item.getData(KEY_RENDERING))); | |
handleTabActivated(item); | |
} catch (DebugException e) { | |
fGotoAddressBar.handleExpressionStatus(new Status(Status.ERROR, MemoryBrowserPlugin.PLUGIN_ID, | |
Messages.getString("MemoryBrowser.FailedToGoToAddressTitle"), e)); //$NON-NLS-1$ | |
return; | |
} | |
} | |
} | |
if (rendering == null) { | |
rendering = (IRepositionableMemoryRendering) activeFolder.getSelection().getData(KEY_RENDERING); | |
} | |
if (retrieval instanceof IMemoryBlockRetrievalExtension && | |
rendering instanceof IRepositionableMemoryRendering) { | |
final IRepositionableMemoryRendering renderingFinal = (IRepositionableMemoryRendering) rendering; | |
new Thread() { | |
@Override | |
public void run() { | |
try { | |
final BigInteger newBase = getExpressionAddress(retrieval, expression, context, memorySpaceId); | |
IMemoryBlockExtension block = (IMemoryBlockExtension) renderingFinal.getMemoryBlock(); | |
if (block.supportBaseAddressModification()) { | |
block.setBaseAddress(newBase); | |
} | |
if(renderingFinal instanceof IRepositionableMemoryRendering2) { | |
((IRepositionableMemoryRendering2)renderingFinal).goToAddress(newBase, expression); | |
} | |
else { | |
renderingFinal.goToAddress(newBase); | |
} | |
runOnUIThread(new Runnable(){ | |
public void run() { | |
CTabItem selection = activeFolder.getSelection(); | |
selection.setData(KEY_EXPRESSION, expression); | |
selection.setData(KEY_EXPRESSION_ADDRESS, newBase); | |
fGotoAddressBar.handleExpressionStatus(Status.OK_STATUS); | |
updateLabel(selection, renderingFinal); | |
} | |
}); | |
} catch (final DebugException e1) { | |
// widgets update require Display | |
runOnUIThread(new Runnable(){ | |
public void run() { | |
fGotoAddressBar.handleExpressionStatus(new Status(Status.ERROR, MemoryBrowserPlugin.PLUGIN_ID, | |
Messages.getString("MemoryBrowser.FailedToGoToAddressTitle"), e1)); //$NON-NLS-1$ | |
} | |
}); | |
} | |
} | |
}.start(); | |
} | |
} | |
} | |
private void updateLabel(CTabItem tab, IMemoryRendering rendering) { | |
// The default is to use the label provided by the base rendering | |
// interface. | |
String label = rendering.getLabel(); | |
// If the rendering provides access to its viewport address (the first | |
// address shown in the rendering, subject to scrolling), display that | |
// in the tab rather than the expression that was used when the tab was | |
// first created. | |
if (rendering instanceof IMemoryRenderingViewportProvider) { | |
BigInteger viewportAddress = ((IMemoryRenderingViewportProvider)rendering).getViewportAddress(); | |
// The base label generation puts the rendering type name in "<>" and | |
// appends it to the label. Fish that out | |
String renderingType = null; | |
int i = label.indexOf('<'); | |
if (i >= 0) { | |
renderingType = label.substring(i); | |
} | |
label = null; | |
// If a memory space is involved, we want to include its ID in the label | |
String memorySpaceID = (String)tab.getData(KEY_MEMORY_SPACE); | |
if (memorySpaceID != null) { | |
IMemoryBlockRetrieval retrieval = (IMemoryBlockRetrieval) tab.getParent().getData(KEY_RETRIEVAL); | |
if (retrieval instanceof IMemorySpaceAwareMemoryBlockRetrieval) { | |
label = ((IMemorySpaceAwareMemoryBlockRetrieval)retrieval).encodeAddress("0x" + viewportAddress.toString(16), memorySpaceID); | |
} | |
} | |
if (label == null) { | |
label = "0x" + viewportAddress.toString(16); | |
} | |
// If the expression that was went to ("Go") is not a hex address, | |
// or it is but the user has scrolled/paged, then show the | |
// expression after the viewport hex address. Additionally, if some | |
// scrolling/paging has moved the viewport, also show the relative | |
// displacement. E.g., | |
// "0x10020 - gSomeVar(+20) <Traditional>" | |
// (for a tab where the user did a "Go" to "gSomeVar" then paged | |
// down, and where gSomeVar is at 0x10000) | |
// | |
String expression = (String)tab.getData(KEY_EXPRESSION); | |
BigInteger evaluatedAddress = (BigInteger)tab.getData(KEY_EXPRESSION_ADDRESS); | |
if(expression != null && !expression.equals("0x" + viewportAddress.toString(16))) { | |
label += " - " + expression; | |
BigInteger delta = evaluatedAddress.subtract(viewportAddress); | |
if (!delta.equals(BigInteger.ZERO)) { | |
label += "("; | |
label += delta.signum() < 0 ? '+' : '-'; | |
label += "0x" + delta.abs().toString(16) +")"; | |
} | |
} | |
label += ' ' + renderingType;; | |
// Allow the memory block to customize the label. The platform's | |
// Memory view support this (it was done in the call to | |
// rendering.getLabel() above) | |
IMemoryBlock block = rendering.getMemoryBlock(); | |
ILabelDecorator labelDec = (ILabelDecorator)block.getAdapter(ILabelDecorator.class); | |
if (labelDec != null) { | |
String newLabel = labelDec.decorateText(label, rendering); | |
if (newLabel != null) { | |
label = newLabel; | |
} | |
} | |
} | |
tab.setText(label); | |
} | |
private CTabFolder createTabFolder(Composite parent) | |
{ | |
final CTabFolder folder = new CTabFolder(parent, SWT.NO_REDRAW_RESIZE | SWT.NO_TRIM | SWT.FLAT); | |
ColorRegistry reg = JFaceResources.getColorRegistry(); | |
Color c1 = reg.get("org.eclipse.ui.workbench.ACTIVE_TAB_BG_START"), //$NON-NLS-1$ | |
c2 = reg.get("org.eclipse.ui.workbench.ACTIVE_TAB_BG_END"); //$NON-NLS-1$ | |
folder.setSelectionBackground(new Color[] {c1, c2}, new int[] {100}, true); | |
folder.setSelectionForeground(reg.get("org.eclipse.ui.workbench.ACTIVE_TAB_TEXT_COLOR")); //$NON-NLS-1$ | |
folder.setSimple(PlatformUI.getPreferenceStore().getBoolean(IWorkbenchPreferenceConstants.SHOW_TRADITIONAL_STYLE_TABS)); | |
folder.setBorderVisible(true); | |
// listener to dispose rendering resources for each closed tab | |
folder.addCTabFolder2Listener(new CTabFolder2Adapter() { | |
@Override | |
public void close(CTabFolderEvent event) { | |
event.doit = true; | |
CTabItem item = (CTabItem) event.item; | |
disposeTab(item); | |
} | |
}); | |
// listener to dispose rendering resources for all tab items when view part is closed | |
folder.addDisposeListener(new DisposeListener() { | |
public void widgetDisposed(DisposeEvent e) { | |
for(CTabItem tab : folder.getItems()) { | |
disposeTab(tab); | |
} | |
folder.removeDisposeListener(this); | |
} | |
}); | |
return folder; | |
} | |
// these utility methods allow us restrict the scope of the unavoidable @SuppressWarnings | |
@SuppressWarnings("unchecked") | |
private static Map<String, IMemoryRendering> getRenderings(CTabItem tabItem) { | |
return (Map<String, IMemoryRendering>)tabItem.getData(KEY_RENDERINGS); | |
} | |
@SuppressWarnings("unchecked") | |
private static List<IMemoryBlockExtension> getMemoryBlocks(CTabItem tabItem) { | |
return (List<IMemoryBlockExtension>)tabItem.getData(KEY_MEMORY_BLOCKS); | |
} | |
/** | |
* dispose rendering resources associated with the tab item | |
* @param item | |
*/ | |
private void disposeTab(CTabItem item ) { | |
if (item.isDisposed()) | |
return; | |
IMemoryRenderingContainer container = (IMemoryRenderingContainer) item.getData(KEY_CONTAINER); | |
fCurrentContainers.remove( container ); | |
Map<String, IMemoryRendering> map = getRenderings(item); | |
Collection<IMemoryRendering> renderings = map.values(); | |
for (IMemoryRendering rendering : renderings) { | |
// always deactivate rendering before disposing it. | |
rendering.deactivated(); | |
rendering.dispose(); | |
if (rendering == fActiveRendering) { | |
fActiveRendering = null; | |
getSite().getSelectionProvider().setSelection(new StructuredSelection()); | |
} | |
} | |
map.clear(); | |
List<IMemoryBlockExtension> blocks = getMemoryBlocks(item); | |
for (IMemoryBlockExtension block : blocks) { | |
try { | |
block.dispose(); | |
} catch (DebugException e) { | |
MemoryBrowserPlugin.getDefault().getLog().log(new Status(Status.ERROR, MemoryBrowserPlugin.PLUGIN_ID, "Could not dispose memory block", e)); //$NON-NLS-1$ | |
} | |
} | |
blocks.clear(); | |
} | |
private CTabItem createTab(CTabFolder tabFolder, int index) { | |
int swtStyle = SWT.CLOSE; | |
CTabItem tab = new CTabItem(tabFolder, swtStyle, index); | |
tab.setData(KEY_RENDERINGS, new HashMap<String, IMemoryRendering>()); | |
tab.setData(KEY_MEMORY_BLOCKS, new ArrayList<IMemoryBlock>()); | |
return tab; | |
} | |
private void hookContextMenu() { | |
MenuManager menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$ | |
menuMgr.setRemoveAllWhenShown(true); | |
menuMgr.addMenuListener(new IMenuListener() { | |
public void menuAboutToShow(IMenuManager manager) { | |
MemoryBrowser.this.fillContextMenu(manager); | |
} | |
}); | |
Menu menu = menuMgr.createContextMenu(getControl()); | |
getControl().setMenu(menu); | |
} | |
private void contributeToActionBars() { | |
IActionBars bars = getViewSite().getActionBars(); | |
fillLocalPullDown(bars.getMenuManager()); | |
fillLocalToolBar(bars.getToolBarManager()); | |
} | |
private void fillLocalPullDown(IMenuManager manager) { | |
MenuManager sub = new MenuManager(Messages.getString("MemoryBrowser.DefaultRendering")); //$NON-NLS-1$ | |
for(final IMemoryRenderingType type : getRenderingTypes()) | |
{ | |
final Action action = new Action( | |
type.getLabel(), IAction.AS_RADIO_BUTTON) | |
{ | |
@Override | |
public void run() | |
{ | |
setDefaultRenderingTypeId(type.getId()); | |
} | |
}; | |
action.setChecked(type.getId().equals(getDefaultRenderingTypeId())); | |
sub.add(action); | |
} | |
manager.add(sub); | |
manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); | |
} | |
private void fillContextMenu(IMenuManager manager) { | |
manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); | |
} | |
private void fillLocalToolBar(IToolBarManager manager) { | |
manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); | |
} | |
private void makeActions() { | |
} | |
private IMemoryRenderingType[] getRenderingTypes() | |
{ | |
return MemoryRenderingManager.getDefault().getRenderingTypes(new IMemoryBlockExtension(){ | |
public void connect(Object client) {} | |
public void disconnect(Object client) {} | |
public void dispose() throws DebugException {} | |
public int getAddressSize() throws DebugException { return 0; } | |
public int getAddressableSize() throws DebugException { return 0; } | |
public BigInteger getBigBaseAddress() throws DebugException { return null; } | |
public BigInteger getBigLength() throws DebugException { return null; } | |
public MemoryByte[] getBytesFromAddress(BigInteger address, long units) throws DebugException { return null; } | |
public MemoryByte[] getBytesFromOffset(BigInteger unitOffset, long addressableUnits) throws DebugException { return null; } | |
public Object[] getConnections() { return null; } | |
public String getExpression() { return null; } | |
public BigInteger getMemoryBlockEndAddress() throws DebugException { return null; } | |
public IMemoryBlockRetrieval getMemoryBlockRetrieval() { return null; } | |
public BigInteger getMemoryBlockStartAddress() throws DebugException { return null; } | |
public void setBaseAddress(BigInteger address) throws DebugException {} | |
public void setValue(BigInteger offset, byte[] bytes) throws DebugException {} | |
public boolean supportBaseAddressModification() throws DebugException { return false; } | |
public boolean supportsChangeManagement() { return false; } | |
public byte[] getBytes() throws DebugException { return null; } | |
public long getLength() { return 0; } | |
public long getStartAddress() { return 0; } | |
public void setValue(long offset, byte[] bytes) throws DebugException {} | |
public boolean supportsValueModification() { return false; } | |
public IDebugTarget getDebugTarget() { return null; } | |
public ILaunch getLaunch() { return null; } | |
public String getModelIdentifier() { return null; } | |
@SuppressWarnings("rawtypes") | |
public Object getAdapter(Class adapter) { return null; } | |
}); | |
} | |
@Override | |
public void setFocus() { | |
getControl().setFocus(); | |
} | |
public void debugContextChanged(DebugContextEvent event) { | |
handleDebugContextChanged(((StructuredSelection) event.getContext()).getFirstElement()); | |
} | |
private final class MemoryBrowserRenderingContainer implements | |
IMemoryRenderingContainer { | |
private final List<IMemoryRendering> renderings = new ArrayList<IMemoryRendering>(); | |
private MemoryBrowserRenderingContainer() { | |
} | |
public void addMemoryRendering(IMemoryRendering rendering) { | |
// do not allow duplicated objects | |
if (!renderings.contains(rendering)) { | |
renderings.add(rendering); | |
} | |
} | |
public IMemoryRendering getActiveRendering() { | |
return renderings.isEmpty() ? null : renderings.get(renderings.size() -1); | |
} | |
public String getId() { | |
return "???"; //$NON-NLS-1$ | |
} | |
public String getLabel() { | |
IMemoryRendering rendering = getActiveRendering(); | |
return rendering != null ? rendering.getLabel() : null; | |
} | |
public IMemoryRenderingSite getMemoryRenderingSite() { | |
return MemoryBrowser.this; | |
} | |
public IMemoryRendering[] getRenderings() { | |
return renderings.toArray(new IMemoryRendering[renderings.size()]); | |
} | |
public void removeMemoryRendering(IMemoryRendering rendering) { | |
renderings.remove(rendering); | |
} | |
} | |
private final class RenderingPropertyChangeListener implements | |
IPropertyChangeListener { | |
private final CTabItem tab; | |
private final IMemoryRendering newRendering; | |
private RenderingPropertyChangeListener(CTabItem tab, | |
IMemoryRendering newRendering) { | |
this.tab = tab; | |
this.newRendering = newRendering; | |
} | |
public void propertyChange(final PropertyChangeEvent event) { | |
WorkbenchJob job = new WorkbenchJob("MemoryBrowser PropertyChanged") { //$NON-NLS-1$ | |
@Override | |
public IStatus runInUIThread(IProgressMonitor monitor) { | |
if(tab.isDisposed()) | |
return Status.OK_STATUS; | |
if (event.getProperty().equals(IBasicPropertyConstants.P_TEXT)) | |
updateLabel(tab, newRendering); | |
return Status.OK_STATUS; | |
} | |
}; | |
job.setSystem(true); | |
job.schedule(); | |
} | |
} | |
private class GetMemorySpacesRequest extends CRequest implements IMemorySpaceAwareMemoryBlockRetrieval.GetMemorySpacesRequest { | |
String [] fMemorySpaces; | |
public String[] getMemorySpaces() { | |
return fMemorySpaces; | |
} | |
public void setMemorySpaces(String[] memorySpaceIds) { | |
fMemorySpaces = memorySpaceIds; | |
} | |
} | |
public void handleDebugContextChanged(final Object context) { | |
if(defaultRenderingTypeId == null) | |
return; | |
IAdaptable adaptable = null; | |
IMemoryBlockRetrieval retrieval = null; | |
ILaunch launch = null; | |
if(context instanceof IAdaptable) | |
{ | |
adaptable = (IAdaptable) context; | |
retrieval = ((IMemoryBlockRetrieval) adaptable.getAdapter(IMemoryBlockRetrieval.class)); | |
launch = ((ILaunch) adaptable.getAdapter(ILaunch.class)); | |
} | |
if(retrieval != null && launch != null && !launch.isTerminated()) { | |
if (retrieval instanceof IMemorySpaceAwareMemoryBlockRetrieval) { | |
final IMemoryBlockRetrieval _retrieval = retrieval; | |
((IMemorySpaceAwareMemoryBlockRetrieval)retrieval).getMemorySpaces(context, new GetMemorySpacesRequest(){ | |
@Override | |
public void done() { | |
updateTab(_retrieval, context, isSuccess() ? getMemorySpaces() : new String[0]); | |
} | |
}); | |
} | |
else { | |
updateTab(retrieval, context, new String[0]); | |
} | |
} | |
else { | |
handleUnsupportedSelection(); | |
} | |
fGotoMemorySpaceControl.pack(true); | |
fStackLayout.topControl.getParent().layout(true); | |
} | |
/** | |
* Called to update the tab once the asynchronous query for memory spaces | |
* has returned a result. | |
* | |
* @param retrieval | |
* the retrieval object associated with the newly active debug | |
* context | |
* @param context | |
* the newly active context | |
* @param memorySpaces | |
* the memory spaces, if applicable. Otherwise an empty array. | |
*/ | |
private void updateTab(final IMemoryBlockRetrieval retrieval, final Object context, final String[] memorySpaces) { | |
// GUI activity must be on the main thread | |
runOnUIThread(new Runnable(){ | |
public void run() { | |
if (fGotoAddressBarControl.isDisposed() || fGotoMemorySpaceControl.isDisposed()) { | |
return; | |
} | |
fGotoAddressBarControl.setVisible(true); | |
// If we've already created a tab folder for this retrieval | |
// object, bring it to the forefront. Otherwise create the | |
// folder. | |
CTabFolder tabFolder = fContextFolders.get(retrieval); | |
if(tabFolder != null) { | |
fStackLayout.topControl = tabFolder; | |
CTabItem tabItem = (CTabItem) tabFolder.getSelection(); | |
if ( tabItem != null ) { | |
getSite().getSelectionProvider().setSelection(new StructuredSelection(tabItem.getData(KEY_RENDERING))); | |
} | |
handleTabActivated(tabItem); | |
} | |
else { | |
tabFolder = createTabFolder(fRenderingsComposite); | |
tabFolder.addSelectionListener(new SelectionListener() { | |
public void widgetDefaultSelected(SelectionEvent e) {} | |
public void widgetSelected(SelectionEvent e) { | |
CTabItem tabItem = (CTabItem)e.item; | |
updateMemorySpaceControlSelection(tabItem); | |
fGotoAddressBar.loadSavedExpressions(context, fGotoMemorySpaceControl.isVisible() ? fGotoMemorySpaceControl.getText() : null); | |
getSite().getSelectionProvider().setSelection(new StructuredSelection(tabItem.getData(KEY_RENDERING))); | |
handleTabActivated(tabItem); | |
} | |
}); | |
tabFolder.setData(KEY_RETRIEVAL, retrieval); | |
fContextFolders.put(retrieval, tabFolder); | |
fStackLayout.topControl = tabFolder; | |
} | |
// update debug context to the new selection | |
tabFolder.setData(KEY_CONTEXT, context); | |
final CTabFolder activeFolder = tabFolder; | |
if (!activeFolder.equals(tabFolder)) { | |
return; | |
} | |
// Don't expose the memory spaces widget if there are none or | |
// only one memory space involved. | |
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=309032#c50 | |
if (memorySpaces.length >= 2) { | |
fGotoMemorySpaceControl.setItems(memorySpaces); | |
// Add the '----' (N/A) entry unless the retrieval object | |
// says it requires a memory space ID in all cases | |
boolean addNA = true; | |
if (retrieval instanceof IMemorySpaceAwareMemoryBlockRetrieval) { | |
addNA = !((IMemorySpaceAwareMemoryBlockRetrieval)retrieval).creatingBlockRequiresMemorySpaceID(); | |
} | |
if (addNA) { | |
fGotoMemorySpaceControl.add(NA_MEMORY_SPACE_ID, 0); | |
} | |
setMemorySpaceControlVisible(true); | |
} | |
else { | |
fGotoMemorySpaceControl.setItems(new String[0]); | |
setMemorySpaceControlVisible(false); | |
} | |
updateMemorySpaceControlSelection(activeFolder.getSelection()); | |
fGotoAddressBar.loadSavedExpressions(context, fGotoMemorySpaceControl.isVisible() ? fGotoMemorySpaceControl.getText() : null); | |
fStackLayout.topControl.getParent().layout(true); | |
} | |
}); | |
} | |
/** | |
* Update the expression text in goto address widget to reflect the memory | |
* rendering expression | |
* | |
* @param item | |
* the active tab; may be null if in a "fresh" memory browser instance | |
*/ | |
protected void updateExpression(CTabItem activeFolder) { | |
String expression = (activeFolder != null) ? (String) activeFolder.getData(KEY_EXPRESSION) : null; | |
if (expression != null) { | |
fGotoAddressBar.setExpressionText(expression); | |
} | |
} | |
protected final void handleTabActivated(CTabItem item) { | |
if (item != null) { | |
updateActiveRendering((IMemoryRendering) item.getData(KEY_RENDERING)); | |
} | |
} | |
private void updateActiveRendering(IMemoryRendering rendering) { | |
if (fActiveRendering == rendering) { | |
return; | |
} | |
if (fActiveRendering != null) { | |
fActiveRendering.deactivated(); | |
fActiveRendering.becomesHidden(); | |
} | |
if (rendering != null) { | |
rendering.activated(); | |
rendering.becomesVisible(); | |
} | |
fActiveRendering = rendering; | |
} | |
private void setMemorySpaceControlVisible(boolean visible) { | |
FormData data = (FormData)fGotoAddressBarControl.getLayoutData(); | |
if (visible) { | |
data.left = new FormAttachment(fGotoMemorySpaceControl); | |
} | |
else { | |
data.left = new FormAttachment(0); | |
} | |
fGotoMemorySpaceControl.setVisible(visible); | |
} | |
/** | |
* Update the selection in the memory space combobox to reflect the memory | |
* space being shown in the given tab | |
* | |
* @param item | |
* the active tab; may be null if in a "fresh" memory browser instance | |
*/ | |
private void updateMemorySpaceControlSelection(CTabItem item) { | |
String[] memorySpaces = fGotoMemorySpaceControl.getItems(); | |
if (memorySpaces.length > 0 ) { | |
// Don't assume that the memory space previously set in the tab | |
// is one of the ones now available. If it isn't, then select | |
// the first available one and update the tab data | |
boolean foundIt = false; | |
if (item != null) { | |
String currentMemorySpace = (String) item.getData(KEY_MEMORY_SPACE); | |
if (currentMemorySpace != null) { | |
assert currentMemorySpace.length() > 0; | |
for (String memorySpace : memorySpaces) { | |
if (memorySpace.equals(currentMemorySpace)) { | |
foundIt = true; | |
fGotoMemorySpaceControl.setText(currentMemorySpace); | |
break; | |
} | |
} | |
} | |
} | |
if (!foundIt) { | |
fGotoMemorySpaceControl.select(0); | |
if (item != null) { | |
item.setData(KEY_MEMORY_SPACE, null); | |
} | |
} | |
fGotoMemorySpaceControl.setVisible(true); | |
} | |
else { | |
fGotoMemorySpaceControl.setVisible(false); | |
} | |
fGotoMemorySpaceControl.getParent().layout(true); | |
} | |
private String getDefaultRenderingTypeId() | |
{ | |
return defaultRenderingTypeId; | |
} | |
public void setDefaultRenderingTypeId(String id) | |
{ | |
defaultRenderingTypeId = id; | |
IPreferenceStore store = MemoryBrowserPlugin.getDefault().getPreferenceStore(); | |
store.setValue(PREF_DEFAULT_RENDERING, defaultRenderingTypeId); | |
} | |
/** | |
* Populate given tab with a rendering positioned at specified expression and memory space. | |
* Will create a new rendering if one does not exist for the given memory space | |
* | |
* @param tab item to populate | |
* @param retrieval memory service to retrieve memory block from | |
* @param context memory block would be retrieved | |
* @param memorySpaceId of the expression or null if not supported | |
* @param expression from where to retrieve the memory block | |
* @return return the memory rendering or null if could not be created | |
* | |
* @throws DebugException if could not retrieve memory block (e.g. invalid expression) | |
*/ | |
private IMemoryRendering populateTabWithRendering(final CTabItem tab, final IMemoryBlockRetrieval retrieval, Object context, String memorySpaceId, String expression) throws DebugException { | |
IMemoryRenderingType type = DebugUITools.getMemoryRenderingManager().getRenderingType(getDefaultRenderingTypeId()); | |
IMemoryRenderingContainer container = (IMemoryRenderingContainer)tab.getData(KEY_CONTAINER); | |
if (container == null) { | |
container = new MemoryBrowserRenderingContainer(); | |
fCurrentContainers.add(container); | |
} | |
IMemoryRendering rendering = getRenderings(tab).get(memorySpaceId); | |
if (rendering == null) { | |
// No rendering yet. Create rendering and associated memory block. | |
// createMemoryBlock will throw if expression cannot be resolved | |
IMemoryBlockExtension block = createMemoryBlock(retrieval, expression, context, memorySpaceId); | |
try { | |
rendering = type.createRendering(); | |
} catch (CoreException e) { | |
MemoryBrowserPlugin.getDefault().getLog().log(new Status(Status.ERROR, MemoryBrowserPlugin.PLUGIN_ID, "", e)); //$NON-NLS-1$ | |
return null; | |
} | |
rendering.init(container, block); | |
container.addMemoryRendering(rendering); | |
rendering.createControl(tab.getParent()); | |
getRenderings(tab).put(memorySpaceId, rendering); | |
getMemoryBlocks(tab).add(block); | |
rendering.addPropertyChangeListener(new RenderingPropertyChangeListener(tab, rendering)); | |
} | |
tab.setControl(rendering.getControl()); | |
tab.getParent().setSelection(0); | |
tab.setData(KEY_RENDERING, rendering); | |
tab.setData(KEY_MEMORY_SPACE, memorySpaceId); | |
tab.setData(KEY_CONTAINER, container); | |
tab.setData(KEY_RENDERING_TYPE, type); | |
tab.setData(KEY_EXPRESSION, expression); | |
tab.setData(KEY_EXPRESSION_ADDRESS, ((IMemoryBlockExtension)rendering.getMemoryBlock()).getBigBaseAddress()); | |
getSite().getSelectionProvider().setSelection(new StructuredSelection(tab.getData(KEY_RENDERING))); | |
return rendering; | |
} | |
private void releaseTabFolder(final IMemoryBlockRetrieval retrieval) | |
{ | |
final CTabFolder folder = fContextFolders.get(retrieval); | |
if(folder != null) | |
{ | |
Runnable run = new Runnable() { | |
public void run() { | |
for(CTabItem tab : folder.getItems()) { | |
disposeTab(tab); | |
} | |
fContextFolders.remove(retrieval); | |
folder.dispose(); | |
if (fStackLayout.topControl.equals(folder)) { | |
handleUnsupportedSelection(); | |
} | |
} | |
}; | |
runOnUIThread(run); | |
} | |
} | |
class SelectionProviderAdapter implements ISelectionProvider { | |
List<ISelectionChangedListener> listeners = new ArrayList<ISelectionChangedListener>(); | |
ISelection theSelection = StructuredSelection.EMPTY; | |
public void addSelectionChangedListener(ISelectionChangedListener listener) { | |
listeners.add(listener); | |
} | |
public ISelection getSelection() { | |
return theSelection; | |
} | |
public void removeSelectionChangedListener( | |
ISelectionChangedListener listener) { | |
listeners.remove(listener); | |
} | |
public void setSelection(ISelection selection) { | |
theSelection = selection; | |
final SelectionChangedEvent e = new SelectionChangedEvent(this, selection); | |
Object[] listenersArray = listeners.toArray(); | |
for (int i = 0; i < listenersArray.length; i++) { | |
final ISelectionChangedListener l = (ISelectionChangedListener) listenersArray[i]; | |
SafeRunner.run(new SafeRunnable() { | |
public void run() { | |
l.selectionChanged(e); | |
} | |
}); | |
} | |
} | |
} | |
/** | |
* create a memory block | |
* @param retrieval memory block retrieval. | |
* @param expression expression to be evaluated to an addressL | |
* @param context context for evaluating the expression. This is typically | |
* a debug element. | |
* @param memorySpaceID a memory space identifier, or null if n/a | |
* @return a memory block based on the given expression and context | |
* @throws DebugException if unable to retrieve the specified memory | |
*/ | |
private IMemoryBlockExtension createMemoryBlock(IMemoryBlockRetrieval retrieval, String expression, Object context, String memorySpaceID) throws DebugException { | |
IMemoryBlockExtension block = null; | |
IMemoryBlockRetrievalExtension retrievalExtension = null; | |
if (retrieval instanceof IMemoryBlockRetrievalExtension) { | |
retrievalExtension = (IMemoryBlockRetrievalExtension) retrieval; | |
} else if(retrieval instanceof IAdaptable) { | |
retrievalExtension = (IMemoryBlockRetrievalExtension)((IAdaptable) retrieval).getAdapter(IMemoryBlockRetrievalExtension.class); | |
} | |
if (retrievalExtension != null) { | |
if (retrievalExtension instanceof IMemorySpaceAwareMemoryBlockRetrieval) { | |
block = ((IMemorySpaceAwareMemoryBlockRetrieval)retrievalExtension).getMemoryBlock(expression, context, memorySpaceID); | |
} | |
else { | |
block = retrievalExtension.getExtendedMemoryBlock(expression, context); | |
} | |
} | |
if ( block == null ) { | |
throw new DebugException(new Status(Status.ERROR, MemoryBrowserPlugin.PLUGIN_ID, "Extended Memory Block could not be obtained")); //$NON-NLS-1$ | |
} | |
return block; | |
} | |
/** | |
* Get a memory address for an expression in a given context. | |
* @param retrieval | |
* @param expression | |
* @param context | |
* @return BigInteger address of the expression | |
* @throws DebugException | |
*/ | |
private BigInteger getExpressionAddress(IMemoryBlockRetrieval retrieval, String expression, Object context, String memorySpaceId) throws DebugException { | |
// Until 257842 issue is solved this is done via IMemoryBlockRetrievalExtension API. | |
IMemoryBlockExtension newBlock = createMemoryBlock(retrieval, expression, context, memorySpaceId); | |
BigInteger address = newBlock.getBigBaseAddress(); | |
newBlock.dispose(); | |
return address; | |
} | |
/** | |
* Execute runnable on UI thread if the current thread is not an UI thread. | |
* Otherwise execute it directly. | |
* | |
* @param runnable | |
* the runnable to execute | |
*/ | |
private void runOnUIThread(final Runnable runnable) { | |
if (Display.getCurrent() != null) { | |
runnable.run(); | |
} | |
else { | |
UIJob job = new UIJob("Memory Browser UI Job"){ //$NON-NLS-1$ | |
@Override | |
public IStatus runInUIThread(IProgressMonitor monitor) { | |
runnable.run(); | |
return Status.OK_STATUS; | |
}}; | |
job.setSystem(true); | |
job.schedule(); | |
} | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.cdt.debug.ui.memory.memorybrowser.api.IMemoryBrowser#go(java.lang.String, java.lang.String, boolean) | |
*/ | |
public void go(String expression, String memorySpaceId, boolean inNewTab) | |
throws CoreException { | |
if (expression == null) { | |
throw new IllegalArgumentException("expression cannot be null"); | |
} | |
expression = expression.trim(); | |
if (expression.length() == 0) { | |
throw new IllegalArgumentException("expression cannot be empty"); | |
} | |
if (!fGotoMemorySpaceControl.isDisposed() && fGotoMemorySpaceControl.isVisible()) { | |
if (memorySpaceId == null) { | |
// if caller passed null, use whatever memory space is selected in the control | |
memorySpaceId = fGotoMemorySpaceControl.getText(); | |
if (memorySpaceId.equals(NA_MEMORY_SPACE_ID)) { | |
memorySpaceId = null; | |
} | |
} | |
else { | |
// if caller passed empty string, it means n/a (same as "----" in the selector) | |
memorySpaceId = memorySpaceId.trim(); | |
if (memorySpaceId.length() == 0) { | |
memorySpaceId = null; | |
} | |
else { | |
// Check that the ID requested by the user is a valid one | |
if (fGotoMemorySpaceControl.indexOf(memorySpaceId) == -1) { | |
throw new IllegalArgumentException("unrecognized memory space ID"); | |
} | |
} | |
fGotoMemorySpaceControl.setText(memorySpaceId == null ? NA_MEMORY_SPACE_ID : memorySpaceId); | |
} | |
} | |
fGotoAddressBar.setExpressionText(expression); | |
performGo(inNewTab, expression, memorySpaceId); | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.cdt.debug.ui.memory.memorybrowser.api.IMemoryBrowser#getSelectedMemorySpace() | |
*/ | |
public String getSelectedMemorySpace() { | |
if (!fGotoMemorySpaceControl.isDisposed() && fGotoMemorySpaceControl.isVisible()) { | |
String id = fGotoMemorySpaceControl.getText(); | |
return id.equals(NA_MEMORY_SPACE_ID) ? null : id; | |
} | |
return null; | |
} | |
} |