/******************************************************************************* * Copyright (c) 2006, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Teodor Madan (Freescale) - Bug 292360 - [Memory View] platform renderings do not implement correctly IMemoryRendering#getControl * Teodor Madan (Freescale) - Bug 292426 - [Memory View] platform renderings cannot be repositioned from non-UI thread through calls to IRepositionableMemoryRendering#goToAddress * Teodor Madan (Freescale) & Jeremiah Swan (IBM) - Bug 300036 - [Memory View] NPE in AbstractAsyncTableRendering#getSelectedAddress on rendering creation *******************************************************************************/ package org.eclipse.debug.internal.ui.memory.provisional; import java.math.BigInteger; import java.util.ArrayList; import java.util.Iterator; import org.eclipse.core.commands.AbstractHandler; import org.eclipse.core.commands.Command; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.model.IMemoryBlock; import org.eclipse.debug.core.model.IMemoryBlockExtension; import org.eclipse.debug.core.model.MemoryByte; import org.eclipse.debug.internal.core.IInternalDebugCoreConstants; import org.eclipse.debug.internal.ui.DebugUIMessages; import org.eclipse.debug.internal.ui.DebugUIPlugin; import org.eclipse.debug.internal.ui.IInternalDebugUIConstants; import org.eclipse.debug.internal.ui.memory.IPersistableDebugElement; import org.eclipse.debug.internal.ui.preferences.IDebugPreferenceConstants; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelChangedListener; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy; import org.eclipse.debug.internal.ui.viewers.model.provisional.IStatusMonitor; import org.eclipse.debug.internal.ui.views.memory.MemoryViewUtil; import org.eclipse.debug.internal.ui.views.memory.renderings.AbstractBaseTableRendering; import org.eclipse.debug.internal.ui.views.memory.renderings.AbstractVirtualContentTableModel; import org.eclipse.debug.internal.ui.views.memory.renderings.AsyncCopyTableRenderingAction; import org.eclipse.debug.internal.ui.views.memory.renderings.AsyncPrintTableRenderingAction; import org.eclipse.debug.internal.ui.views.memory.renderings.AsyncTableRenderingCellModifier; import org.eclipse.debug.internal.ui.views.memory.renderings.AsyncTableRenderingViewer; import org.eclipse.debug.internal.ui.views.memory.renderings.CopyTableRenderingToClipboardAction; import org.eclipse.debug.internal.ui.views.memory.renderings.FormatTableRenderingAction; import org.eclipse.debug.internal.ui.views.memory.renderings.FormatTableRenderingDialog; import org.eclipse.debug.internal.ui.views.memory.renderings.GoToAddressAction; import org.eclipse.debug.internal.ui.views.memory.renderings.GoToAddressComposite; import org.eclipse.debug.internal.ui.views.memory.renderings.IPresentationErrorListener; import org.eclipse.debug.internal.ui.views.memory.renderings.IVirtualContentListener; import org.eclipse.debug.internal.ui.views.memory.renderings.MemorySegment; import org.eclipse.debug.internal.ui.views.memory.renderings.PendingPropertyChanges; import org.eclipse.debug.internal.ui.views.memory.renderings.PrintTableRenderingAction; import org.eclipse.debug.internal.ui.views.memory.renderings.ReformatAction; import org.eclipse.debug.internal.ui.views.memory.renderings.ResetToBaseAddressAction; import org.eclipse.debug.internal.ui.views.memory.renderings.TableRenderingContentDescriptor; import org.eclipse.debug.internal.ui.views.memory.renderings.TableRenderingLine; import org.eclipse.debug.ui.DebugUITools; import org.eclipse.debug.ui.IDebugUIConstants; import org.eclipse.debug.ui.memory.AbstractTableRendering; import org.eclipse.debug.ui.memory.IMemoryBlockTablePresentation; 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.IResettableMemoryRendering; import org.eclipse.debug.ui.memory.MemoryRenderingElement; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; 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.ImageDescriptor; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.TextViewer; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.CellEditor; import org.eclipse.jface.viewers.IBasicPropertyConstants; import org.eclipse.jface.viewers.ICellModifier; import org.eclipse.jface.viewers.IColorProvider; import org.eclipse.jface.viewers.IFontProvider; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredViewer; import org.eclipse.jface.viewers.TextCellEditor; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.ControlListener; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseTrackAdapter; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.TableItem; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchActionConstants; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.commands.ICommandService; import org.eclipse.ui.contexts.IContextActivation; import org.eclipse.ui.contexts.IContextService; import org.eclipse.ui.dialogs.PropertyDialogAction; import org.eclipse.ui.model.IWorkbenchAdapter; import org.eclipse.ui.part.PageBook; import org.eclipse.ui.progress.UIJob; /** * Abstract implementation of a table rendering. * <p> * Clients should subclass from this class if they wish to provide a table * rendering. * </p> * <p> * * The label of the rendering is constructed by retrieving the expression from * <code>IMemoryBlockExtension</code>. For IMemoryBlock, the label is * constructed using the memory block's start address. * * This rendering manages the change states of its memory bytes if the memory * block does not opt to manage the change states. For IMemoryBlockExtension, if * the memory block returns false when #supportsChangeManagement() is called, * this rendering will calculate the change state for each byte when its content * is updated. Clients may manages the change states of its memory block by * returning true when #supportsChangeManagement() is called. This will cause * this rendering to stop calculating the change states of the memory block. * Instead it would rely on the attributes returned in the MemoryByte array to * determine if a byte has changed. For IMemoryBlock, this rendering will manage * the change states its content. * * When firing change event, be aware of the following: - whenever a change * event is fired, the content provider for Memory View view checks to see if * memory has actually changed. - If memory has actually changed, a refresh will * commence. Changes to the memory block will be computed and will be shown with * the delta icons. - If memory has not changed, content will not be refreshed. * However, previous delta information will be erased. The screen will be * refreshed to show that no memory has been changed. (All delta icons will be * removed.) * * Please note that these APIs will be called multiple times by the Memory View. * To improve performance, debug adapters need to cache the content of its * memory block and only retrieve updated data when necessary. * </p> * * @since 3.2 */ public abstract class AbstractAsyncTableRendering extends AbstractBaseTableRendering implements IPropertyChangeListener, IResettableMemoryRendering { /** * Property identifier for the selected address in a table rendering This * property is used for synchronization between renderings. */ public static final String PROPERTY_SELECTED_ADDRESS = AbstractTableRendering.PROPERTY_SELECTED_ADDRESS; /** * Property identifier for the column size in a table rendering This * property is used for synchronization between renderings. */ public static final String PROPERTY_COL_SIZE = AbstractTableRendering.PROPERTY_COL_SIZE; /** * Property identifier for the top row address in a table rendering. This * property is used for synchronization between renderings. */ public static final String PROPERTY_TOP_ADDRESS = AbstractTableRendering.PROPERTY_TOP_ADDRESS; private static final String ID_ASYNC_TABLE_RENDERING_CONTEXT = "org.eclipse.debug.ui.memory.abstractasynctablerendering"; //$NON-NLS-1$ private static final String ID_GO_TO_ADDRESS_COMMAND = "org.eclipse.debug.ui.command.gotoaddress"; //$NON-NLS-1$ private static final String ID_NEXT_PAGE_COMMAND = "org.eclipse.debug.ui.command.nextpage"; //$NON-NLS-1$ private static final String ID_PREV_PAGE_COMMAND = "org.eclipse.debug.ui.command.prevpage"; //$NON-NLS-1$ /** * Property identifier for the row size in a table rendering This property * is used for synchronization between renderings. */ public static final String PROPERTY_ROW_SIZE = AbstractTableRendering.PROPERTY_ROW_SIZE; private static final int DEFAULT_BUFFER_THRESHOLD = 1; private boolean fActivated = false; // TODO: review use of MemorySegment, need to be careful to ensure flexible // hierarchy private class ToggleAddressColumnAction extends Action { public ToggleAddressColumnAction() { super(); PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IDebugUIConstants.PLUGIN_ID + ".ShowAddressColumnAction_context"); //$NON-NLS-1$ updateActionLabel(); } /* * (non-Javadoc) * @see org.eclipse.jface.action.IAction#run() */ @Override public void run() { fIsShowAddressColumn = !fIsShowAddressColumn; if (!fIsShowAddressColumn) { fTableViewer.getTable().getColumn(0).setWidth(0); } else { fTableViewer.getTable().getColumn(0).pack(); } updateActionLabel(); } /** * */ private void updateActionLabel() { if (fIsShowAddressColumn) { setText(DebugUIMessages.ShowAddressColumnAction_0); } else { setText(DebugUIMessages.ShowAddressColumnAction_1); } } } private class NextPageAction extends Action { private NextPageAction() { super(); setText(DebugUIMessages.AbstractTableRendering_4); PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IDebugUIConstants.PLUGIN_ID + ".NextPageAction_context"); //$NON-NLS-1$ } @Override public void run() { BigInteger address = fContentDescriptor.getLoadAddress(); address = address.add(BigInteger.valueOf(getPageSizeInUnits())); handlePageStartAddressChanged(address); } } private class PrevPageAction extends Action { private PrevPageAction() { super(); setText(DebugUIMessages.AbstractTableRendering_6); PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IDebugUIConstants.PLUGIN_ID + ".PrevPageAction_context"); //$NON-NLS-1$ } @Override public void run() { BigInteger address = fContentDescriptor.getLoadAddress(); address = address.subtract(BigInteger.valueOf(getPageSizeInUnits())); handlePageStartAddressChanged(address); } } private class RenderingGoToAddressAction extends GoToAddressAction { public RenderingGoToAddressAction(IMemoryRenderingContainer container, AbstractBaseTableRendering rendering) { super(container, rendering); } @Override public void run() { showGoToAddressComposite(); } } private class SwitchPageJob extends UIJob { private Object fLock = new Object(); private boolean fShowMessagePage = false; private String fMessage = IInternalDebugCoreConstants.EMPTY_STRING; private SwitchPageJob() { super("SwitchPageJob");//$NON-NLS-1$ setSystem(true); } private void setShowMessagePage(boolean showMsg) { synchronized (fLock) { fShowMessagePage = showMsg; } } private void setMessage(String message) { synchronized (fLock) { fMessage = message; } } @Override public IStatus runInUIThread(IProgressMonitor monitor) { if (fPageBook.isDisposed()) { return Status.OK_STATUS; } String msgToShow = null; boolean showMsgPage = false; synchronized (fLock) { msgToShow = fMessage; showMsgPage = fShowMessagePage; } if (showMsgPage) { StyledText styleText = null; fShowMessage = true; styleText = fTextViewer.getTextWidget(); if (styleText != null) { styleText.setText(msgToShow); } fPageBook.showPage(fTextViewer.getControl()); } else { fShowMessage = false; fPageBook.showPage(fTableViewer.getControl().getParent()); } return Status.OK_STATUS; } } private class SerialByObjectRule implements ISchedulingRule { private Object fObject = null; public SerialByObjectRule(Object lock) { fObject = lock; } /* * (non-Javadoc) * @see * org.eclipse.core.runtime.jobs.ISchedulingRule#contains(org.eclipse * .core.runtime.jobs.ISchedulingRule) */ @Override public boolean contains(ISchedulingRule rule) { return rule == this; } /* * (non-Javadoc) * @see * org.eclipse.core.runtime.jobs.ISchedulingRule#isConflicting(org.eclipse * .core.runtime.jobs.ISchedulingRule) */ @Override public boolean isConflicting(ISchedulingRule rule) { if (rule instanceof SerialByObjectRule) { SerialByObjectRule rRule = (SerialByObjectRule) rule; return fObject == rRule.fObject; } return false; } } private PageBook fPageBook; private AsyncTableRenderingViewer fTableViewer; private TextViewer fTextViewer; private Shell fToolTipShell; private MemoryViewPresentationContext fPresentationContext; private int fAddressableSize; private TableRenderingContentDescriptor fContentDescriptor; private int fBytePerLine; private int fColumnSize; private boolean fShowMessage = false; private String fLabel; private IWorkbenchAdapter fWorkbenchAdapter; private int fPageSize; private int fPreBufferSize = -1; private int fPostBufferSize = -1; private SashForm fSashForm; private GoToAddressComposite fGoToAddressComposite; // actions private GoToAddressAction fGoToAddressAction; private PrintTableRenderingAction fPrintViewTabAction; private CopyTableRenderingToClipboardAction fCopyToClipboardAction; private FormatTableRenderingAction fFormatRenderingAction; private ReformatAction fReformatAction; private ToggleAddressColumnAction fToggleAddressColumnAction; private ResetToBaseAddressAction fResetMemoryBlockAction; private PropertyDialogAction fPropertiesDialogAction; private NextPageAction fNextAction; private PrevPageAction fPrevAction; private ArrayList<IContextActivation> fContext = new ArrayList<IContextActivation>(); private AbstractHandler fGoToAddressHandler; private AbstractHandler fNextPageHandler; private AbstractHandler fPrevPageHandler; private boolean fIsCreated = false; private boolean fIsDisposed = false; private boolean fIsShowAddressColumn = true; private SwitchPageJob fSwitchPageJob = new SwitchPageJob(); private boolean fError = false; private PendingPropertyChanges fPendingSyncProperties; // list of menu listeners for popupMenuMgr. private ArrayList<IMenuListener> fMenuListeners; private MenuManager fMenuMgr; private ISchedulingRule serialByRenderingRule = new SerialByObjectRule(this); /** * Identifier for an empty group preceding all context menu actions (value * <code>"popUpBegin"</code>). */ public static final String EMPTY_MEMORY_GROUP = "popUpBegin"; //$NON-NLS-1$ /** * Identifier for an empty group following navigation actions in the * rendering (value <code>navigationGroup</code>). */ public static final String EMPTY_NAVIGATION_GROUP = "navigationGroup"; //$NON-NLS-1$ /** * Identifier for an empty group following actions that are only applicable * in non-auto loading mode (value <code>nonAutoLoadGroup</code>). */ public static final String EMPTY_NON_AUTO_LOAD_GROUP = "nonAutoLoadGroup"; //$NON-NLS-1$ /** * Identifier for an empty group following properties actions (value * <code>propertyGroup</code>). */ public static final String EMPTY_PROPERTY_GROUP = "propertyGroup"; //$NON-NLS-1$ private ISelectionChangedListener fViewerSelectionChangedListener = new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { updateSyncTopAddress(getTopVisibleAddress()); updateSyncSelectedAddress(getSelectedAddress()); } }; private SelectionAdapter fScrollBarSelectionListener = new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { updateSyncTopAddress(getTopVisibleAddress()); } }; private IModelChangedListener fModelChangedListener = new IModelChangedListener() { @Override public void modelChanged(IModelDelta delta, IModelProxy proxy) { if (delta.getElement() == getMemoryBlock()) { showTable(); updateRenderingLabel(isVisible()); } } }; private IVirtualContentListener fViewerListener = new IVirtualContentListener() { private int startThreshold; private int endThreshold; @Override public void handledAtBufferStart() { if (getMemoryBlock() instanceof IMemoryBlockExtension) { if (isDynamicLoad() && startThreshold != 0) { BigInteger address = getTopVisibleAddress(); if (address != null && !isAtTopLimit()) { reloadTable(address); } } } } @Override public void handleAtBufferEnd() { if (getMemoryBlock() instanceof IMemoryBlockExtension) { if (isDynamicLoad() && endThreshold != 0) { BigInteger address = getTopVisibleAddress(); if (address != null && !isAtBottomLimit()) { reloadTable(address); } } } } @Override public int getThreshold(int bufferEndOrStart) { int threshold = DEFAULT_BUFFER_THRESHOLD; if (bufferEndOrStart == IVirtualContentListener.BUFFER_START) { if (threshold > getPreBufferSize()) { threshold = getPreBufferSize(); } } else { if (threshold > getPostBufferSize()) { threshold = getPostBufferSize(); } } if (bufferEndOrStart == IVirtualContentListener.BUFFER_START) { startThreshold = threshold; } else { endThreshold = threshold; } return threshold; } }; private IPresentationErrorListener fPresentationErrorListener = new IPresentationErrorListener() { @Override public void handlePresentationFailure(IStatusMonitor monitor, IStatus status) { showMessage(status.getMessage()); } }; /** * Constructs a new table rendering of the specified type. * * @param renderingId memory rendering type identifier */ public AbstractAsyncTableRendering(String renderingId) { super(renderingId); } /* * (non-Javadoc) * @see * org.eclipse.debug.ui.memory.IResettableMemoryRendering#resetRendering() */ @Override public void resetRendering() throws DebugException { if (!fIsCreated) { return; } BigInteger baseAddress = fContentDescriptor.getContentBaseAddress(); fTableViewer.setSelection(baseAddress); reloadTable(baseAddress); fTableViewer.setTopIndex(baseAddress); if (!isDynamicLoad()) { updateSyncPageStartAddress(baseAddress); } updateSyncSelectedAddress(baseAddress); updateSyncTopAddress(baseAddress); } @Override public Control createControl(Composite parent) { fPageBook = new PageBook(parent, SWT.NONE); createMessagePage(fPageBook); createTableViewer(fPageBook); addListeners(); return fPageBook; } /** * Create the error page of this rendering * * @param parent the parent to add the page to */ private void createMessagePage(Composite parent) { if (fTextViewer == null) { fTextViewer = new TextViewer(parent, SWT.WRAP); fTextViewer.setDocument(new Document()); StyledText styleText = fTextViewer.getTextWidget(); styleText.setEditable(false); styleText.setEnabled(false); } } /** * @param parent the parent composite */ private void createTableViewer(final Composite parent) { StringBuffer buffer = new StringBuffer(); IMemoryRenderingType type = DebugUITools.getMemoryRenderingManager().getRenderingType(getRenderingId()); buffer.append(type.getLabel()); buffer.append(": "); //$NON-NLS-1$ buffer.append(DebugUIMessages.AbstractAsyncTableRendering_2); Job job = new Job(buffer.toString()) { @Override protected IStatus run(IProgressMonitor monitor) { // gather information from memory block initAddressableSize(); final BigInteger topVisibleAddress = getInitialTopVisibleAddress(); BigInteger mbBaseAddress = null; try { mbBaseAddress = getMemoryBlockBaseAddress(); } catch (DebugException e) { fError = true; showMessage(e.getMessage()); } // if it takes too long to get the base address, and user has // canceled // remove this rendering. if (monitor.isCanceled()) { getMemoryRenderingContainer().removeMemoryRendering(AbstractAsyncTableRendering.this); return Status.CANCEL_STATUS; } final BigInteger finalMbBaseAddress = mbBaseAddress; final BigInteger initialSelectedAddress = getInitialSelectedAddress(); if (monitor.isCanceled()) { getMemoryRenderingContainer().removeMemoryRendering(AbstractAsyncTableRendering.this); return Status.CANCEL_STATUS; } createContentDescriptor(topVisibleAddress); // if it takes too long to get other information, and user has // canceled // remove this rendering. if (monitor.isCanceled()) { getMemoryRenderingContainer().removeMemoryRendering(AbstractAsyncTableRendering.this); return Status.CANCEL_STATUS; } // batch update on UI thread UIJob uiJob = new UIJob("Create Table Viewer UI Job") { //$NON-NLS-1$ @Override public IStatus runInUIThread(IProgressMonitor progressMonitor) { if (fPageBook.isDisposed()) { return Status.OK_STATUS; } fSashForm = new SashForm(parent, SWT.VERTICAL); fTableViewer = new AsyncTableRenderingViewer(AbstractAsyncTableRendering.this, fSashForm, SWT.VIRTUAL | SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.HIDE_SELECTION | SWT.BORDER); GridData data = new GridData(GridData.FILL_BOTH); fTableViewer.getControl().setLayoutData(data); createGoToAddressComposite(fSashForm); hideGotoAddressComposite(); IMemoryRenderingSite site = getMemoryRenderingContainer().getMemoryRenderingSite(); IMemoryRenderingContainer container = getMemoryRenderingContainer(); fPresentationContext = new MemoryViewPresentationContext(site, container, AbstractAsyncTableRendering.this); fTableViewer.setContext(fPresentationContext); // must call this after the context is created as the // information is stored in the context getDynamicLoadFromPreference(); getPageSizeFromPreference(); int numberOfLines = getNumLinesToLoad(); fContentDescriptor.setNumLines(numberOfLines); if (numberOfLines == 0) { // if the table viewer is not initialized yet, add // listener // and load table when we can get the height of the // table fTableViewer.getTable().addPaintListener(new PaintListener() { @Override public void paintControl(PaintEvent e) { fTableViewer.getTable().removePaintListener(this); fContentDescriptor.setNumLines(getNumLinesToLoad()); refresh(); } }); } BigInteger baseAddress = finalMbBaseAddress; if (baseAddress == null) { baseAddress = BigInteger.ZERO; } if (!(getMemoryBlock() instanceof IMemoryBlockExtension) || !isDynamicLoad()) { // If not extended memory block, do not create any // buffer // no scrolling fContentDescriptor.setPreBuffer(0); fContentDescriptor.setPostBuffer(0); } setupInitialFormat(); fTableViewer.setCellModifier(newInternalCellModifier()); fTableViewer.getTable().setHeaderVisible(true); fTableViewer.getTable().setLinesVisible(true); fTableViewer.addPresentationErrorListener(fPresentationErrorListener); fTableViewer.setInput(getMemoryBlock()); fTableViewer.resizeColumnsToPreferredSize(); fTableViewer.setTopIndex(topVisibleAddress); fTableViewer.setSelection(initialSelectedAddress); // SET UP FONT // set to a non-proportional font fTableViewer.getTable().setFont(JFaceResources.getFont(IInternalDebugUIConstants.FONT_NAME)); if (!fError) { showTable(); } fTableViewer.addVirtualContentListener(fViewerListener); // create context menu // create pop up menu for the rendering createActions(); IMenuListener menuListener = new IMenuListener() { @Override public void menuAboutToShow(IMenuManager mgr) { fillContextMenu(mgr); } }; createPopupMenu(fTableViewer.getControl(), menuListener); createPopupMenu(fTableViewer.getCursor(), menuListener); fTableViewer.addSelectionChangedListener(fViewerSelectionChangedListener); fTableViewer.getTable().getVerticalBar().addSelectionListener(fScrollBarSelectionListener); // add resize listener to figure out number of visible // lines // so that the viewer get refreshed with the correct // number of lines on the // next refresh request fTableViewer.getTable().addListener(SWT.Resize, new Listener() { @Override public void handleEvent(Event event) { if (!fTableViewer.getTable().isDisposed()) { fContentDescriptor.setNumLines(getNumLinesToLoad()); } } }); createToolTip(); if (isActivated()) { activatePageActions(); } // now the rendering is successfully created fIsCreated = true; return Status.OK_STATUS; } }; uiJob.setSystem(true); uiJob.schedule(); return Status.OK_STATUS; } }; job.schedule(); } /** * Create popup menu for this rendering * * @param control - control to create the popup menu for * @param menuListener - listener to notify when popup menu is about to show */ private void createPopupMenu(Control control, IMenuListener menuListener) { IMemoryRenderingContainer container = getMemoryRenderingContainer(); if (fMenuMgr == null) { fMenuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$ fMenuMgr.setRemoveAllWhenShown(true); IMemoryRenderingSite site = container.getMemoryRenderingSite(); String menuId = container.getId(); ISelectionProvider selProvider = site.getSite().getSelectionProvider(); addMenuListener(menuListener); site.getSite().registerContextMenu(menuId, fMenuMgr, selProvider); } addMenuListener(menuListener); Menu popupMenu = fMenuMgr.createContextMenu(control); control.setMenu(popupMenu); } private void addMenuListener(IMenuListener menuListener) { if (fMenuListeners == null) { fMenuListeners = new ArrayList<IMenuListener>(); } if (!fMenuListeners.contains(menuListener)) { fMenuMgr.addMenuListener(menuListener); fMenuListeners.add(menuListener); } } private BigInteger getInitialSelectedAddress() { // figure out selected address BigInteger selectedAddress = (BigInteger) getSynchronizedProperty(AbstractAsyncTableRendering.PROPERTY_SELECTED_ADDRESS); if (selectedAddress == null) { if (getMemoryBlock() instanceof IMemoryBlockExtension) { try { selectedAddress = ((IMemoryBlockExtension) getMemoryBlock()).getBigBaseAddress(); } catch (DebugException e) { selectedAddress = BigInteger.ZERO; } if (selectedAddress == null) { selectedAddress = BigInteger.ZERO; } } else { long address = getMemoryBlock().getStartAddress(); selectedAddress = BigInteger.valueOf(address); } } return selectedAddress; } private void addListeners() { DebugUIPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(this); addRenderingToSyncService(); JFaceResources.getFontRegistry().addListener(this); } private void removeListeners() { DebugUIPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(this); removeRenderingFromSyncService(); JFaceResources.getFontRegistry().removeListener(this); if (fMenuListeners != null) { Iterator<IMenuListener> iter = fMenuListeners.iterator(); while (iter.hasNext()) { fMenuMgr.removeMenuListener(iter.next()); } fMenuListeners.clear(); } } private void addRenderingToSyncService() { IMemoryRenderingSynchronizationService syncService = getMemoryRenderingContainer().getMemoryRenderingSite().getSynchronizationService(); if (syncService == null) { return; } syncService.addPropertyChangeListener(this, null); } private void removeRenderingFromSyncService() { IMemoryRenderingSynchronizationService syncService = getMemoryRenderingContainer().getMemoryRenderingSite().getSynchronizationService(); if (syncService == null) { return; } syncService.removePropertyChangeListener(this); } private void initAddressableSize() { // set up addressable size and figure out number of bytes required per // line fAddressableSize = -1; try { if (getMemoryBlock() instanceof IMemoryBlockExtension) { fAddressableSize = ((IMemoryBlockExtension) getMemoryBlock()).getAddressableSize(); } else { fAddressableSize = 1; } } catch (DebugException e1) { DebugUIPlugin.log(e1); // log error and default to 1 fAddressableSize = 1; return; } if (fAddressableSize < 1) { DebugUIPlugin.logErrorMessage("Invalid addressable size"); //$NON-NLS-1$ fAddressableSize = 1; } } private BigInteger getInitialTopVisibleAddress() { BigInteger topVisibleAddress = (BigInteger) getSynchronizedProperty(AbstractAsyncTableRendering.PROPERTY_TOP_ADDRESS); if (topVisibleAddress == null) { if (getMemoryBlock() instanceof IMemoryBlockExtension) { try { topVisibleAddress = ((IMemoryBlockExtension) getMemoryBlock()).getBigBaseAddress(); } catch (DebugException e1) { topVisibleAddress = new BigInteger("0"); //$NON-NLS-1$ } } else { topVisibleAddress = BigInteger.valueOf(getMemoryBlock().getStartAddress()); } } return topVisibleAddress; } private void setupInitialFormat() { boolean validated = validateInitialFormat(); if (!validated) { // pop up dialog to ask user for default values StringBuffer msgBuffer = new StringBuffer(DebugUIMessages.AbstractTableRendering_20); msgBuffer.append(" "); //$NON-NLS-1$ msgBuffer.append(this.getLabel()); msgBuffer.append("\n\n"); //$NON-NLS-1$ msgBuffer.append(DebugUIMessages.AbstractTableRendering_16); msgBuffer.append("\n"); //$NON-NLS-1$ msgBuffer.append(DebugUIMessages.AbstractTableRendering_18); msgBuffer.append("\n\n"); //$NON-NLS-1$ int bytePerLine = fBytePerLine; int columnSize = fColumnSize; // initialize this value to populate the dialog properly fBytePerLine = getDefaultRowSize() / getAddressableSize(); fColumnSize = getDefaultColumnSize() / getAddressableSize(); FormatTableRenderingDialog dialog = new FormatTableRenderingDialog(this, DebugUIPlugin.getShell()); dialog.openError(msgBuffer.toString()); // restore to original value before formatting fBytePerLine = bytePerLine; fColumnSize = columnSize; bytePerLine = dialog.getRowSize() * getAddressableSize(); columnSize = dialog.getColumnSize() * getAddressableSize(); format(bytePerLine, columnSize); } else { // Row size is stored as number of addressable units in preference // store int bytePerLine = getDefaultRowSize(); // column size is now stored as number of addressable units int columnSize = getDefaultColumnSize(); // format memory block with specified "bytesPerLine" and // "columnSize" boolean ok = format(bytePerLine, columnSize); if (!ok) { // this is to ensure that the rest of the rendering can be // created // and we can recover from a format error format(bytePerLine, bytePerLine); } } } private boolean validateInitialFormat() { int rowSize = getDefaultRowSize(); int columnSize = getDefaultColumnSize(); if (rowSize < columnSize || rowSize % columnSize != 0 || rowSize == 0 || columnSize == 0) { return false; } return true; } /* * (non-Javadoc) * @see org.eclipse.debug.ui.memory.IMemoryRendering#getControl() */ @Override public Control getControl() { return fPageBook; } /* * (non-Javadoc) * @see * org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse * .jface.util.PropertyChangeEvent) */ @Override public void propertyChange(PropertyChangeEvent event) { if (!fIsCreated) { return; } // if memory view table font has changed if (event.getProperty().equals(IInternalDebugUIConstants.FONT_NAME)) { if (!fIsDisposed) { Font memoryViewFont = JFaceResources.getFont(IInternalDebugUIConstants.FONT_NAME); setFont(memoryViewFont); } return; } Object evtSrc = event.getSource(); // always update page size, only refresh if the table is visible if (event.getProperty().equals(IDebugPreferenceConstants.PREF_TABLE_RENDERING_PAGE_SIZE)) { getPageSizeFromPreference(); } if (event.getProperty().equals(IDebugPreferenceConstants.PREF_TABLE_RENDERING_PRE_BUFFER_SIZE)) { getPreBufferSizeFromPreference(); fContentDescriptor.setPreBuffer(getPreBufferSize()); } if (event.getProperty().equals(IDebugPreferenceConstants.PREF_TABLE_RENDERING_POST_BUFFER_SIZE)) { getPostBufferSizeFromPreference(); fContentDescriptor.setPostBuffer(getPostBufferSize()); } // do not handle event if the rendering is displaying an error // or if it's not visible if (isDisplayingError() || !isVisible()) { handlePropertiesChangeWhenHidden(event); return; } if (event.getProperty().equals(IDebugUIConstants.PREF_PADDED_STR) || event.getProperty().equals(IDebugUIConstants.PREF_CHANGED_DEBUG_ELEMENT_COLOR) || event.getProperty().equals(IDebugUIConstants.PREF_MEMORY_HISTORY_KNOWN_COLOR) || event.getProperty().equals(IDebugUIConstants.PREF_MEMORY_HISTORY_UNKNOWN_COLOR)) { if (!fIsDisposed) { fTableViewer.refresh(false); } return; } if (event.getProperty().equals(IDebugPreferenceConstants.PREF_TABLE_RENDERING_PRE_BUFFER_SIZE) || event.getProperty().equals(IDebugPreferenceConstants.PREF_TABLE_RENDERING_POST_BUFFER_SIZE)) { if (!fIsDisposed) { fTableViewer.refresh(true); } return; } if (event.getProperty().equals(IDebugPreferenceConstants.PREF_DYNAMIC_LOAD_MEM)) { handleDyanicLoadChanged(); return; } if (event.getProperty().equals(IDebugPreferenceConstants.PREF_TABLE_RENDERING_PAGE_SIZE)) { if (!isDynamicLoad()) { int pageSize = DebugUIPlugin.getDefault().getPreferenceStore().getInt(IDebugPreferenceConstants.PREF_TABLE_RENDERING_PAGE_SIZE); handlePageSizeChanged(pageSize); } return; } if (evtSrc == this) { return; } if (evtSrc instanceof IMemoryRendering) { IMemoryRendering rendering = (IMemoryRendering) evtSrc; IMemoryBlock memoryBlock = rendering.getMemoryBlock(); // do not handle event from renderings displaying other memory // blocks if (memoryBlock != getMemoryBlock()) { return; } } String propertyName = event.getProperty(); Object value = event.getNewValue(); if (propertyName.equals(AbstractAsyncTableRendering.PROPERTY_SELECTED_ADDRESS) && value instanceof BigInteger) { selectedAddressChanged((BigInteger) value); } else if (propertyName.equals(AbstractAsyncTableRendering.PROPERTY_COL_SIZE) && value instanceof Integer) { columnSizeChanged(((Integer) value).intValue()); } else if (propertyName.equals(AbstractAsyncTableRendering.PROPERTY_ROW_SIZE) && value instanceof Integer) { rowSizeChanged(((Integer) value).intValue()); } else if (propertyName.equals(AbstractAsyncTableRendering.PROPERTY_TOP_ADDRESS) && value instanceof BigInteger) { topVisibleAddressChanged((BigInteger) value); } else if (propertyName.equals(IInternalDebugUIConstants.PROPERTY_PAGE_START_ADDRESS) && value instanceof BigInteger) { handlePageStartAddressChanged((BigInteger) value); } } /** * @param pageSize the new page size */ private void handlePageSizeChanged(int pageSize) { fPageSize = pageSize; // only refresh if in non-auto-load mode fContentDescriptor.setNumLines(pageSize); refresh(); } private void handlePropertiesChangeWhenHidden(PropertyChangeEvent event) { if (fPendingSyncProperties == null) { return; } String propertyName = event.getProperty(); Object value = event.getNewValue(); if (event.getSource() instanceof IMemoryRendering) { IMemoryRendering rendering = (IMemoryRendering) event.getSource(); if (rendering == this || rendering.getMemoryBlock() != getMemoryBlock()) { return; } } if (propertyName.equals(AbstractAsyncTableRendering.PROPERTY_COL_SIZE) && value instanceof Integer) { fPendingSyncProperties.setColumnSize(((Integer) value).intValue()); } else if (propertyName.equals(AbstractAsyncTableRendering.PROPERTY_ROW_SIZE) && value instanceof Integer) { fPendingSyncProperties.setRowSize(((Integer) value).intValue()); } else if (propertyName.equals(AbstractAsyncTableRendering.PROPERTY_SELECTED_ADDRESS) && value instanceof BigInteger) { fPendingSyncProperties.setSelectedAddress((BigInteger) value); } else if (propertyName.equals(AbstractAsyncTableRendering.PROPERTY_TOP_ADDRESS) && value instanceof BigInteger) { fPendingSyncProperties.setTopVisibleAddress((BigInteger) value); } else if (propertyName.equals(IInternalDebugUIConstants.PROPERTY_PAGE_START_ADDRESS) && value instanceof BigInteger) { fPendingSyncProperties.setPageStartAddress((BigInteger) value); } else if (event.getProperty().equals(IDebugPreferenceConstants.PREF_TABLE_RENDERING_PAGE_SIZE)) { int pageSize = DebugUIPlugin.getDefault().getPreferenceStore().getInt(IDebugPreferenceConstants.PREF_TABLE_RENDERING_PAGE_SIZE); fPendingSyncProperties.setPageSize(pageSize); } } private void topVisibleAddressChanged(final BigInteger address) { final Runnable runnable = new Runnable() { @Override public void run() { if (fTableViewer.getTable().isDisposed()) { return; } doTopVisibleAddressChanged(address); } }; runOnUIThread(runnable); } /** * @param address the changed address */ private void doTopVisibleAddressChanged(final BigInteger address) { if (fIsDisposed) { return; } if (!isDynamicLoad()) { fTableViewer.setTopIndex(address); fTableViewer.topIndexChanged(); return; } if (!isAtTopBuffer(address) && !isAtBottomBuffer(address)) { fTableViewer.setTopIndex(address); fTableViewer.topIndexChanged(); } else { reloadTable(address); } } private boolean isAtBottomBuffer(BigInteger address) { int idx = fTableViewer.indexOf(address); if (idx < 0) { return true; } int bottomIdx = idx + getNumberOfVisibleLines(); int elementsCnt = fTableViewer.getVirtualContentModel().getElements().length; int numLinesLeft = elementsCnt - bottomIdx; if (numLinesLeft < fViewerListener.getThreshold(IVirtualContentListener.BUFFER_END)) { return true; } return false; } private boolean isAtTopBuffer(BigInteger address) { int topIdx = fTableViewer.indexOf(address); if (topIdx < fViewerListener.getThreshold(IVirtualContentListener.BUFFER_START)) { return true; } return false; } private void runOnUIThread(final Runnable runnable) { if (Display.getCurrent() != null) { runnable.run(); } else { UIJob job = new UIJob("Async Table Rendering UI Job") { //$NON-NLS-1$ @Override public IStatus runInUIThread(IProgressMonitor monitor) { runnable.run(); return Status.OK_STATUS; } }; job.setSystem(true); job.schedule(); } } private void selectedAddressChanged(final BigInteger address) { Runnable runnable = new Runnable() { @Override public void run() { if (fTableViewer.getTable().isDisposed()) { return; } // call this to make the table viewer to reload when needed int i = fTableViewer.indexOf(address); if (i < 0) { // the model may not have been populated yet, // try to predict if the address will be in the buffer boolean contained = isAddressBufferred(address); if (!contained) { topVisibleAddressChanged(address); } } fTableViewer.setSelection(address); } }; runOnUIThread(runnable); } private boolean isAddressBufferred(BigInteger address) { // figure out the buffer top address BigInteger loadAddress = fContentDescriptor.getLoadAddress(); loadAddress = MemoryViewUtil.alignToBoundary(loadAddress, getAddressableUnitPerLine()); int unitPerLine = getAddressableUnitPerLine(); loadAddress = loadAddress.subtract(BigInteger.valueOf(getPreBufferSize() * unitPerLine)); // figure out the buffer end address int numLines = fContentDescriptor.getNumLines(); BigInteger bufferEnd = loadAddress.add(BigInteger.valueOf(fContentDescriptor.getPostBuffer() * unitPerLine)); bufferEnd = bufferEnd.add(BigInteger.valueOf(numLines * unitPerLine + unitPerLine)); // see if the address is contained based on current content descriptor if (address.compareTo(loadAddress) >= 0 && address.compareTo(bufferEnd) <= 0) { return true; } return false; } private void setFont(Font font) { // set font fTableViewer.getTable().setFont(font); fTableViewer.getCursor().setFont(font); } private int getDefaultColumnSize() { // default to global preference store IPreferenceStore prefStore = DebugUITools.getPreferenceStore(); int columnSize = prefStore.getInt(IDebugPreferenceConstants.PREF_COLUMN_SIZE); // actual column size is number of addressable units * size of the // addressable unit columnSize = columnSize * getAddressableSize(); // check synchronized col size Integer colSize = (Integer) getSynchronizedProperty(AbstractAsyncTableRendering.PROPERTY_COL_SIZE); if (colSize != null) { // column size is stored as actual number of bytes in synchronizer int syncColSize = colSize.intValue(); if (syncColSize > 0) { columnSize = syncColSize; } } else { IPersistableDebugElement elmt = getMemoryBlock().getAdapter(IPersistableDebugElement.class); int defaultColSize = -1; if (elmt != null) { if (elmt.supportsProperty(this, IDebugPreferenceConstants.PREF_COL_SIZE_BY_MODEL)) { defaultColSize = getDefaultFromPersistableElement(IDebugPreferenceConstants.PREF_COL_SIZE_BY_MODEL); } } if (defaultColSize <= 0) { // if not provided, get default by model defaultColSize = getDefaultColumnSizeByModel(getMemoryBlock().getModelIdentifier()); } if (defaultColSize > 0) { columnSize = defaultColSize * getAddressableSize(); } } return columnSize; } private int getDefaultRowSize() { int rowSize = DebugUITools.getPreferenceStore().getInt(IDebugPreferenceConstants.PREF_ROW_SIZE); int bytePerLine = rowSize * getAddressableSize(); // check synchronized row size Integer size = (Integer) getSynchronizedProperty(AbstractAsyncTableRendering.PROPERTY_ROW_SIZE); if (size != null) { // row size is stored as actual number of bytes in synchronizer int syncRowSize = size.intValue(); if (syncRowSize > 0) { bytePerLine = syncRowSize; } } else { int defaultRowSize = -1; IPersistableDebugElement elmt = getMemoryBlock().getAdapter(IPersistableDebugElement.class); if (elmt != null) { if (elmt.supportsProperty(this, IDebugPreferenceConstants.PREF_ROW_SIZE_BY_MODEL)) { defaultRowSize = getDefaultFromPersistableElement(IDebugPreferenceConstants.PREF_ROW_SIZE_BY_MODEL); return defaultRowSize * getAddressableSize(); } } if (defaultRowSize <= 0) { // no synchronized property, ask preference store by id defaultRowSize = getDefaultRowSizeByModel(getMemoryBlock().getModelIdentifier()); } if (defaultRowSize > 0) { bytePerLine = defaultRowSize * getAddressableSize(); } } return bytePerLine; } /** * Returns the addressable size of this rendering's memory block in bytes. * * @return the addressable size of this rendering's memory block in bytes */ @Override public int getAddressableSize() { return fAddressableSize; } private Object getSynchronizedProperty(String propertyId) { IMemoryRenderingSynchronizationService syncService = getMemoryRenderingContainer().getMemoryRenderingSite().getSynchronizationService(); if (syncService == null) { return null; } return syncService.getProperty(getMemoryBlock(), propertyId); } private int getDefaultFromPersistableElement(String propertyId) { int defaultValue = -1; IPersistableDebugElement elmt = getMemoryBlock().getAdapter(IPersistableDebugElement.class); if (elmt != null) { try { Object valueMB = elmt.getProperty(this, propertyId); if (valueMB != null && !(valueMB instanceof Integer)) { IStatus status = DebugUIPlugin.newErrorStatus("Model returned invalid type on " + propertyId, null); //$NON-NLS-1$ DebugUIPlugin.log(status); } if (valueMB != null) { Integer value = (Integer) valueMB; defaultValue = value.intValue(); } } catch (CoreException e) { DebugUIPlugin.log(e); } } return defaultValue; } /** * @param modelId the {@link String} model id * @return default number of addressable units per line for the model */ private int getDefaultRowSizeByModel(String modelId) { int row = DebugUITools.getPreferenceStore().getInt(getRowPrefId(modelId)); if (row == 0) { DebugUITools.getPreferenceStore().setValue(getRowPrefId(modelId), IDebugPreferenceConstants.PREF_ROW_SIZE_DEFAULT); } row = DebugUITools.getPreferenceStore().getInt(getRowPrefId(modelId)); return row; } /** * @param modelId the {@link String} model id * @return default number of addressable units per column for the model */ private int getDefaultColumnSizeByModel(String modelId) { int col = DebugUITools.getPreferenceStore().getInt(getColumnPrefId(modelId)); if (col == 0) { DebugUITools.getPreferenceStore().setValue(getColumnPrefId(modelId), IDebugPreferenceConstants.PREF_COLUMN_SIZE_DEFAULT); } col = DebugUITools.getPreferenceStore().getInt(getColumnPrefId(modelId)); return col; } private String getRowPrefId(String modelId) { String rowPrefId = IDebugPreferenceConstants.PREF_ROW_SIZE + ":" + modelId; //$NON-NLS-1$ return rowPrefId; } private String getColumnPrefId(String modelId) { String colPrefId = IDebugPreferenceConstants.PREF_COLUMN_SIZE + ":" + modelId; //$NON-NLS-1$ return colPrefId; } /** * Format view tab based on the bytes per line and column. * * @param bytesPerLine - number of bytes per line, possible values: (1 / 2 / * 4 / 8 / 16 / 32 / 64/ 128) * addressableSize * @param columnSize - number of bytes per column, possible values: (1 / 2 / * 4 / 8 / 16 / 32/ 64 / 128) * addressableSize * @return true if format is successful, false, otherwise */ @Override public boolean format(int bytesPerLine, int columnSize) { // bytes per cell must be divisible to bytesPerLine if (bytesPerLine % columnSize != 0) { return false; } if (bytesPerLine < columnSize) { return false; } // do not format if the view tab is already in that format if (fBytePerLine == bytesPerLine && fColumnSize == columnSize) { return false; } fBytePerLine = bytesPerLine; fColumnSize = columnSize; formatViewer(); updateSyncRowSize(); updateSyncColSize(); return true; } /** * Returns the number of addressable units per row. * * @return number of addressable units per row */ @Override public int getAddressableUnitPerLine() { return fBytePerLine / getAddressableSize(); } /** * Returns the number of addressable units per column. * * @return number of addressable units per column */ @Override public int getAddressableUnitPerColumn() { return fColumnSize / getAddressableSize(); } /** * This method estimates the number of visible lines in the rendering table. * * @return estimated number of visible lines in the table */ private int getNumberOfVisibleLines() { if (fTableViewer == null) { return -1; } Table table = fTableViewer.getTable(); int height = fTableViewer.getTable().getSize().y; // when table is not yet created, height is zero if (height == 0) { // make use of the table viewer to estimate table size height = fTableViewer.getTable().getParent().getSize().y; } if (height == 0) { return 0; } // height of border int border = fTableViewer.getTable().getHeaderHeight(); // height of scroll bar int scroll = fTableViewer.getTable().getHorizontalBar().getSize().y; // height of table is table's area minus border and scroll bar height height = height - border - scroll; // calculate number of visible lines int lineHeight = getMinTableItemHeight(table); int numberOfLines = height / lineHeight; if (numberOfLines <= 0) { return 0; } return numberOfLines; } private int getMinTableItemHeight(Table table) { // Hack to get around Linux GTK problem. // On Linux GTK, table items have variable item height as // carriage returns are actually shown in a cell. Some rows will be // taller than others. When calculating number of visible lines, we // need to find the smallest table item height. Otherwise, the rendering // underestimates the number of visible lines. As a result the rendering // will not be able to get more memory as needed. if (MemoryViewUtil.isLinuxGTK()) { // check each of the items and find the minimum TableItem[] items = table.getItems(); int minHeight = table.getItemHeight(); for (int i = 0; i < items.length; i++) { if (items[i].getData() != null) { minHeight = Math.min(items[i].getBounds(0).height, minHeight); } } return minHeight; } return table.getItemHeight(); } private BigInteger getMemoryBlockBaseAddress() throws DebugException { if (getMemoryBlock() instanceof IMemoryBlockExtension) { return ((IMemoryBlockExtension) getMemoryBlock()).getBigBaseAddress(); } else { return BigInteger.valueOf(getMemoryBlock().getStartAddress()); } } /** * Displays the given message on the error page * * @param message - the message to display */ protected void showMessage(final String message) { fSwitchPageJob.setShowMessagePage(true); fSwitchPageJob.setMessage(message); fSwitchPageJob.schedule(); } /** * Returns the number of bytes displayed in a single column cell. * * @return the number of bytes displayed in a single column cell */ @Override public int getBytesPerColumn() { return fColumnSize; } /** * Returns the number of bytes displayed in a row. * * @return the number of bytes displayed in a row */ @Override public int getBytesPerLine() { return fBytePerLine; } /** * Returns whether the error page is displayed. * * @return whether the error page is displayed */ public boolean isDisplayingError() { return fShowMessage; } /** * Displays the content of the table viewer. */ public void showTable() { fSwitchPageJob.setShowMessagePage(false); fSwitchPageJob.schedule(); } private BigInteger getTopVisibleAddress() { if (fTableViewer == null) { return BigInteger.valueOf(0); } Table table = fTableViewer.getTable(); int topIndex = table.getTopIndex(); if (topIndex < 0) { return null; } if (table.getItemCount() > topIndex) { MemorySegment topItem = (MemorySegment) table.getItem(topIndex).getData(); if (topItem != null) { return topItem.getAddress(); } } return null; } private synchronized void reloadTable(final BigInteger topAddress) { if (DebugUIPlugin.DEBUG_DYNAMIC_LOADING) { DebugUIPlugin.trace(this + " reload at: " + topAddress.toString(16)); //$NON-NLS-1$ } fContentDescriptor.setLoadAddress(topAddress); fContentDescriptor.setNumLines(getNumLinesToLoad()); fTableViewer.setTopIndex(topAddress); fTableViewer.refresh(); } private boolean isAtTopLimit() { BigInteger startAddress = fContentDescriptor.getStartAddress(); startAddress = MemoryViewUtil.alignToBoundary(startAddress, getAddressableUnitPerLine()); AbstractVirtualContentTableModel model = fTableViewer.getVirtualContentModel(); if (model != null) { Object key = model.getKey(0); if (key instanceof BigInteger) { BigInteger startBufferAddress = (BigInteger) key; startBufferAddress = MemoryViewUtil.alignToBoundary(startBufferAddress, getAddressableUnitPerLine()); if (startAddress.compareTo(startBufferAddress) == 0) { return true; } } } return false; } private boolean isAtBottomLimit() { BigInteger endAddress = fContentDescriptor.getEndAddress(); endAddress = MemoryViewUtil.alignToBoundary(endAddress, getAddressableUnitPerLine()); AbstractVirtualContentTableModel model = fTableViewer.getVirtualContentModel(); if (model != null) { int numElements = model.getElements().length; Object key = model.getKey(numElements - 1); if (key instanceof BigInteger) { BigInteger endBufferAddress = (BigInteger) key; endBufferAddress = MemoryViewUtil.alignToBoundary(endBufferAddress, getAddressableUnitPerLine()); if (endAddress.compareTo(endBufferAddress) == 0) { return true; } } } return false; } private void formatViewer() { fTableViewer.disposeColumns(); fTableViewer.disposeCellEditors(); doFormatTable(); fTableViewer.setColumnHeaders(getColumnProperties()); fTableViewer.showColumnHeader(true); Table table = fTableViewer.getTable(); int colCnt = table.getColumnCount(); CellEditor[] editors = new CellEditor[fTableViewer.getTable().getColumnCount()]; for (int i = 0; i < colCnt; i++) { editors[i] = createCellEditor(table, i); } fTableViewer.setCellEditors(editors); fTableViewer.formatViewer(); // This resize needs to happen after the viewer has finished // getting the labels. // This fix is a hack to delay the resize until the viewer has a chance // to get // the setData event from the UI thread. Otherwise, the columns will be // squeezed together. UIJob job = new UIJob("resize to fit") { //$NON-NLS-1$ @Override public IStatus runInUIThread(IProgressMonitor monitor) { resizeColumnsToPreferredSize(); return Status.OK_STATUS; } }; job.setSystem(true); job.schedule(); } private void doFormatTable() { int bytesPerLine = getBytesPerLine(); int columnSize = getBytesPerColumn(); int numColumns = bytesPerLine / columnSize; Table table = fTableViewer.getTable(); TableColumn column0 = new TableColumn(table, SWT.LEFT, 0); column0.setText(DebugUIMessages.AbstractTableRendering_2); // create new byte columns TableColumn[] byteColumns = new TableColumn[numColumns]; for (int i = 0; i < byteColumns.length; i++) { TableColumn column = new TableColumn(table, SWT.LEFT, i + 1); byteColumns[i] = column; } // Empty column for cursor navigation TableColumn emptyCol = new TableColumn(table, SWT.LEFT, byteColumns.length + 1); emptyCol.setText(" "); //$NON-NLS-1$ emptyCol.setWidth(1); emptyCol.setResizable(false); table.setHeaderVisible(true); // allow clients to override column labels setColumnHeadings(); } private String[] getColumnProperties() { int numColumns = getAddressableUnitPerLine() / getAddressableUnitPerColumn(); // +2 to include properties for address and navigation column String[] columnProperties = new String[numColumns + 2]; columnProperties[0] = TableRenderingLine.P_ADDRESS; int addressableUnit = getAddressableUnitPerColumn(); // use column beginning offset to the row address as properties for (int i = 1; i < columnProperties.length - 1; i++) { // column properties are stored as number of addressable units from // the // the line address columnProperties[i] = Integer.toHexString((i - 1) * addressableUnit); } // Empty column for cursor navigation columnProperties[columnProperties.length - 1] = " "; //$NON-NLS-1$ return columnProperties; } /** * Create a cell editor from the specified composite and column. * * @param composite parent composite that the cell editor is to be created * from. * @param column the column where the cell editor is required * @return the cell editor for editing memory * * @since 3.3 * */ protected CellEditor createCellEditor(Composite composite, int column) { return new TextCellEditor(composite); } private ICellModifier newInternalCellModifier() { return new AsyncTableRenderingCellModifier(this, createCellModifier()); } /** * Create a custom cell modifier for this rendering. Return null if the * default cell modifier is to be used. * * @return the cell modifier for this rendering, or <code>null</code> if the * default cell modifier is to be used. * * @since 3.3 * */ protected ICellModifier createCellModifier() { return null; } /* * (non-Javadoc) * @see org.eclipse.debug.ui.memory.AbstractMemoryRendering#dispose() */ @Override public void dispose() { if (fIsDisposed) { return; } fIsDisposed = true; removeListeners(); if (fMenuMgr != null) { fMenuMgr.removeAll(); fMenuMgr.dispose(); fMenuMgr = null; } if (fTableViewer != null) { if (fViewerListener != null) { fTableViewer.removeVirtualContentListener(fViewerListener); } if (fPresentationErrorListener != null) { fTableViewer.removePresentationErrorListener(fPresentationErrorListener); } fTableViewer.removeSelectionChangedListener(fViewerSelectionChangedListener); fTableViewer.getTable().getVerticalBar().removeSelectionListener(fScrollBarSelectionListener); fTableViewer.dispose(); } if (fPresentationContext != null) { fPresentationContext.dispose(); } if (fToolTipShell != null && !fToolTipShell.isDisposed()) { fToolTipShell.dispose(); fToolTipShell = null; } fIsDisposed = true; super.dispose(); } /** * Updates the label of this rendering, optionally displaying the base * address of this rendering's memory block. * * @param showAddress whether to display the base address of this * rendering's memory block in this rendering's label */ protected void updateRenderingLabel(final boolean showAddress) { Job job = new Job("Update Rendering Label") { //$NON-NLS-1$ @Override protected IStatus run(IProgressMonitor monitor) { if (fIsDisposed) { return Status.OK_STATUS; } fLabel = buildLabel(showAddress); firePropertyChangedEvent(new PropertyChangeEvent(AbstractAsyncTableRendering.this, IBasicPropertyConstants.P_TEXT, null, fLabel)); return Status.OK_STATUS; } }; job.setSystem(true); job.setRule(serialByRenderingRule); job.schedule(); } private String buildLabel(boolean showAddress) { String label = IInternalDebugCoreConstants.EMPTY_STRING; if (getMemoryBlock() instanceof IMemoryBlockExtension) { label = ((IMemoryBlockExtension) getMemoryBlock()).getExpression(); if (label == null) { label = DebugUIMessages.AbstractTableRendering_8; } if (label.startsWith("&")) { //$NON-NLS-1$ label = "&" + label; //$NON-NLS-1$ } try { if (showAddress && ((IMemoryBlockExtension) getMemoryBlock()).getBigBaseAddress() != null) { label += " : 0x"; //$NON-NLS-1$ label += ((IMemoryBlockExtension) getMemoryBlock()).getBigBaseAddress().toString(16).toUpperCase(); } } catch (DebugException e) { // do nothing, the label will not show the address } } else { long address = getMemoryBlock().getStartAddress(); label = Long.toHexString(address).toUpperCase(); } String preName = DebugUITools.getMemoryRenderingManager().getRenderingType(getRenderingId()).getLabel(); if (preName != null) { label += " <" + preName + ">"; //$NON-NLS-1$ //$NON-NLS-2$ } return decorateLabel(label); } /* * Returns the label of this rendering. * @return label of this rendering */ @Override public String getLabel() { if (fLabel == null) { fLabel = DebugUIMessages.AbstractAsyncTableRendering_1; updateRenderingLabel(isVisible()); } return fLabel; } /* * (non-Javadoc) * @see org.eclipse.core.runtime.PlatformObject#getAdapter(java.lang.Class) */ @SuppressWarnings("unchecked") @Override public <T> T getAdapter(Class<T> adapter) { if (adapter == IColorProvider.class) { return (T) getColorProviderAdapter(); } if (adapter == ILabelProvider.class) { return (T) getLabelProviderAdapter(); } if (adapter == IFontProvider.class) { return (T) getFontProviderAdapter(); } if (adapter == IModelChangedListener.class) { return (T) fModelChangedListener; } if (adapter == IWorkbenchAdapter.class) { // needed workbench adapter to fill the title of property page if (fWorkbenchAdapter == null) { fWorkbenchAdapter = new IWorkbenchAdapter() { @Override public Object[] getChildren(Object o) { return new Object[0]; } @Override public ImageDescriptor getImageDescriptor(Object object) { return null; } @Override public String getLabel(Object o) { return AbstractAsyncTableRendering.this.getLabel(); } @Override public Object getParent(Object o) { return null; } }; } return (T) fWorkbenchAdapter; } if (adapter == TableRenderingContentDescriptor.class) { return (T) getContentDescriptor(); } return super.getAdapter(adapter); } /** * Returns the number of characters a byte will convert to or -1 if unknown. * * @return the number of characters a byte will convert to or -1 if unknown */ @Override public int getNumCharsPerByte() { return -1; } /** * Create actions for this rendering */ protected void createActions() { fCopyToClipboardAction = new AsyncCopyTableRenderingAction(this, fTableViewer); fGoToAddressAction = new RenderingGoToAddressAction(getMemoryRenderingContainer(), this); fResetMemoryBlockAction = new ResetToBaseAddressAction(this); fPrintViewTabAction = new AsyncPrintTableRenderingAction(this, fTableViewer); fFormatRenderingAction = new FormatTableRenderingAction(this); fReformatAction = new ReformatAction(this); fToggleAddressColumnAction = new ToggleAddressColumnAction(); IMemoryRenderingSite site = getMemoryRenderingContainer().getMemoryRenderingSite(); if (site.getSite().getSelectionProvider() != null) { fPropertiesDialogAction = new PropertyDialogAction(site.getSite(), site.getSite().getSelectionProvider()); } fNextAction = new NextPageAction(); fPrevAction = new PrevPageAction(); } /** * Returns the currently selected address in this rendering. * * @return the currently selected address in this rendering */ @Override public BigInteger getSelectedAddress() { if (!fIsCreated) { return null; } Object key = fTableViewer.getSelectionKey(); if (key != null && key instanceof BigInteger) { return (BigInteger) key; } return null; } /** * Returns the currently selected content in this rendering as MemoryByte. * * @return the currently selected content in array of MemoryByte. Returns an * empty array if the selected address is out of buffered range. */ @Override public MemoryByte[] getSelectedAsBytes() { if (getSelectedAddress() == null) { return new MemoryByte[0]; } Object key = fTableViewer.getSelectionKey(); AbstractVirtualContentTableModel model = fTableViewer.getVirtualContentModel(); if (model != null) { model = (AbstractVirtualContentTableModel) fTableViewer.getModel(); int row = model.indexOfKey(key); Object element = model.getElement(row); int col = model.columnOf(element, key); // check precondition if (col <= 0 || col > getBytesPerLine() / getBytesPerColumn()) { return new MemoryByte[0]; } if (!(element instanceof MemorySegment)) { return new MemoryByte[0]; } MemorySegment line = (MemorySegment) element; int offset = (col - 1) * (getAddressableUnitPerColumn() * getAddressableSize()); // make a copy of the bytes to ensure that data cannot be changed // by caller MemoryByte[] bytes = line.getBytes(offset, getAddressableUnitPerColumn() * getAddressableSize()); MemoryByte[] retBytes = new MemoryByte[bytes.length]; System.arraycopy(bytes, 0, retBytes, 0, bytes.length); return retBytes; } return new MemoryByte[0]; } /** * Returns the currently selected content in this rendering as a String. * * @return the currently selected content in this rendering */ @Override public String getSelectedAsString() { if (getSelectedAddress() == null) { return IInternalDebugCoreConstants.EMPTY_STRING; } MemoryByte[] bytes = getSelectedAsBytes(); if (bytes.length > 0) { return getString(this.getRenderingId(), getSelectedAddress(), bytes); } else { return IInternalDebugCoreConstants.EMPTY_STRING; } } /** * Moves the cursor to the specified address. Will load more memory if the * address is not currently visible. * * @param address address to position cursor at * @throws DebugException if an exception occurs */ @Override public void goToAddress(final BigInteger address) throws DebugException { if (!fIsCreated || fTableViewer.getVirtualContentModel() == null) { return; } final int keyIndex = fTableViewer.getVirtualContentModel().indexOfKey(address); if (keyIndex < 0) { // if not extended memory block // do not allow user to go to an address that's out of range if (!(getMemoryBlock() instanceof IMemoryBlockExtension)) { Status stat = new Status(IStatus.ERROR, DebugUIPlugin.getUniqueIdentifier(), DebugException.NOT_SUPPORTED, DebugUIMessages.AbstractTableRendering_11, null); DebugException e = new DebugException(stat); throw e; } BigInteger startAdd = fContentDescriptor.getStartAddress(); BigInteger endAdd = fContentDescriptor.getEndAddress(); if (address.compareTo(startAdd) < 0 || address.compareTo(endAdd) > 0) { Status stat = new Status(IStatus.ERROR, DebugUIPlugin.getUniqueIdentifier(), DebugException.NOT_SUPPORTED, DebugUIMessages.AbstractTableRendering_11, null); DebugException e = new DebugException(stat); throw e; } } Runnable runnable = new Runnable() { @Override public void run() { showTable(); if (keyIndex >= 0) { // address is within range, set cursor and reveal fTableViewer.setSelection(address); updateSyncTopAddress(getTopVisibleAddress()); updateSyncSelectedAddress(address); } else { // load at the address fTableViewer.setSelection(address); reloadTable(address); updateSyncSelectedAddress(address); if (!isDynamicLoad()) { updateSyncPageStartAddress(address); } updateSyncTopAddress(address); } } }; runOnUIThread(runnable); } /** * Refresh the table viewer with the current top visible address. Update * labels in the memory rendering. */ @Override public void refresh() { if (!fIsCreated) { return; } fTableViewer.refresh(); } /** * Resize column to the preferred size. */ @Override public void resizeColumnsToPreferredSize() { if (!fIsCreated) { return; } fTableViewer.resizeColumnsToPreferredSize(); if (!fIsShowAddressColumn) { final TableColumn column = fTableViewer.getTable().getColumn(0); column.addControlListener(new ControlListener() { @Override public void controlMoved(ControlEvent e) { } @Override public void controlResized(ControlEvent e) { column.removeControlListener(this); column.setWidth(0); } }); } } /** * Updates labels of this rendering. */ @Override public void updateLabels() { if (!fIsCreated) { return; } UIJob job = new UIJob("updateLabels") { //$NON-NLS-1$ @Override public IStatus runInUIThread(IProgressMonitor monitor) { // do not handle if the rendering is already disposed if (fPageBook.isDisposed()) { return Status.OK_STATUS; } // update tab labels updateRenderingLabel(true); if (fTableViewer != null) { // update column labels setColumnHeadings(); // rebuild cache and force labels to be refreshed fTableViewer.formatViewer(); } return Status.OK_STATUS; } }; job.setSystem(true); job.schedule(); } /** * Fills the context menu for this rendering * * @param menu menu to fill */ protected void fillContextMenu(IMenuManager menu) { menu.add(new Separator(EMPTY_MEMORY_GROUP)); menu.add(new Separator()); menu.add(fResetMemoryBlockAction); menu.add(fGoToAddressAction); menu.add(new Separator(EMPTY_NAVIGATION_GROUP)); menu.add(new Separator()); menu.add(fFormatRenderingAction); if (!isDynamicLoad() && getMemoryBlock() instanceof IMemoryBlockExtension) { menu.add(new Separator()); menu.add(fPrevAction); menu.add(fNextAction); menu.add(new Separator(EMPTY_NON_AUTO_LOAD_GROUP)); } menu.add(new Separator()); menu.add(fReformatAction); menu.add(fToggleAddressColumnAction); menu.add(new Separator()); menu.add(fCopyToClipboardAction); menu.add(fPrintViewTabAction); if (fPropertiesDialogAction != null) { menu.add(new Separator()); menu.add(fPropertiesDialogAction); menu.add(new Separator(EMPTY_PROPERTY_GROUP)); } menu.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); } private int getPageSizeInUnits() { return fPageSize * getAddressableUnitPerLine(); } private void getPageSizeFromPreference() { fPageSize = DebugUIPlugin.getDefault().getPreferenceStore().getInt(IDebugPreferenceConstants.PREF_TABLE_RENDERING_PAGE_SIZE); } private void getPreBufferSizeFromPreference() { fPreBufferSize = DebugUIPlugin.getDefault().getPreferenceStore().getInt(IDebugPreferenceConstants.PREF_TABLE_RENDERING_PRE_BUFFER_SIZE); } private void getPostBufferSizeFromPreference() { fPostBufferSize = DebugUIPlugin.getDefault().getPreferenceStore().getInt(IDebugPreferenceConstants.PREF_TABLE_RENDERING_POST_BUFFER_SIZE); } private void updateDynamicLoadProperty() { boolean value = DebugUIPlugin.getDefault().getPreferenceStore().getBoolean(IDebugPreferenceConstants.PREF_DYNAMIC_LOAD_MEM); if (value != isDynamicLoad()) { setDynamicLoad(value); if (!fIsDisposed) { if (isDynamicLoad()) { fContentDescriptor.setPostBuffer(getPostBufferSize()); fContentDescriptor.setPreBuffer(getPreBufferSize()); fContentDescriptor.setNumLines(getNumberOfVisibleLines()); } else { fContentDescriptor.setPostBuffer(0); fContentDescriptor.setPreBuffer(0); fContentDescriptor.setNumLines(fPageSize); } } } } private void getDynamicLoadFromPreference() { setDynamicLoad(DebugUIPlugin.getDefault().getPreferenceStore().getBoolean(IDebugPreferenceConstants.PREF_DYNAMIC_LOAD_MEM)); } private boolean isDynamicLoad() { return fContentDescriptor.isDynamicLoad(); } private int getPageSize() { return fPageSize; } private int getNumLinesToLoad() { int numberOfLines = -1; if (isDynamicLoad()) { numberOfLines = getNumberOfVisibleLines(); } else { numberOfLines = getPageSize(); } return numberOfLines; } private void setDynamicLoad(boolean load) { fContentDescriptor.setDynamicLoad(load); } private void handlePageStartAddressChanged(BigInteger address) { // do not handle if in dynamic mode if (isDynamicLoad()) { return; } if (!(getMemoryBlock() instanceof IMemoryBlockExtension)) { return; } // do not handle event if the base address of the memory // block has changed, wait for debug event to update to // new location if (isMemoryBlockBaseAddressChanged()) { return; } Object key = fTableViewer.getKey(0); if (key != null) { if (key.equals(address)) { return; } } BigInteger start = fContentDescriptor.getStartAddress(); BigInteger end = fContentDescriptor.getEndAddress(); // smaller than start address, load at start address if (address.compareTo(start) < 0) { if (isAtTopLimit()) { return; } address = start; } // bigger than end address, no need to load, already at top if (address.compareTo(end) > 0) { if (isAtBottomLimit()) { return; } address = end.subtract(BigInteger.valueOf(getPageSizeInUnits())); } fContentDescriptor.setLoadAddress(address); final BigInteger finaladdress = address; Runnable runnable = new Runnable() { @Override public void run() { if (fTableViewer.getTable().isDisposed()) { return; } fTableViewer.setTopIndex(finaladdress); refresh(); } }; runOnUIThread(runnable); updateSyncPageStartAddress(address); updateSyncTopAddress(address); } private void handleDyanicLoadChanged() { // if currently in dynamic load mode, update page // start address BigInteger pageStart = getTopVisibleAddress(); updateSyncPageStartAddress(pageStart); updateDynamicLoadProperty(); if (isDynamicLoad()) { refresh(); fTableViewer.setTopIndex(pageStart); } else { handlePageStartAddressChanged(pageStart); } } /* * (non-Javadoc) * @see org.eclipse.debug.ui.memory.IMemoryRendering#becomesHidden() */ @Override public void becomesHidden() { // creates new object for storing potential changes in sync properties fPendingSyncProperties = new PendingPropertyChanges(); super.becomesHidden(); if (getMemoryBlock() instanceof IMemoryBlockExtension) { updateRenderingLabel(false); } } /* * (non-Javadoc) * @see org.eclipse.debug.ui.memory.IMemoryRendering#becomesVisible() */ @Override public void becomesVisible() { if (!fIsCreated) { // label can still be constructed even though the rendering has not // finished being // initialized updateRenderingLabel(true); super.becomesVisible(); return; } // do not do anything if already visible if (isVisible() == true) { // super should always be called super.becomesVisible(); return; } super.becomesVisible(); if (fPendingSyncProperties != null) { // deal with format boolean format = false; int rowSize = getBytesPerLine(); if (fPendingSyncProperties.getRowSize() > 0) { format = true; rowSize = fPendingSyncProperties.getRowSize(); } int colSize = getBytesPerColumn(); if (fPendingSyncProperties.getColumnSize() > 0) { format = true; colSize = fPendingSyncProperties.getColumnSize(); } if (format) { format(rowSize, colSize); } BigInteger selectedAddress = fPendingSyncProperties.getSelectedAddress(); if (selectedAddress != null) { fTableViewer.setSelection(selectedAddress); } updateDynamicLoadProperty(); if (isDynamicLoad()) { BigInteger topVisibleAddress = fPendingSyncProperties.getTopVisibleAddress(); if (topVisibleAddress != null) { fContentDescriptor.setLoadAddress(topVisibleAddress); fTableViewer.setTopIndex(topVisibleAddress); } } else if (!(getMemoryBlock() instanceof IMemoryBlockExtension)) { BigInteger topVisibleAddress = fPendingSyncProperties.getTopVisibleAddress(); if (topVisibleAddress != null) { fTableViewer.setTopIndex(topVisibleAddress); } } else { if (fPendingSyncProperties.getPageSize() > 0) { fPageSize = fPendingSyncProperties.getPageSize(); fContentDescriptor.setNumLines(fPageSize); } BigInteger pageStartAddress = fPendingSyncProperties.getPageStartAddress(); if (pageStartAddress != null) { fContentDescriptor.setLoadAddress(pageStartAddress); } fTableViewer.setTopIndex(pageStartAddress); } showTable(); refresh(); } updateRenderingLabel(true); Job job = new Job("becomesVisible") //$NON-NLS-1$ { @Override protected IStatus run(IProgressMonitor monitor) { if (fIsDisposed) { return Status.OK_STATUS; } try { fContentDescriptor.updateContentBaseAddress(); } catch (DebugException e) { showMessage(e.getMessage()); } return Status.OK_STATUS; } }; job.setSystem(true); job.schedule(); // discard these properties fPendingSyncProperties = null; } /** * Handle column size changed event from synchronizer * * @param newColumnSize the new column size */ private void columnSizeChanged(final int newColumnSize) { // ignore event if rendering is not visible if (!isVisible()) { return; } Display.getDefault().asyncExec(new Runnable() { @Override public void run() { int rowSize = getBytesPerLine(); if (rowSize < newColumnSize) { rowSize = newColumnSize; } format(rowSize, newColumnSize); } }); } /** * @param newRowSize - new row size in number of bytes */ private void rowSizeChanged(final int newRowSize) { // ignore event if rendering is not visible if (!isVisible()) { return; } Display.getDefault().asyncExec(new Runnable() { @Override public void run() { int colSize = getBytesPerColumn(); if (newRowSize < colSize) { colSize = newRowSize; } format(newRowSize, colSize); } }); } /** * update selected address in synchronizer if update is true. * * @param address the address to update */ private void updateSyncSelectedAddress(BigInteger address) { if (!fIsCreated) { return; } PropertyChangeEvent event = new PropertyChangeEvent(this, AbstractAsyncTableRendering.PROPERTY_SELECTED_ADDRESS, null, address); firePropertyChangedEvent(event); } /** * update column size in synchronizer */ private void updateSyncColSize() { if (!fIsCreated) { return; } PropertyChangeEvent event = new PropertyChangeEvent(this, AbstractAsyncTableRendering.PROPERTY_COL_SIZE, null, Integer.valueOf(fColumnSize)); firePropertyChangedEvent(event); } /** * update column size in synchronizer */ private void updateSyncRowSize() { if (!fIsCreated) { return; } PropertyChangeEvent event = new PropertyChangeEvent(this, AbstractAsyncTableRendering.PROPERTY_ROW_SIZE, null, Integer.valueOf(fBytePerLine)); firePropertyChangedEvent(event); } /** * update top visible address in synchronizer * * @param address the address to update */ private void updateSyncTopAddress(BigInteger address) { if (!fIsCreated) { return; } PropertyChangeEvent event = new PropertyChangeEvent(this, AbstractAsyncTableRendering.PROPERTY_TOP_ADDRESS, null, address); firePropertyChangedEvent(event); } private void updateSyncPageStartAddress(BigInteger address) { if (!fIsCreated) { return; } if (isMemoryBlockBaseAddressChanged()) { return; } PropertyChangeEvent event = new PropertyChangeEvent(this, IInternalDebugUIConstants.PROPERTY_PAGE_START_ADDRESS, null, address); firePropertyChangedEvent(event); } /** * Returns the color provider for this rendering's memory block or * <code>null</code> if none. * <p> * By default a color provider is obtained by asking this rendering's memory * block for its {@link IColorProvider} adapter. When the color provider is * queried for color information, it is provided with a * {@link MemoryRenderingElement} as an argument. * </p> * * @return the color provider for this rendering's memory block, or * <code>null</code> */ protected IColorProvider getColorProviderAdapter() { return getMemoryBlock().getAdapter(IColorProvider.class); } /** * Returns the label provider for this rendering's memory block or * <code>null</code> if none. * <p> * By default a label provider is obtained by asking this rendering's memory * block for its {@link ILabelProvider} adapter. When the label provider is * queried for label information, it is provided with a * {@link MemoryRenderingElement} as an argument. * </p> * * @return the label provider for this rendering's memory block, or * <code>null</code> */ protected ILabelProvider getLabelProviderAdapter() { return getMemoryBlock().getAdapter(ILabelProvider.class); } /** * Returns the font provider for this rendering's memory block or * <code>null</code> if none. * <p> * By default a font provider is obtained by asking this rendering's memory * block for its {@link IFontProvider} adapter. When the font provider is * queried for font information, it is provided with a * {@link MemoryRenderingElement} as an argument. * </p> * * @return the font provider for this rendering's memory block, or * <code>null</code> */ protected IFontProvider getFontProviderAdapter() { return getMemoryBlock().getAdapter(IFontProvider.class); } /** * Returns the table presentation for this rendering's memory block or * <code>null</code> if none. * <p> * By default a table presentation is obtained by asking this rendering's * memory block for its {@link IMemoryBlockTablePresentation} adapter. * </p> * * @return the table presentation for this rendering's memory block, or * <code>null</code> */ protected IMemoryBlockTablePresentation getTablePresentationAdapter() { return getMemoryBlock().getAdapter(IMemoryBlockTablePresentation.class); } /** * Setup the viewer so it supports hovers to show the offset of each field */ private void createToolTip() { fToolTipShell = new Shell(DebugUIPlugin.getShell(), SWT.ON_TOP | SWT.RESIZE); GridLayout gridLayout = new GridLayout(); gridLayout.numColumns = 1; gridLayout.marginWidth = 2; gridLayout.marginHeight = 0; fToolTipShell.setLayout(gridLayout); fToolTipShell.setBackground(fTableViewer.getTable().getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND)); final Control toolTipControl = createToolTipControl(fToolTipShell); if (toolTipControl == null) { // if client decide not to use tooltip support fToolTipShell.dispose(); return; } MouseTrackAdapter listener = new MouseTrackAdapter() { private TableItem fTooltipItem = null; private int fCol = -1; @Override public void mouseExit(MouseEvent e) { if (!fToolTipShell.isDisposed()) { fToolTipShell.setVisible(false); } fTooltipItem = null; } @Override public void mouseHover(MouseEvent e) { Point hoverPoint = new Point(e.x, e.y); Control control = null; if (e.widget instanceof Control) { control = (Control) e.widget; } if (control == null) { return; } hoverPoint = control.toDisplay(hoverPoint); TableItem item = getItem(hoverPoint); int column = getColumn(hoverPoint); // Only if there is a change in hover if (this.fTooltipItem != item || fCol != column) { // Keep Track of the latest hover fTooltipItem = item; fCol = column; if (item != null) { toolTipAboutToShow(toolTipControl, fTooltipItem, column); // Setting location of the tooltip Rectangle shellBounds = fToolTipShell.getBounds(); shellBounds.x = hoverPoint.x; shellBounds.y = hoverPoint.y + item.getBounds(0).height; fToolTipShell.setBounds(shellBounds); fToolTipShell.pack(); fToolTipShell.setVisible(true); } else { fToolTipShell.setVisible(false); } } } }; fTableViewer.getTable().addMouseTrackListener(listener); fTableViewer.getCursor().addMouseTrackListener(listener); } /** * Creates the control used to display tool tips for cells in this table. By * default a label is used to display the address of the cell. Clients may * override this method to create custom tooltip controls. * <p> * Also see the methods <code>getToolTipText(...)</code> and * <code>toolTipAboutToShow(...)</code>. * </p> * * @param composite parent for the tooltip control * @return the tooltip control to be displayed * @since 3.2 */ protected Control createToolTipControl(Composite composite) { Control fToolTipLabel = new Label(composite, SWT.NONE); fToolTipLabel.setForeground(fTableViewer.getTable().getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND)); fToolTipLabel.setBackground(fTableViewer.getTable().getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND)); fToolTipLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_CENTER)); return fToolTipLabel; } /** * Bug with table widget,BUG 113015, the widget is not able to return the * correct table item if SWT.FULL_SELECTION is not on when the table is * created. Created the following function to work around the problem. We * can remove this method when the bug is fixed. * * @param point the given {@link Point} to find the {@link TableItem} for * @return the table item where the point is located, return null if the * item cannot be located. */ private TableItem getItem(Point point) { TableItem[] items = fTableViewer.getTable().getItems(); for (int i = 0; i < items.length; i++) { TableItem item = items[i]; if (item.getData() != null) { Point start = new Point(item.getBounds(0).x, item.getBounds(0).y); start = fTableViewer.getTable().toDisplay(start); Point end = new Point(start.x + item.getBounds(0).width, start.y + item.getBounds(0).height); if (start.y < point.y && point.y < end.y) { return item; } } } return null; } /** * Method for figuring out which column the point is located. * * @param point the {@link Point} the get the column number for * @return the column index where the point is located, return -1 if column * is not found. */ private int getColumn(Point point) { int colCnt = fTableViewer.getTable().getColumnCount(); TableItem item = null; for (int i = 0; i < fTableViewer.getTable().getItemCount(); i++) { item = fTableViewer.getTable().getItem(i); if (item.getData() != null) { break; } } if (item != null) { for (int i = 0; i < colCnt; i++) { Point start = new Point(item.getBounds(i).x, item.getBounds(i).y); start = fTableViewer.getTable().toDisplay(start); Point end = new Point(start.x + item.getBounds(i).width, start.y + item.getBounds(i).height); if (start.x < point.x && end.x > point.x) { return i; } } } return -1; } /** * Called when the tool tip is about to show in this rendering. Clients who * overrides <code>createTooltipControl</code> may need to also override * this method to ensure that the tooltip shows up properly in their * customized control. * <p> * By default a text tooltip is displayed, and the contents for the tooltip * are generated by the <code>getToolTipText(...)</code> method. * </p> * * @param toolTipControl - the control for displaying the tooltip * @param item - the table item where the mouse is pointing. * @param col - the column at which the mouse is pointing. * @since 3.2 */ protected void toolTipAboutToShow(Control toolTipControl, TableItem item, int col) { if (toolTipControl instanceof Label) { Object address = fTableViewer.getKey(fTableViewer.getTable().indexOf(item), col); if (address != null && address instanceof BigInteger) { Object data = item.getData(); if (data instanceof MemorySegment) { MemorySegment line = (MemorySegment) data; if (col > 0) { int start = (col - 1) * getBytesPerColumn(); int end = start + getBytesPerColumn(); MemoryByte[] bytes = line.getBytes(start, end); String str = getToolTipText((BigInteger) address, bytes); if (str != null) { ((Label) toolTipControl).setText(str); } } else { String str = getToolTipText((BigInteger) address, new MemoryByte[] {}); if (str != null) { ((Label) toolTipControl).setText(str); } } } } } } /** * Returns the text to display in a tool tip at the specified address for * the specified bytes. By default the address of the bytes is displayed. * Subclasses may override. * * @param address address of cell that tool tip is displayed for * @param bytes the bytes in the cell * @return the tooltip text for the memory bytes located at the specified * address * @since 3.2 */ protected String getToolTipText(BigInteger address, MemoryByte[] bytes) { StringBuffer buf = new StringBuffer("0x"); //$NON-NLS-1$ buf.append(address.toString(16).toUpperCase()); return buf.toString(); } private void setColumnHeadings() { String[] columnLabels = new String[0]; IMemoryBlockTablePresentation presentation = getTablePresentationAdapter(); if (presentation != null) { columnLabels = presentation.getColumnLabels(getMemoryBlock(), getBytesPerLine(), getBytesPerLine() / getBytesPerColumn()); } // check that column labels returned are not null if (columnLabels == null) { columnLabels = new String[0]; } int numByteColumns = fBytePerLine / fColumnSize; TableColumn[] columns = fTableViewer.getTable().getColumns(); int j = 0; for (int i = 1; i < columns.length - 1; i++) { // if the number of column labels returned is correct // use supplied column labels if (columnLabels.length == numByteColumns) { columns[i].setText(columnLabels[j]); j++; } else { // otherwise, use default int addressableUnit = getAddressableUnitPerColumn(); if (addressableUnit >= 4) { columns[i].setText(Integer.toHexString(j * addressableUnit).toUpperCase() + " - " + Integer.toHexString(j * addressableUnit + addressableUnit - 1).toUpperCase()); //$NON-NLS-1$ } else { columns[i].setText(Integer.toHexString(j * addressableUnit).toUpperCase()); } j++; } } } /** * * Return this rendering's viewer * * @return this rendering's viewer */ public StructuredViewer getViewer() { return fTableViewer; } private boolean isMemoryBlockBaseAddressChanged() { try { BigInteger address = getMemoryBlockBaseAddress(); BigInteger oldBaseAddress = fContentDescriptor.getContentBaseAddress(); if (!oldBaseAddress.equals(address)) { return true; } } catch (DebugException e) { // fail silently } return false; } /** * @param topVisibleAddress the address to get the description for */ private void createContentDescriptor(final BigInteger topVisibleAddress) { fContentDescriptor = new TableRenderingContentDescriptor(AbstractAsyncTableRendering.this); fContentDescriptor.setPostBuffer(getPostBufferSize()); fContentDescriptor.setPreBuffer(getPreBufferSize()); fContentDescriptor.setLoadAddress(topVisibleAddress); try { fContentDescriptor.updateContentBaseAddress(); } catch (DebugException e) { fError = true; showMessage(e.getMessage()); } fContentDescriptor.setAddressableSize(getAddressableSize()); try { int addressSize = 4; if (getMemoryBlock() instanceof IMemoryBlockExtension) { IMemoryBlockExtension extMb = (IMemoryBlockExtension) getMemoryBlock(); addressSize = extMb.getAddressSize(); if (addressSize <= 0) { DebugUIPlugin.logErrorMessage("Invalid address Size: " + addressSize); //$NON-NLS-1$ addressSize = 4; } fContentDescriptor.setAddressSize(addressSize); } fContentDescriptor.setAddressSize(addressSize); } catch (DebugException e) { fError = true; showMessage(e.getMessage()); } finally { if (fContentDescriptor.getAddressSize() <= 0) { fContentDescriptor.setAddressSize(4); } } } /** * Return the number of lines to be bufferred before the top visible line of * the memory rendering * * @return number of lines to be buffered before the top visible line in the * memory rendering */ private int getPreBufferSize() { if (fPreBufferSize < 0) { getPreBufferSizeFromPreference(); } return fPreBufferSize; } /** * Returns the number of lines to be bufferred after the last visible line * in the memory rendering * * @return the number of lines to be bufferred after the last visible line * in the memory rendering */ private int getPostBufferSize() { if (fPostBufferSize < 0) { getPostBufferSizeFromPreference(); } return fPostBufferSize; } private TableRenderingContentDescriptor getContentDescriptor() { return fContentDescriptor; } private void createGoToAddressComposite(Composite parent) { fGoToAddressComposite = new GoToAddressComposite(); fGoToAddressComposite.createControl(parent); Button button = fGoToAddressComposite.getButton(IDialogConstants.OK_ID); if (button != null) { button.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { doGoToAddress(); } }); button = fGoToAddressComposite.getButton(IDialogConstants.CANCEL_ID); if (button != null) { button.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { hideGotoAddressComposite(); } }); } } fGoToAddressComposite.getExpressionWidget().addSelectionListener(new SelectionAdapter() { @Override public void widgetDefaultSelected(SelectionEvent e) { doGoToAddress(); } }); fGoToAddressComposite.getExpressionWidget().addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (e.keyCode == SWT.ESC) { hideGotoAddressComposite(); } super.keyPressed(e); } }); } private void showGoToAddressComposite() { String selectedStr = getSelectedAsString(); Text text = fGoToAddressComposite.getExpressionWidget(); text.setText(selectedStr); text.setSelection(0, text.getCharCount()); double height = fGoToAddressComposite.getHeight(); double canvasHeight = fSashForm.getParent().getClientArea().height; double tableHeight = canvasHeight - height; double tableWeight = (tableHeight / canvasHeight) * 100; double textWeight = (height / canvasHeight) * 100; fSashForm.setWeights(new int[] { (int) tableWeight, (int) textWeight }); fSashForm.setMaximizedControl(null); fGoToAddressComposite.getExpressionWidget().setFocus(); } private void hideGotoAddressComposite() { fSashForm.setMaximizedControl(fTableViewer.getControl()); if (isActivated()) { fTableViewer.getControl().setFocus(); } } /** * */ private void doGoToAddress() { try { BigInteger address = fGoToAddressComposite.getGoToAddress(fContentDescriptor.getContentBaseAddress(), getSelectedAddress()); fGoToAddressAction.doGoToAddress(address.toString(16)); hideGotoAddressComposite(); } catch (DebugException e1) { MemoryViewUtil.openError(DebugUIMessages.GoToAddressAction_Go_to_address_failed, DebugUIMessages.GoToAddressAction_Go_to_address_failed, e1); } catch (NumberFormatException e1) { MemoryViewUtil.openError(DebugUIMessages.GoToAddressAction_Go_to_address_failed, DebugUIMessages.GoToAddressAction_Address_is_invalid, e1); } } @Override public void activated() { super.activated(); fActivated = true; IWorkbench workbench = PlatformUI.getWorkbench(); ICommandService commandSupport = workbench.getAdapter(ICommandService.class); IContextService contextSupport = workbench.getAdapter(IContextService.class); if (commandSupport != null && contextSupport != null) { fContext.add(contextSupport.activateContext(ID_ASYNC_TABLE_RENDERING_CONTEXT)); Command gotoCommand = commandSupport.getCommand(ID_GO_TO_ADDRESS_COMMAND); if (fGoToAddressHandler == null) { fGoToAddressHandler = new AbstractHandler() { @Override public Object execute(ExecutionEvent event) throws ExecutionException { if (fSashForm.getMaximizedControl() != null) { fGoToAddressAction.run(); } else { hideGotoAddressComposite(); } return null; } }; } gotoCommand.setHandler(fGoToAddressHandler); // The page up and page down actions can only be activated if the // rendering // is in manual scrolling mode. We are unable to determine the // scrolling mode // until the content descriptor is created. When the rendering is // activated, the content // descriptor may not be created yet. In that case, we cannot // activate the shortcuts here. // We will activate the shortcut after the rendering is created. if (fContentDescriptor != null && !isDynamicLoad()) { activatePageActions(); } } } private void activatePageActions() { IWorkbench workbench = PlatformUI.getWorkbench(); ICommandService commandSupport = workbench.getAdapter(ICommandService.class); if (commandSupport != null) { Command nextPage = commandSupport.getCommand(ID_NEXT_PAGE_COMMAND); if (fNextPageHandler == null) { fNextPageHandler = new AbstractHandler() { @Override public Object execute(ExecutionEvent arg0) throws ExecutionException { fNextAction.run(); return null; } }; } nextPage.setHandler(fNextPageHandler); Command prevPage = commandSupport.getCommand(ID_PREV_PAGE_COMMAND); if (fPrevPageHandler == null) { fPrevPageHandler = new AbstractHandler() { @Override public Object execute(ExecutionEvent arg0) throws ExecutionException { fPrevAction.run(); return null; } }; } prevPage.setHandler(fPrevPageHandler); } } @Override public void deactivated() { fActivated = false; IWorkbench workbench = PlatformUI.getWorkbench(); ICommandService commandSupport = workbench.getAdapter(ICommandService.class); IContextService contextSupport = workbench.getAdapter(IContextService.class); if (commandSupport != null && contextSupport != null) { // remove handler Command command = commandSupport.getCommand(ID_GO_TO_ADDRESS_COMMAND); command.setHandler(null); command = commandSupport.getCommand(ID_NEXT_PAGE_COMMAND); command.setHandler(null); command = commandSupport.getCommand(ID_PREV_PAGE_COMMAND); command.setHandler(null); if (fContext != null) { contextSupport.deactivateContexts(fContext); } } super.deactivated(); } private boolean isActivated() { return fActivated; } /** * Returns text for the given memory bytes at the specified address for the * specified rendering type. This is called by the label provider for. * Subclasses must override. * * @param renderingTypeId rendering type identifier * @param address address where the bytes belong to * @param data the bytes * @return a string to represent the memory. Cannot not return * <code>null</code>. Returns a string to pad the cell if the memory * cannot be converted successfully. */ @Override abstract public String getString(String renderingTypeId, BigInteger address, MemoryByte[] data); /** * Returns bytes for the given text corresponding to bytes at the given * address for the specified rendering type. This is called by the cell * modifier when modifying bytes in a memory block. Subclasses must convert * the string value to an array of bytes. The bytes will be passed to the * debug adapter for memory block modification. Returns <code>null</code> if * the bytes cannot be formatted properly. * * @param renderingTypeId rendering type identifier * @param address address the bytes begin at * @param currentValues current values of the data in bytes format * @param newValue the string to be converted to bytes * @return the bytes converted from a string */ @Override abstract public byte[] getBytes(String renderingTypeId, BigInteger address, MemoryByte[] currentValues, String newValue); }