/******************************************************************************* * Copyright (c) 2004, 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 *******************************************************************************/ package org.eclipse.debug.ui.memory; import java.math.BigInteger; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; 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.IMemoryBlockConnection; import org.eclipse.debug.internal.ui.memory.IPersistableDebugElement; import org.eclipse.debug.internal.ui.preferences.IDebugPreferenceConstants; 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.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.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.TableRenderingCellModifier; import org.eclipse.debug.internal.ui.views.memory.renderings.TableRenderingContentInput; import org.eclipse.debug.internal.ui.views.memory.renderings.TableRenderingContentProvider; import org.eclipse.debug.internal.ui.views.memory.renderings.TableRenderingLabelProvider; import org.eclipse.debug.internal.ui.views.memory.renderings.TableRenderingLabelProviderEx; 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.jface.action.Action; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.Separator; 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.TableViewer; import org.eclipse.jface.viewers.TextCellEditor; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.custom.TableCursor; import org.eclipse.swt.custom.TableEditor; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.FocusAdapter; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.MouseAdapter; 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.events.TraverseEvent; import org.eclipse.swt.events.TraverseListener; 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.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.ScrollBar; 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.IWorkbenchActionConstants; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.dialogs.PropertyDialogAction; import org.eclipse.ui.model.IWorkbenchAdapter; import org.eclipse.ui.part.PageBook; /** * 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.1 */ public abstract class AbstractTableRendering 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 = "selectedAddress"; //$NON-NLS-1$ /** * 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 = "columnSize"; //$NON-NLS-1$ /** * 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 = "topAddress"; //$NON-NLS-1$ /** * Property identifier for the row size in a table rendering * This property is used for synchronization between renderings. * @since 3.2 */ public static final String PROPERTY_ROW_SIZE = "rowSize"; //$NON-NLS-1$ private static final int BUFFER_THRESHOLD = 1; // threshold value private static final int BUFFER_START = 0; // flag to indicate asking for threshold at buffer start private static final int BUFFER_END = 1; // flat to indicate asking for threshold at buffer end private PageBook fPageBook; private TableViewer fTableViewer; private TextViewer fTextViewer; private int fBytePerLine; // number of bytes per line: 16 private int fColumnSize; // number of bytes per column: 1,2,4,8 private int fAddressableSize; private boolean fIsShowingErrorPage; private TableRenderingContentProvider fContentProvider; private BigInteger fSelectedAddress; private TableRenderingContentInput fContentInput; private TableRenderingCellModifier fCellModifier; private boolean fIsCreated; private CellEditor[] fEditors; private String fLabel; private TableCursor fTableCursor; private boolean fIsDisposed; private TraverseListener fCursorTraverseListener; private KeyAdapter fCursorKeyAdapter; private BigInteger fTopRowAddress; private CopyTableRenderingToClipboardAction fCopyToClipboardAction; private GoToAddressAction fGoToAddressAction; private ResetToBaseAddressAction fResetMemoryBlockAction; private PrintTableRenderingAction fPrintViewTabAction; private ReformatAction fReformatAction; private ToggleAddressColumnAction fToggleAddressColumnAction; private EventHandleLock fEvtHandleLock = new EventHandleLock(); private TableEditor fCursorEditor; private FocusAdapter fEditorFocusListener; private MouseAdapter fCursorMouseListener; private KeyAdapter fEditorKeyListener; private SelectionAdapter fCursorSelectionListener; private IWorkbenchAdapter fWorkbenchAdapter; private IMemoryBlockConnection fConnection; private boolean fIsShowAddressColumn = true; private SelectionAdapter fScrollbarSelectionListener; private PropertyDialogAction fPropertiesAction; private int fPageSize; private NextPageAction fNextAction; private PrevPageAction fPrevAction; private Shell fToolTipShell; private FormatTableRenderingAction fFormatRenderingAction; private IMenuListener fMenuListener; private int fPreBuffer; private int fPostBuffer; private class EventHandleLock { Object fOwner; public boolean acquireLock(Object client) { if (fOwner == null) { fOwner = client; return true; } return false; } public boolean releaseLock(Object client) { if (fOwner == client) { fOwner = null; return true; } return false; } } 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; resizeColumnsToPreferredSize(); 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 = fContentInput.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 = fContentInput.getLoadAddress(); address = address.subtract(BigInteger.valueOf(getPageSizeInUnits())); handlePageStartAddressChanged(address); } } /** * Constructs a new table rendering of the specified type. * * @param renderingId memory rendering type identifier */ public AbstractTableRendering(String renderingId) { super(renderingId); } /* (non-Javadoc) * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent) */ @Override public void propertyChange(PropertyChangeEvent event) { // 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; } if (event.getProperty().equals(IDebugUIConstants.PREF_PADDED_STR) || event.getProperty().equals(IDebugUIConstants.PREF_MEMORY_HISTORY_KNOWN_COLOR) || event.getProperty().equals(IDebugUIConstants.PREF_MEMORY_HISTORY_UNKNOWN_COLOR)) { if (!fIsDisposed) { fTableViewer.refresh(); fTableCursor.redraw(); } return; } Object evtSrc = event.getSource(); if (event.getProperty().equals(IDebugPreferenceConstants.PREF_TABLE_RENDERING_PAGE_SIZE) || event.getProperty().equals(IDebugPreferenceConstants.PREF_TABLE_RENDERING_PRE_BUFFER_SIZE) || event.getProperty().equals(IDebugPreferenceConstants.PREF_TABLE_RENDERING_POST_BUFFER_SIZE)) { // always update page size, only refresh if the table is visible getPageSizeFromPreference(); } // do not handle event if the rendering is displaying an error if (isDisplayingError()) { return; } // do not handle property change event if the rendering is not visible if (!isVisible()) { return; } if (event.getProperty().equals(IDebugPreferenceConstants.PREF_DYNAMIC_LOAD_MEM)) { handleDyanicLoadChanged(); return; } if (event.getProperty().equals(IDebugPreferenceConstants.PREF_TABLE_RENDERING_PAGE_SIZE)) { if (!isDynamicLoad()) { // only refresh if in non-autoload mode refresh(); } return; } if (event.getProperty().equals(IDebugPreferenceConstants.PREF_TABLE_RENDERING_PRE_BUFFER_SIZE) || event.getProperty().equals(IDebugPreferenceConstants.PREF_TABLE_RENDERING_POST_BUFFER_SIZE)) { if (isDynamicLoad()) { // only refresh if in non-autoload mode refresh(); } return; } if (evtSrc == this) { return; } if (!(evtSrc instanceof IMemoryRendering)) { return; } 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(AbstractTableRendering.PROPERTY_SELECTED_ADDRESS) && value instanceof BigInteger) { selectedAddressChanged((BigInteger)value); } else if (propertyName.equals(AbstractTableRendering.PROPERTY_COL_SIZE) && value instanceof Integer) { columnSizeChanged(((Integer)value).intValue()); } else if (propertyName.equals(AbstractTableRendering.PROPERTY_ROW_SIZE) && value instanceof Integer) { rowSizeChanged(((Integer)value).intValue()); } else if (propertyName.equals(AbstractTableRendering.PROPERTY_TOP_ADDRESS) && value instanceof BigInteger) { if (needMoreLines()) { if (isDynamicLoad()) { reloadTable(getTopVisibleAddress(), false); } } topVisibleAddressChanged((BigInteger)value, false); } else if (propertyName.equals(IInternalDebugUIConstants.PROPERTY_PAGE_START_ADDRESS) && value instanceof BigInteger) { handlePageStartAddressChanged((BigInteger)value); } } private void handleDyanicLoadChanged() { // if currently in dynamic load mode, update page // start address updateSyncPageStartAddress(); updateDynamicLoadProperty(); if (isDynamicLoad()) { refresh(); } else { BigInteger pageStart = (BigInteger)getSynchronizedProperty(IInternalDebugUIConstants.PROPERTY_PAGE_START_ADDRESS); if (pageStart == null) { pageStart = fTopRowAddress; } handlePageStartAddressChanged(pageStart); } } private void updateDynamicLoadProperty() { boolean value = DebugUIPlugin .getDefault() .getPreferenceStore() .getBoolean(IDebugPreferenceConstants.PREF_DYNAMIC_LOAD_MEM); if (value != isDynamicLoad()) { fContentProvider.setDynamicLoad(value); if (!fIsDisposed) { if (isDynamicLoad()) { fContentInput.setPostBuffer(20); fContentInput.setPreBuffer(20); fContentInput.setNumLines(getNumberOfVisibleLines()); } else { fContentInput.setPostBuffer(0); fContentInput.setPreBuffer(0); fContentInput.setNumLines(fPageSize); } } } } /** * Handle top visible address change event from synchronizer * @param address the address * @param force if the notification should be forced */ private void topVisibleAddressChanged(final BigInteger address, boolean force) { // do not handle event if rendering is not visible // continue to handle event if caller decides to force the rendering // to move to the top visible address even when the rendering // is not visible if (!isVisible() && !force) { 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 (isBaseAddressChanged()) { return; } if (!address.equals(fTopRowAddress)) { fTopRowAddress = address; updateSyncTopAddress(); if (getMemoryBlock() instanceof IMemoryBlockExtension) { handleTopAddressChangedforExtended(address); } else { handleTopAddressChangedForSimple(address); } } } /** * @param address the address */ private void handleTopAddressChangedForSimple(final BigInteger address) { // IMemoryBlock support int index = findAddressIndex(address); Table table = fTableViewer.getTable(); if (index >= 0) { setTopIndex(table, index); } if (isAddressVisible(fSelectedAddress)) { fTableCursor.setVisible(true); } else { fTableCursor.setVisible(false); } } /** * @param address the address */ private void handleTopAddressChangedforExtended(final BigInteger address) { Object evtLockClient = new Object(); try { if (!fEvtHandleLock.acquireLock(evtLockClient)) { return; } if (!isAddressOutOfRange(address)) { Table table = fTableViewer.getTable(); int index = findAddressIndex(address); int startThreshold = getBufferThreshold(BUFFER_START); int endThrreshold = getBufferThreshold(BUFFER_END); if (index >= startThreshold && table.getItemCount() - (index+getNumberOfVisibleLines()) >= endThrreshold) { // update cursor position setTopIndex(table, index); } else { int numInBuffer = table.getItemCount(); if (index < getBufferThreshold(BUFFER_START)) { if(isAtTopLimit()) { setTopIndex(table, index); } else { if (isDynamicLoad() && getBufferThreshold(BUFFER_START) > 0) { reloadTable(address, false); } else { setTopIndex(table, index); } } } else if ((numInBuffer-(index+getNumberOfVisibleLines())) <= getBufferThreshold(BUFFER_END)) { if (!isAtBottomLimit() && isDynamicLoad() && getBufferThreshold(BUFFER_END) > 0) { reloadTable(address, false); } else { setTopIndex(table, index); } } } } else { // approaching limit, reload table reloadTable(address, false); } if (isAddressVisible(fSelectedAddress)) { fTableCursor.setVisible(true); } else { fTableCursor.setVisible(false); } } finally { fEvtHandleLock.releaseLock(evtLockClient); } } /** * @param value the new value */ private void selectedAddressChanged(BigInteger value) { // do not handle event if the base address of the memory // block has changed, wait for debug event to update to // new location if (isBaseAddressChanged()) { return; } try { // do not handle event if the event is out of range and the // rendering is in non-dynamic-load mode, otherwise, will // cause rendering to continue to scroll when it shouldn't if (isDynamicLoad()) { goToAddress(value); } else if (!isAddressOutOfRange(value)) { goToAddress(value); } } catch (DebugException e) { // do nothing } } private void handlePageStartAddressChanged(BigInteger address) { // do not handle if in dynamic mode if (isDynamicLoad()) { return; } if (fContentInput == null) { 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 (isBaseAddressChanged()) { return; } if(fContentProvider.getBufferTopAddress().equals(address)) { return; } BigInteger start = fContentInput.getStartAddress(); BigInteger end = fContentInput.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())); } fContentInput.setLoadAddress(address); refresh(); updateSyncPageStartAddress(); setTopIndex(fTableViewer.getTable(), 0); fTopRowAddress = address; updateSyncTopAddress(); BigInteger selectedAddress = (BigInteger)getSynchronizedProperty(AbstractTableRendering.PROPERTY_SELECTED_ADDRESS); if (selectedAddress != null) { fSelectedAddress = selectedAddress; if (!isAddressOutOfRange(fSelectedAddress)) { setCursorAtAddress(fSelectedAddress); fTableCursor.setVisible(true); } else { fTableCursor.setVisible(false); } } } /* (non-Javadoc) * @see org.eclipse.debug.ui.memory.IMemoryRendering#createControl(org.eclipse.swt.widgets.Composite) */ @Override public Control createControl(Composite parent) { fPageBook = new PageBook(parent, SWT.NONE); createErrorPage(fPageBook); createTableViewer(fPageBook); fTableViewer.getTable().redraw(); createToolTip(); return fPageBook; } /** * Create the table viewer and other support controls * for this rendering. * * @param parent parent composite */ private void createTableViewer(Composite parent) { fTableViewer= new TableViewer(parent, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.HIDE_SELECTION | SWT.BORDER); TableRenderingLabelProvider labelProvider; if (hasCustomizedDecorations()) { labelProvider = new TableRenderingLabelProviderEx(this); } else { labelProvider = new TableRenderingLabelProvider(this); } fTableViewer.setLabelProvider(labelProvider); fContentProvider = new TableRenderingContentProvider(); fContentProvider.setDynamicLoad(DebugUIPlugin.getDefault().getPreferenceStore().getBoolean(IDebugPreferenceConstants.PREF_DYNAMIC_LOAD_MEM)); fTableViewer.setContentProvider(fContentProvider); fContentProvider.setViewer(fTableViewer); ScrollBar scroll = ((Table)fTableViewer.getControl()).getVerticalBar(); scroll.setMinimum(-100); scroll.setMaximum(200); fTableViewer.getTable().setHeaderVisible(true); fTableViewer.getTable().setLinesVisible(true); // set up addressable size and figure out number of bytes required per line fAddressableSize = -1; try { if (getMemoryBlock() instanceof IMemoryBlockExtension) { fAddressableSize = ((IMemoryBlockExtension)getMemoryBlock()).getAddressableSize(); } } catch (DebugException e1) { // log error and default to 1 fAddressableSize = 1; displayError(e1); return; } if (getAddressableSize() < 1) { fAddressableSize = 1; } // set up initial format setupInitialFormat(); // set up selected address setupSelectedAddress(); // figure out top visible address BigInteger topVisibleAddress = getInitialTopVisibleAddress(); getPageSizeFromPreference(); if (isDynamicLoad()) { int numLines = getNumberOfVisibleLines(); if (numLines <= 0) { // add listener to reload when we know the number of lines to load fTableViewer.getTable().addPaintListener(new PaintListener() { @Override public void paintControl(PaintEvent e) { fTableViewer.getTable().removePaintListener(this); fContentInput.setNumLines(getNumberOfVisibleLines()); reloadTable(fContentInput.getLoadAddress(), false); resizeColumnsToPreferredSize(); setCursorAtAddress(fSelectedAddress); fTableCursor.setVisible(true); }}); } fContentInput = new TableRenderingContentInput(this, fPreBuffer, fPostBuffer, topVisibleAddress, numLines, false, null); } else { BigInteger addressToLoad = topVisibleAddress; // check synchronization service to see if we need to sync with another rendering Object obj = getSynchronizedProperty(IInternalDebugUIConstants.PROPERTY_PAGE_START_ADDRESS); if (obj != null && obj instanceof BigInteger) { addressToLoad = (BigInteger)obj; } fContentInput = new TableRenderingContentInput(this, 0, 0, addressToLoad, fPageSize, false, null); } fTableViewer.setInput(fContentInput); // set up cell modifier fCellModifier = new TableRenderingCellModifier(this); fTableViewer.setCellModifier(fCellModifier); // SET UP FONT // set to a non-proportional font fTableViewer.getTable().setFont(JFaceResources.getFont(IInternalDebugUIConstants.FONT_NAME)); if (!(getMemoryBlock() instanceof IMemoryBlockExtension)) { // If not extended memory block, do not create any buffer // no scrolling fContentInput.setPreBuffer(0); fContentInput.setPostBuffer(0); } // set up table cursor createCursor(fTableViewer.getTable(), fSelectedAddress); fTableViewer.getTable().addMouseListener(new MouseAdapter() { @Override public void mouseDown(MouseEvent e) { handleTableMouseEvent(e); }}); // create pop up menu for the rendering createActions(); createPopupMenu(fTableViewer.getControl()); createPopupMenu(fTableCursor); fMenuListener = new IMenuListener() { @Override public void menuAboutToShow(IMenuManager manager) { fillContextMenu(manager); manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); }}; getPopupMenuManager().addMenuListener(fMenuListener); // now the rendering is successfully created fIsCreated = true; //synchronize addRenderingToSyncService(); synchronize(); fTopRowAddress = getTopVisibleAddress(); // Need to resize column after content is filled in // Pack function does not work unless content is not filled in // since the table is not able to compute the preferred size. resizeColumnsToPreferredSize(); try { if (getMemoryBlock() instanceof IMemoryBlockExtension) { if(((IMemoryBlockExtension)getMemoryBlock()).getBigBaseAddress() == null) { DebugException e = new DebugException(DebugUIPlugin.newErrorStatus(DebugUIMessages.AbstractTableRendering_1, null)); displayError(e); } } } catch (DebugException e1) { displayError(e1); } // add font change listener and update font when the font has been changed JFaceResources.getFontRegistry().addListener(this); fScrollbarSelectionListener = new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent event) { handleScrollBarSelection(); }}; scroll.addSelectionListener(fScrollbarSelectionListener); DebugUIPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(this); } private boolean validateInitialFormat() { int rowSize = getDefaultRowSize(); int columnSize = getDefaultColumnSize(); if (rowSize < columnSize || rowSize % columnSize != 0 || rowSize == 0 || columnSize == 0) { return false; } return true; } private BigInteger getInitialTopVisibleAddress() { BigInteger topVisibleAddress = (BigInteger) getSynchronizedProperty(AbstractTableRendering.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 setupSelectedAddress() { // figure out selected address BigInteger selectedAddress = (BigInteger) getSynchronizedProperty(AbstractTableRendering.PROPERTY_SELECTED_ADDRESS); if (selectedAddress == null) { if (getMemoryBlock() instanceof IMemoryBlockExtension) { try { selectedAddress = ((IMemoryBlockExtension) getMemoryBlock()) .getBigBaseAddress(); } catch (DebugException e1) { selectedAddress = new BigInteger("0"); //$NON-NLS-1$ } if (selectedAddress == null) { selectedAddress = new BigInteger("0"); //$NON-NLS-1$ } } else { long address = getMemoryBlock().getStartAddress(); selectedAddress = BigInteger.valueOf(address); } } setSelectedAddress(selectedAddress); } 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 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 column size Integer colSize = (Integer)getSynchronizedProperty(AbstractTableRendering.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(AbstractTableRendering.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; } 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; } private void getPageSizeFromPreference() { fPageSize = DebugUIPlugin.getDefault().getPreferenceStore().getInt(IDebugPreferenceConstants.PREF_TABLE_RENDERING_PAGE_SIZE); fPreBuffer = DebugUIPlugin.getDefault().getPreferenceStore().getInt(IDebugPreferenceConstants.PREF_TABLE_RENDERING_PRE_BUFFER_SIZE); fPostBuffer = DebugUIPlugin.getDefault().getPreferenceStore().getInt(IDebugPreferenceConstants.PREF_TABLE_RENDERING_POST_BUFFER_SIZE); } private void createCursor(Table table, BigInteger address) { fTableCursor = new TableCursor(table, SWT.NONE); Display display = fTableCursor.getDisplay(); // set up cursor color fTableCursor.setBackground(display.getSystemColor(SWT.COLOR_LIST_SELECTION)); fTableCursor.setForeground(display.getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT)); fTableCursor.setFont(JFaceResources.getFont(IInternalDebugUIConstants.FONT_NAME)); fTableCursor.setVisible(true); fCursorKeyAdapter = new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { handleCursorKeyPressed(e); } }; fTableCursor.addKeyListener(fCursorKeyAdapter); fCursorTraverseListener = new TraverseListener() { @Override public void keyTraversed(TraverseEvent e) { handleCursorTraverseEvt(e); }}; fTableCursor.addTraverseListener(fCursorTraverseListener); fCursorMouseListener = new MouseAdapter() { @Override public void mouseDown(MouseEvent e) { handleCursorMouseEvent(e); }}; fTableCursor.addMouseListener(fCursorMouseListener); // cursor may be disposed before disposed is called // remove listeners whenever the cursor is disposed fTableCursor.addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(DisposeEvent e) { if (fTableCursor == null) { return; } fTableCursor.removeTraverseListener(fCursorTraverseListener); fTableCursor.removeKeyListener(fCursorKeyAdapter); fTableCursor.removeMouseListener(fCursorMouseListener); fTableCursor.removeSelectionListener(fCursorSelectionListener); }}); fCursorSelectionListener = new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if (!fEvtHandleLock.acquireLock(this)) { return; } handleCursorMoved(); fEvtHandleLock.releaseLock(this); } }; fTableCursor.addSelectionListener(fCursorSelectionListener); setCursorAtAddress(address); fCursorEditor = new TableEditor (fTableViewer.getTable()); } private void handleCursorTraverseEvt(TraverseEvent e){ if (fTableCursor.getRow() == null) { return; } Table table = (Table)fTableCursor.getParent(); int row = table.indexOf(fTableCursor.getRow()); int col = fTableCursor.getColumn(); if (col == getNumCol() && e.keyCode == SWT.ARROW_RIGHT) { if (row + 1>= table.getItemCount()) { return; } row = row +1; col = 0; fTableCursor.setSelection(row, col); } if (col <= 1 && e.keyCode == SWT.ARROW_LEFT) { if (row-1 < 0) { return; } row = row - 1; col = getNumCol()+1; fTableCursor.setSelection(row, col); } Object evtLockClient = new Object(); if (!fEvtHandleLock.acquireLock(evtLockClient)) { return; } handleCursorMoved(); fEvtHandleLock.releaseLock(evtLockClient); } /** * Update selected address. * Load more memory if required. */ private void handleCursorMoved() { if (fIsDisposed) { return; } BigInteger selectedAddress = getSelectedAddressFromCursor(fTableCursor); // when the cursor is moved, the selected address is changed if (selectedAddress != null && !selectedAddress.equals(fSelectedAddress)) { setSelectedAddress(selectedAddress); updateSyncSelectedAddress(); } // now check to see if the cursor is approaching buffer limit TableItem item = fTableCursor.getRow(); if (item == null) { return; } if (getMemoryBlock() instanceof IMemoryBlockExtension) { int row = fTableViewer.getTable().indexOf(item); if (row < getBufferThreshold(BUFFER_START)) { if (!isAtTopLimit() && getBufferThreshold(BUFFER_START) > 0) { if (isDynamicLoad()) { refresh(); setCursorAtAddress(fSelectedAddress); } } } else if (row >= fTableViewer.getTable().getItemCount() - getBufferThreshold(BUFFER_END)) { if (!isAtBottomLimit() && getBufferThreshold(BUFFER_END) > 0) { if (isDynamicLoad()) { refresh(); setCursorAtAddress(fSelectedAddress); } } } } // if the cursor has moved, the top index of the table may change // just update the synchronization service BigInteger address = getTopVisibleAddress(); if (!address.equals(fTopRowAddress)) { fTopRowAddress = address; updateSyncTopAddress(); } } private void handleCursorKeyPressed(KeyEvent event) { // allow edit if user hits return if (event.character == '\r' && event.getSource() instanceof TableCursor) { activateCellEditor(null); return; } if (MemoryViewUtil.isValidEditEvent(event.keyCode)) { // activate edit as soon as user types something at the cursor if (event.getSource() instanceof TableCursor) { String initialValue = String.valueOf(event.character); activateCellEditor(initialValue); return; } } } /** * Calculate selected address based on cursor's current position * @param cursor the cursor * @return the selected address */ private BigInteger getSelectedAddressFromCursor(TableCursor cursor) { TableItem row = cursor.getRow(); int col = cursor.getColumn(); return getAddressFromTableItem(row, col); } private BigInteger getAddressFromTableItem(TableItem row, int col) { if (row == null) { return null; } // get row address String temp = ((TableRenderingLine)row.getData()).getAddress(); BigInteger rowAddress = new BigInteger(temp, 16); int offset; if (col > 0) { // get address offset int addressableUnit = getAddressableUnitPerColumn(); offset = (col-1) * addressableUnit; } else { offset = 0; } return rowAddress.add(BigInteger.valueOf(offset)); } /** * Sets the cursor at the specified address * @param address the address * @return true if successful, false otherwise */ private boolean setCursorAtAddress(BigInteger address) { if (fContentProvider.getBufferTopAddress() == null) { return false; } // selected address is out of range, simply return false if (address.compareTo(fContentProvider.getBufferTopAddress()) < 0) { return false; } // calculate selected row address int addressableUnit = getAddressableUnitPerLine(); int numOfRows = address.subtract(fContentProvider.getBufferTopAddress()).intValue()/addressableUnit; BigInteger rowAddress = fContentProvider.getBufferTopAddress().add(BigInteger.valueOf(numOfRows * addressableUnit)); // try to find the row of the selected address int row = findAddressIndex(address); if (row == -1) { return false; } // calculate offset to the row address BigInteger offset = address.subtract(rowAddress); // locate column int colAddressableUnit = getAddressableUnitPerColumn(); int col = ((offset.intValue()/colAddressableUnit)+1); if (col == 0) { col = 1; } fTableCursor.setSelection(row, col); return true; } /** * 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) { // selected address gets changed as the cursor is moved // during the reformat. // Back up the address and restore it later. BigInteger selectedAddress = fSelectedAddress; // 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; Object evtLockClient = new Object(); if (!fEvtHandleLock.acquireLock(evtLockClient)) { return false; } // if the tab is already created and is being reformatted if (fIsCreated) { if (fTableViewer == null) { return false; } if (fTableViewer.getTable() == null) { return false; } // clean up old columns TableColumn[] oldColumns = fTableViewer.getTable().getColumns(); for (int i=0; i<oldColumns.length; i++) { oldColumns[i].dispose(); } // clean up old cell editors CellEditor[] oldCellEditors = fTableViewer.getCellEditors(); for (int i=0; i<oldCellEditors.length; i++) { oldCellEditors[i].dispose(); } } TableColumn column0 = new TableColumn(fTableViewer.getTable(),SWT.LEFT,0); column0.setText(DebugUIMessages.AbstractTableRendering_2); // create new byte columns TableColumn [] byteColumns = new TableColumn[bytesPerLine/columnSize]; String[] columnLabels = new String[0]; IMemoryBlockTablePresentation presentation = getTablePresentationAdapter(); if (presentation != null) { columnLabels = presentation.getColumnLabels(getMemoryBlock(), bytesPerLine, getNumCol()); } // check that column labels are not null if (columnLabels == null) { columnLabels = new String[0]; } for (int i=0;i<byteColumns.length; i++) { TableColumn column = new TableColumn(fTableViewer.getTable(), SWT.LEFT, i+1); // if the number of column labels returned is correct // use supplied column labels if (columnLabels.length == byteColumns.length) { column.setText(columnLabels[i]); } else { // otherwise, use default int addressableUnit = columnSize/getAddressableSize(); if (getAddressableUnitPerColumn() >= 4) { column.setText(Integer.toHexString(i*addressableUnit).toUpperCase() + " - " + Integer.toHexString(i*addressableUnit+addressableUnit-1).toUpperCase()); //$NON-NLS-1$ } else { column.setText(Integer.toHexString(i*addressableUnit).toUpperCase()); } } } //Empty column for cursor navigation TableColumn emptyCol = new TableColumn(fTableViewer.getTable(),SWT.LEFT,byteColumns.length+1); emptyCol.setText(" "); //$NON-NLS-1$ emptyCol.setWidth(1); emptyCol.setResizable(false); // +2 to include properties for address and navigation column String[] columnProperties = new String[byteColumns.length+2]; columnProperties[0] = TableRenderingLine.P_ADDRESS; int addressableUnit = columnSize / getAddressableSize(); // 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$ fTableViewer.setColumnProperties(columnProperties); Table table = fTableViewer.getTable(); fEditors = new CellEditor[table.getColumnCount()]; for (int i=0; i<fEditors.length; i++) { fEditors[i] = new TextCellEditor(table); } // create and set cell editors fTableViewer.setCellEditors(fEditors); if (fIsCreated) { fTableViewer.refresh(); } resizeColumnsToPreferredSize(); updateSyncRowSize(); updateSyncColSize(); if (fIsCreated) { // for Linux GTK, this must happen after table viewer is refreshed int i = findAddressIndex(fTopRowAddress); if (i >= 0) { setTopIndex(fTableViewer.getTable(), i); } if (isAddressVisible(selectedAddress)) { // after refresh, make sure the cursor is at the correct position setCursorAtAddress(selectedAddress); } } fEvtHandleLock.releaseLock(evtLockClient); return true; } /** * Create the error page for this rendering. * The error page is used to report any error resulted from * getting memory from a memory block. * @param parent the parent composite */ private void createErrorPage(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); } } /** * Displays the content of the table viewer. */ public void displayTable() { fIsShowingErrorPage = false; fPageBook.showPage(fTableViewer.getControl()); } /** * Displays an error message for the given exception. * * @param e exception to display */ public void displayError(DebugException e) { StyledText styleText = null; fIsShowingErrorPage = true; styleText = fTextViewer.getTextWidget(); if (styleText != null) { styleText.setText(DebugUIMessages.AbstractTableRendering_3 + e.getMessage()); } fPageBook.showPage(fTextViewer.getControl()); // clear content cache if we need to display error fContentProvider.clearContentCache(); } /** * Returns whether the error page is displayed. * * @return whether the error page is displayed */ public boolean isDisplayingError() { return fIsShowingErrorPage; } /* (non-Javadoc) * @see org.eclipse.debug.ui.memory.IMemoryRendering#getControl() */ @Override public Control getControl() { return fPageBook; } /** * 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); } /** * 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; } int numberOfLines = doGetNumberOfVisibleLines(table, height); if (numberOfLines <= 0) { return 0; } return numberOfLines; } /** * @param table the table * @param height the current height of the table * @return the number of visible lines in the table */ private int doGetNumberOfVisibleLines(Table table, int height) { // 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; return numberOfLines; } private static void setTopIndex(Table table, int index) { table.setTopIndex(index); } private void addRenderingToSyncService() { IMemoryRenderingSynchronizationService syncService = getMemoryRenderingContainer().getMemoryRenderingSite().getSynchronizationService(); if (syncService == null) { return; } syncService.addPropertyChangeListener(this, null); // we could be in a format error even though the error is not yet displayed // do not update sync property in this case if (!isDisplayingError()) { if (syncService.getSynchronizationProvider() == null) { syncService.setSynchronizationProvider(this); } // check if there is already synchronization info available Object selectedAddress =getSynchronizedProperty( AbstractTableRendering.PROPERTY_SELECTED_ADDRESS); Object rowSize = getSynchronizedProperty(AbstractTableRendering.PROPERTY_ROW_SIZE); Object colSize =getSynchronizedProperty( AbstractTableRendering.PROPERTY_COL_SIZE); Object topAddress =getSynchronizedProperty( AbstractTableRendering.PROPERTY_TOP_ADDRESS); if (!isDynamicLoad()) { Object pageStartAddress = getSynchronizedProperty(IInternalDebugUIConstants.PROPERTY_PAGE_START_ADDRESS); if (pageStartAddress == null) { updateSyncPageStartAddress(); } } // if info is available, some other view tab has already been // created // do not overwrite info in the synchronizer if that's the case if (selectedAddress == null) { updateSyncSelectedAddress(); } if (rowSize == null) { updateSyncRowSize(); } if (colSize == null) { updateSyncColSize(); } if (topAddress == null) { updateSyncTopAddress(); } } } /** * Get properties from synchronizer and synchronize settings */ private void synchronize() { if (!isDynamicLoad()) { BigInteger pageStart = (BigInteger)getSynchronizedProperty(IInternalDebugUIConstants.PROPERTY_PAGE_START_ADDRESS); if (pageStart != null && fContentInput != null && fContentInput.getLoadAddress() != null) { if (!fContentInput.getLoadAddress().equals(pageStart)) { handlePageStartAddressChanged(pageStart); } } else if (pageStart != null) { handlePageStartAddressChanged(pageStart); } } Integer rowSize = (Integer) getSynchronizedProperty(AbstractTableRendering.PROPERTY_ROW_SIZE); Integer columnSize = (Integer) getSynchronizedProperty(AbstractTableRendering.PROPERTY_COL_SIZE); BigInteger selectedAddress = (BigInteger)getSynchronizedProperty(AbstractTableRendering.PROPERTY_SELECTED_ADDRESS); BigInteger topAddress = (BigInteger)getSynchronizedProperty(AbstractTableRendering.PROPERTY_TOP_ADDRESS); if (rowSize != null) { int rSize = rowSize.intValue(); if (rSize > 0 && rSize != fBytePerLine) { rowSizeChanged(rSize); } } if (columnSize != null) { int colSize = columnSize.intValue(); if (colSize > 0 && colSize != fColumnSize) { columnSizeChanged(colSize); } } if (topAddress != null) { if (!topAddress.equals(getTopVisibleAddress())) { if (selectedAddress != null) { if (!fSelectedAddress.equals(selectedAddress)) { selectedAddressChanged(selectedAddress); } } topVisibleAddressChanged(topAddress, false); } } if (selectedAddress != null) { if (selectedAddress.compareTo(fSelectedAddress) != 0) { selectedAddressChanged(selectedAddress); } } } /** * Resize column to the preferred size. */ @Override public void resizeColumnsToPreferredSize() { // pack columns Table table = fTableViewer.getTable(); TableColumn[] columns = table.getColumns(); for (int i=0 ;i<columns.length-1; i++) { columns[i].pack(); } if (!fIsShowAddressColumn) { columns[0].setWidth(0); } } /** * update selected address in synchronizer if update is true. */ private void updateSyncSelectedAddress() { if (!fIsCreated) { return; } PropertyChangeEvent event = new PropertyChangeEvent(this, AbstractTableRendering.PROPERTY_SELECTED_ADDRESS, null, fSelectedAddress); firePropertyChangedEvent(event); } /** * update column size in synchronizer */ private void updateSyncColSize() { if (!fIsCreated) { return; } PropertyChangeEvent event = new PropertyChangeEvent(this, AbstractTableRendering.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, AbstractTableRendering.PROPERTY_ROW_SIZE, null, Integer.valueOf(fBytePerLine)); firePropertyChangedEvent(event); } /** * update top visible address in synchronizer */ private void updateSyncTopAddress() { if (!fIsCreated) { return; } PropertyChangeEvent event = new PropertyChangeEvent(this, AbstractTableRendering.PROPERTY_TOP_ADDRESS, null, fTopRowAddress); firePropertyChangedEvent(event); } private void updateSyncPageStartAddress() { if (!fIsCreated) { return; } if (isBaseAddressChanged()) { return; } BigInteger pageStart; if (isDynamicLoad()) { // if dynamic loading, the page address should be the top // row address pageStart = fTopRowAddress; } else { // otherwise, the address is the buffer's start address pageStart = fContentProvider.getBufferTopAddress(); } PropertyChangeEvent event = new PropertyChangeEvent(this, IInternalDebugUIConstants.PROPERTY_PAGE_START_ADDRESS, null, pageStart); firePropertyChangedEvent(event); } /** * Fills the context menu for this rendering * * @param menu menu to fill */ protected void fillContextMenu(IMenuManager menu) { menu.add(new Separator("topMenu")); //$NON-NLS-1$ menu.add(fResetMemoryBlockAction); menu.add(fGoToAddressAction); 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()); menu.add(fReformatAction); menu.add(fToggleAddressColumnAction); menu.add(new Separator()); menu.add(fCopyToClipboardAction); menu.add(fPrintViewTabAction); if (fPropertiesAction != null) { menu.add(new Separator()); menu.add(fPropertiesAction); } } /** * 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(); } /** * 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; } /** * Updates labels of this rendering. */ @Override public void updateLabels() { // update tab labels updateRenderingLabel(true); if (fTableViewer != null) { // update column labels setColumnHeadings(); fTableViewer.refresh(); } } /* Returns the label of this rendering. * * @return label of this rendering */ @Override public String getLabel() { if (fLabel == null) { fLabel = buildLabel(true); } return fLabel; } /** * 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(boolean showAddress) { fLabel = buildLabel(showAddress); firePropertyChangedEvent(new PropertyChangeEvent(this, IBasicPropertyConstants.P_TEXT, null, fLabel)); } 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); } private void setColumnHeadings() { String[] columnLabels = new String[0]; IMemoryBlockTablePresentation presentation = getTablePresentationAdapter(); if (presentation != null) { columnLabels = presentation.getColumnLabels(getMemoryBlock(), fBytePerLine, getNumCol()); } // 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 if (fColumnSize >= 4) { columns[i].setText(Integer.toHexString(j*fColumnSize).toUpperCase() + " - " + Integer.toHexString(j*fColumnSize+fColumnSize-1).toUpperCase()); //$NON-NLS-1$ } else { columns[i].setText(Integer.toHexString(j*fColumnSize).toUpperCase()); } j++; } } } /** * Refresh the table viewer with the current top visible address. * Update labels in the memory rendering. */ @Override public void refresh() { // refresh at start address of this memory block // address may change if expression is evaluated to a different value IMemoryBlock mem = getMemoryBlock(); BigInteger address; if (mem instanceof IMemoryBlockExtension) { try { address = ((IMemoryBlockExtension)mem).getBigBaseAddress(); if (address == null) { DebugException e = new DebugException(DebugUIPlugin.newErrorStatus(DebugUIMessages.AbstractTableRendering_10, null)); displayError(e); return; } updateRenderingLabel(true); // base address has changed if (address.compareTo(fContentProvider.getContentBaseAddress()) != 0) { // get to new address setSelectedAddress(address); updateSyncSelectedAddress(); reloadTable(address, true); if (!isDynamicLoad()) { updateSyncPageStartAddress(); setTopIndex(fTableViewer.getTable(), 0); } fTopRowAddress = getTopVisibleAddress(); updateSyncTopAddress(); fContentInput.updateContentBaseAddress(); } else { // reload at top of table if (isDynamicLoad()) { address = getTopVisibleAddress(); } else { address = fContentInput.getLoadAddress(); } reloadTable(address, true); } } catch (DebugException e) { displayError(e); return; } } else { address = BigInteger.valueOf(mem.getStartAddress()); reloadTable(address, true); } } synchronized private void reloadTable(BigInteger topAddress, boolean updateDelta){ if (fTableViewer == null) { return; } try { Table table = (Table)fTableViewer.getControl(); TableRenderingContentInput input; if (isDynamicLoad()) { input = new TableRenderingContentInput(this, fPreBuffer, fPostBuffer, topAddress, getNumberOfVisibleLines(), updateDelta, null); } else { input = new TableRenderingContentInput(this, fContentInput.getPreBuffer(), fContentInput.getPostBuffer(), topAddress, fPageSize, updateDelta, null); } fContentInput = input; fTableViewer.setInput(fContentInput); if (isDynamicLoad()) { if (getMemoryBlock() instanceof IMemoryBlockExtension) { int topIdx = findAddressIndex(topAddress); if (topIdx != -1) { setTopIndex(table, topIdx); } } // cursor needs to be refreshed after reload if (isAddressVisible(fSelectedAddress)) { setCursorAtAddress(fSelectedAddress); } } else { if (!isAddressOutOfRange(fSelectedAddress)) { setCursorAtAddress(fSelectedAddress); fTableCursor.setVisible(true); } else { fTableCursor.setVisible(false); } } } finally { } } private BigInteger getTopVisibleAddress() { if (fTableViewer == null) { return BigInteger.valueOf(0); } Table table = fTableViewer.getTable(); int topIndex = getTopVisibleIndex(table); if (topIndex < 1) { topIndex = 0; } if (table.getItemCount() > topIndex) { TableRenderingLine topItem = (TableRenderingLine)table.getItem(topIndex).getData(); String calculatedAddress = null; if (topItem == null) { calculatedAddress = table.getItem(topIndex).getText(); } else { calculatedAddress = topItem.getAddress(); } BigInteger bigInt = new BigInteger(calculatedAddress, 16); return bigInt; } return BigInteger.valueOf(0); } private int findAddressIndex(BigInteger address) { TableItem items[] = fTableViewer.getTable().getItems(); for (int i=0; i<items.length; i++){ // Again, when the table resizes, the table may have a null item // at then end. This is to handle that. if (items[i] != null) { TableRenderingLine line = (TableRenderingLine)items[i].getData(); BigInteger lineAddress = new BigInteger(line.getAddress(), 16); int addressableUnit = getAddressableUnitPerLine(); BigInteger endLineAddress = lineAddress.add(BigInteger.valueOf(addressableUnit)); if (lineAddress.compareTo(address) <= 0 && endLineAddress.compareTo(address) > 0) { return i; } } } return -1; } private static int getTopVisibleIndex(Table table) { int index = table.getTopIndex(); TableItem item; try { item = table.getItem(index); } catch (IllegalArgumentException e) { return 0; } int cnt = table.getItemCount(); while (item.getBounds(0).y < 0) { index++; if (index >= cnt) { index--; break; } item = table.getItem(index); } return index; } /** * Returns this rendering's table viewer. * * @return the {@link TableViewer} */ public TableViewer getTableViewer() { return fTableViewer; } /* (non-Javadoc) * @see org.eclipse.debug.ui.memory.IMemoryRendering#dispose() */ @Override public void dispose() { try { // prevent rendering from being disposed again if (fIsDisposed) { return; } fIsDisposed = true; if (fContentProvider != null) { fContentProvider.dispose(); } ScrollBar scroll = ((Table)fTableViewer.getControl()).getVerticalBar(); if (scroll != null && !scroll.isDisposed()) { scroll.removeSelectionListener(fScrollbarSelectionListener); } if (!fTableCursor.isDisposed()) { fTableCursor.removeTraverseListener(fCursorTraverseListener); fTableCursor.removeKeyListener(fCursorKeyAdapter); fTableCursor.removeMouseListener(fCursorMouseListener); } fCursorEditor.dispose(); fTextViewer = null; fTableViewer = null; fTableCursor = null; // clean up cell editors for (int i=0; i<fEditors.length; i++) { fEditors[i].dispose(); } // remove font change listener when the view tab is disposed JFaceResources.getFontRegistry().removeListener(this); // remove the view tab from the synchronizer IMemoryRenderingSynchronizationService syncService = getMemoryRenderingContainer().getMemoryRenderingSite().getSynchronizationService(); if (syncService != null) { syncService.removePropertyChangeListener(this); } DebugUIPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(this); fToolTipShell.dispose(); if (getPopupMenuManager() != null) { getPopupMenuManager().removeMenuListener(fMenuListener); } super.dispose(); } catch (Exception e) {} } private int getNumCol() { int bytesPerLine = getBytesPerLine(); int columnSize = getBytesPerColumn(); return bytesPerLine/columnSize; } /* (non-Javadoc) * @see org.eclipse.debug.ui.IMemoryViewTab#setFont(org.eclipse.swt.graphics.Font) */ private void setFont(Font font) { int oldIdx = getTopVisibleIndex(fTableViewer.getTable()); // BUG in table, if font is changed when table is not starting // from the top, causes table grid-line to be misaligned. setTopIndex(fTableViewer.getTable(), 0); // set font fTableViewer.getTable().setFont(font); fTableCursor.setFont(font); setTopIndex(fTableViewer.getTable(), oldIdx); resizeColumnsToPreferredSize(); // update table cursor and force redraw setCursorAtAddress(fSelectedAddress); } /** * 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(BigInteger address) throws DebugException { Object evtLockClient = new Object(); try { if (!fEvtHandleLock.acquireLock(evtLockClient)) { return; } // if address is within the range, highlight if (!isAddressOutOfRange(address)) { setSelectedAddress(address); updateSyncSelectedAddress(); setCursorAtAddress(fSelectedAddress); // force the cursor to be shown if (!isAddressVisible(fSelectedAddress)) { int i = findAddressIndex(fSelectedAddress); fTableViewer.getTable().showItem(fTableViewer.getTable().getItem(i)); } } else { // 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 = fContentInput.getStartAddress(); BigInteger endAdd = fContentInput.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; } setSelectedAddress(address); updateSyncSelectedAddress(); reloadTable(address, false); if (!isDynamicLoad()) { updateSyncPageStartAddress(); } // if the table is reloaded, the top address is changed in this case fTopRowAddress = address; updateSyncTopAddress(); // set the cursor at the selected address after reload setCursorAtAddress(address); } fTableCursor.setVisible(true); } catch (DebugException e) { throw e; } finally { fEvtHandleLock.releaseLock(evtLockClient); } } /** * Check if address provided is out of buffered range * @param address the address * @return if address is out of buffered range */ private boolean isAddressOutOfRange(BigInteger address) { return fContentProvider.isAddressOutOfRange(address); } /** * Check if address is visible * @param address the address * @return if the given address is visible */ private boolean isAddressVisible(BigInteger address) { // if view tab is not yet created // cursor should always be visible if (!fIsCreated) { return true; } BigInteger topVisible = getTopVisibleAddress(); int addressableUnit = getAddressableUnitPerLine(); BigInteger lastVisible = getTopVisibleAddress().add(BigInteger.valueOf((getNumberOfVisibleLines() * addressableUnit) + addressableUnit)); if (topVisible.compareTo(address) <= 0 && lastVisible.compareTo(address) > 0) { return true; } return false; } /** * Create actions for this rendering */ protected void createActions() { fCopyToClipboardAction = new CopyTableRenderingToClipboardAction(this, fTableViewer); fGoToAddressAction = new GoToAddressAction(getMemoryRenderingContainer(), this); fResetMemoryBlockAction = new ResetToBaseAddressAction(this); fPrintViewTabAction = new PrintTableRenderingAction(this, fTableViewer); fFormatRenderingAction = new FormatTableRenderingAction(this); fReformatAction = new ReformatAction(this); fToggleAddressColumnAction = new ToggleAddressColumnAction(); IMemoryRenderingSite site = getMemoryRenderingContainer().getMemoryRenderingSite(); if (site.getSite().getSelectionProvider() != null) { fPropertiesAction = new PropertyDialogAction(site.getSite(),site.getSite().getSelectionProvider()); } fNextAction = new NextPageAction(); fPrevAction = new PrevPageAction(); } /** * Handle scrolling and reload table if necessary */ private synchronized void handleScrollBarSelection() { Object evtLockClient = new Object(); try { if (fIsDisposed) { return; } BigInteger address = getTopVisibleAddress(); if (!fTopRowAddress.equals(address)) { fTopRowAddress = address; updateSyncTopAddress(); } if (!fEvtHandleLock.acquireLock(evtLockClient)) { return; } if (getMemoryBlock() instanceof IMemoryBlockExtension) { if (isDynamicLoad()) { if (!isAddressOutOfRange(address)) { Table table = fTableViewer.getTable(); int numInBuffer = table.getItemCount(); int index = findAddressIndex(address); if (index < getBufferThreshold(BUFFER_START)) { if (isAtTopLimit()) { setTopIndex(table, index); } else if (getBufferThreshold(BUFFER_START) > 0) { reloadTable(address, false); } } else if (getBufferThreshold(BUFFER_END) != 0 && (numInBuffer-(index+getNumberOfVisibleLines())) <= getBufferThreshold(BUFFER_END)) { if (!isAtBottomLimit() && getBufferThreshold(BUFFER_END) > 0) { reloadTable(address, false); } } } else { // approaching limit, reload table reloadTable(address, false); } } if (isAddressVisible(fSelectedAddress)) { fTableCursor.setVisible(true); } else { fTableCursor.setVisible(false); } } } finally { fEvtHandleLock.releaseLock(evtLockClient); } } private boolean isAtTopLimit() { BigInteger startAddress = fContentInput.getStartAddress(); startAddress = MemoryViewUtil.alignToBoundary(startAddress, getAddressableUnitPerLine() ); BigInteger startBufferAddress = fContentProvider.getBufferTopAddress(); startBufferAddress = MemoryViewUtil.alignToBoundary(startBufferAddress, getAddressableUnitPerLine()); if (startAddress.compareTo(startBufferAddress) == 0) { return true; } return false; } private boolean isAtBottomLimit() { BigInteger endAddress = fContentInput.getEndAddress(); endAddress = MemoryViewUtil.alignToBoundary(endAddress, getAddressableUnitPerLine()); BigInteger endBufferAddress = fContentProvider.getBufferEndAddress(); endBufferAddress = MemoryViewUtil.alignToBoundary(endBufferAddress, getAddressableUnitPerLine()); if (endAddress.compareTo(endBufferAddress) == 0) { return true; } return false; } private boolean needMoreLines() { if (getMemoryBlock() instanceof IMemoryBlockExtension) { Table table = fTableViewer.getTable(); TableItem firstItem = table.getItem(0); TableItem lastItem = table.getItem(table.getItemCount()-1); if (firstItem == null || lastItem == null) { return true; } TableRenderingLine first = (TableRenderingLine)firstItem.getData(); TableRenderingLine last = (TableRenderingLine) lastItem.getData(); if (first == null ||last == null) { // For some reason, the table does not return the correct number // of table items in table.getItemCount(), causing last to be null. // This check is to ensure that we don't get a null pointer exception. return true; } BigInteger startAddress = new BigInteger(first.getAddress(), 16); BigInteger lastAddress = new BigInteger(last.getAddress(), 16); int addressableUnit = getAddressableUnitPerLine(); lastAddress = lastAddress.add(BigInteger.valueOf(addressableUnit)); BigInteger topVisibleAddress = getTopVisibleAddress(); long numVisibleLines = getNumberOfVisibleLines(); long numOfBytes = numVisibleLines * addressableUnit; BigInteger lastVisibleAddrss = topVisibleAddress.add(BigInteger.valueOf(numOfBytes)); // if there are only 3 lines left at the top, refresh BigInteger numTopLine = topVisibleAddress.subtract(startAddress).divide(BigInteger.valueOf(addressableUnit)); if (numTopLine.compareTo(BigInteger.valueOf(getBufferThreshold(BUFFER_START))) <= 0 && (startAddress.compareTo(BigInteger.valueOf(0)) != 0)) { if (!isAtTopLimit() && getBufferThreshold(BUFFER_START) > 0) { return true; } } // if there are only 3 lines left at the bottom, refresh BigInteger numBottomLine = lastAddress.subtract(lastVisibleAddrss).divide(BigInteger.valueOf(addressableUnit)); if (numBottomLine.compareTo(BigInteger.valueOf(getBufferThreshold(BUFFER_END))) <= 0) { if (!isAtBottomLimit() && getBufferThreshold(BUFFER_END) > 0) { return true; } } return false; } return false; } private void handleTableMouseEvent(MouseEvent e) { // figure out new cursor position based on here the mouse is pointing TableItem[] tableItems = fTableViewer.getTable().getItems(); TableItem selectedRow = null; int colNum = -1; int numCol = fTableViewer.getColumnProperties().length; for (int j=0; j<tableItems.length; j++) { TableItem item = tableItems[j]; for (int i=0; i<numCol; i++) { Rectangle bound = item.getBounds(i); if (bound.contains(e.x, e.y)) { colNum = i; selectedRow = item; break; } } } // if column position cannot be determined, return if (colNum < 1) { return; } // handle user mouse click onto table // move cursor to new position if (selectedRow != null) { int row = fTableViewer.getTable().indexOf(selectedRow); fTableCursor.setVisible(true); fTableCursor.setSelection(row, colNum); // manually call this since we don't get an event when // the table cursor changes selection. handleCursorMoved(); fTableCursor.setFocus(); } } /** * Handle column size changed event from synchronizer * @param newColumnSize the new column size */ private void columnSizeChanged(final int newColumnSize) { // ignore event if view tab is disabled if (!isVisible()) { return; } Display.getDefault().asyncExec(new Runnable() { @Override public void run() { format(getBytesPerLine(), newColumnSize); } }); } /** * @param newRowSize - new row size in number of bytes */ private void rowSizeChanged(final int newRowSize) { // ignore event if view tab is disabled if (!isVisible()) { return; } int bytesPerLine = newRowSize; int col = getBytesPerColumn(); if (bytesPerLine < getBytesPerColumn()) { col = bytesPerLine; } final int columnSize = col; final int rowSize = bytesPerLine; Display.getDefault().asyncExec(new Runnable() { @Override public void run() { format(rowSize, columnSize); } }); } private void handleCursorMouseEvent(MouseEvent e){ if (e.button == 1) { int col = fTableCursor.getColumn(); if (col > 0 && col <= (getNumCol())) { activateCellEditor(null); } } } /** * Activate cell editor and pre-fill it with initial value. * If initialValue is null, use cell content as initial value * @param initialValue the initial value to edit */ private void activateCellEditor(String initialValue) { int col = fTableCursor.getColumn(); int row = findAddressIndex(fSelectedAddress); if (row < 0) { return; } // do not allow user to edit address column if (col == 0 || col > getNumCol()) { return; } ICellModifier cellModifier = null; if (fTableViewer == null) { return; } cellModifier = fTableViewer.getCellModifier(); TableItem tableItem = fTableViewer.getTable().getItem(row); Object element = tableItem.getData(); Object property = fTableViewer.getColumnProperties()[col]; Object value = cellModifier.getValue(element, (String)property); // The cell modifier canModify function always returns false if the edit action // is not invoked from here. This is to prevent data to be modified when // the table cursor loses focus from a cell. By default, data will // be changed in a table when the cell loses focus. This is to workaround // this default behavior and only change data when the cell editor // is activated. ((TableRenderingCellModifier)cellModifier).setEditActionInvoked(true); boolean canEdit = cellModifier.canModify(element, (String)property); ((TableRenderingCellModifier)cellModifier).setEditActionInvoked(false); if (!canEdit) { return; } // activate based on current cursor position TextCellEditor selectedEditor = (TextCellEditor)fTableViewer.getCellEditors()[col]; if (fTableViewer != null && selectedEditor != null) { // The control that will be the editor must be a child of the Table Text text = (Text)selectedEditor.getControl(); String cellValue = null; if (initialValue != null) { cellValue = initialValue; } else { cellValue = ((String)value); } text.setText(cellValue); fCursorEditor.horizontalAlignment = SWT.LEFT; fCursorEditor.grabHorizontal = true; // Open the text editor in selected column of the selected row. fCursorEditor.setEditor (text, tableItem, col); // Assign focus to the text control selectedEditor.setFocus(); if (initialValue != null) { text.clearSelection(); } text.setFont(JFaceResources.getFont(IInternalDebugUIConstants.FONT_NAME)); // add listeners for the text control addListeners(text); // move cursor below text control fTableCursor.moveBelow(text); } } /** * @param text the {@link Text} widget to add the listeners to */ private void addListeners(Text text) { fEditorFocusListener = new FocusAdapter() { @Override public void focusLost(FocusEvent e) { handleTableEditorFocusLost(e); } }; text.addFocusListener(fEditorFocusListener); fEditorKeyListener = new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { handleKeyEventInEditor(e); } }; text.addKeyListener(fEditorKeyListener); } /** * @param text the {@link Text} widget to remove the listeners from */ private void removeListeners(Text text) { text.removeFocusListener(fEditorFocusListener); text.removeKeyListener(fEditorKeyListener); } private void handleTableEditorFocusLost(FocusEvent event) { final FocusEvent e = event; Display.getDefault().syncExec(new Runnable() { @Override public void run() { try { int row = findAddressIndex(fSelectedAddress); int col = fTableCursor.getColumn(); Text text = (Text)e.getSource(); removeListeners(text); // get new value String newValue = text.getText(); // modify memory at fRow and fCol modifyValue(row, col, newValue); // show cursor after modification is completed setCursorAtAddress(fSelectedAddress); fTableCursor.moveAbove(text); fTableCursor.setVisible(false); fTableCursor.setVisible(true); } catch (NumberFormatException e1) { MemoryViewUtil.openError(DebugUIMessages.MemoryViewCellModifier_failure_title, DebugUIMessages.MemoryViewCellModifier_data_is_invalid, null); } } }); } /** * @param event the {@link KeyEvent} */ private void handleKeyEventInEditor(KeyEvent event) { final KeyEvent e = event; Display.getDefault().asyncExec(new Runnable() { @Override public void run() { Text text = (Text)e.getSource(); int row = findAddressIndex(fSelectedAddress); int col = fTableCursor.getColumn(); try { switch (e.keyCode) { case SWT.ARROW_UP : // move text editor box up one row if (row-1 < 0) { return; } // modify value for current cell modifyValue(row, col, text.getText()); row--; // update cursor location and selection in table fTableCursor.setSelection(row, col); handleCursorMoved(); // remove listeners when focus is lost removeListeners(text); activateCellEditor(null); break; case SWT.ARROW_DOWN : // move text editor box down one row if (row+1 >= fTableViewer.getTable().getItemCount()) { return; } // modify value for current cell modifyValue(row, col, text.getText()); row++; // update cursor location and selection in table fTableCursor.setSelection(row, col); handleCursorMoved(); // remove traverse listener when focus is lost removeListeners(text); activateCellEditor(null); break; case 0: // if user has entered the max number of characters allowed in a cell, move to next cell // Extra changes will be used as initial value for the next cell int numCharsPerByte = getNumCharsPerByte(); if (numCharsPerByte > 0) { if (text.getText().length() > getBytesPerColumn()*numCharsPerByte) { String newValue = text.getText(); text.setText(newValue.substring(0, getBytesPerColumn()*numCharsPerByte)); modifyValue(row, col, text.getText()); // if cursor is at the end of a line, move to next line if (col >= getNumCol()) { col = 1; row++; } else { // move to next column row++; } // update cursor position and selected address fTableCursor.setSelection(row, col); handleCursorMoved(); removeListeners(text); // activate text editor at next cell activateCellEditor(newValue.substring(getBytesPerColumn()*numCharsPerByte)); } } break; case SWT.ESC: // if user has pressed escape, do not commit the changes // that's why "modifyValue" is not called fTableCursor.setSelection(row, col); handleCursorMoved(); removeListeners(text); // cursor needs to have focus to remove focus from cell editor fTableCursor.setFocus(); break; default : numCharsPerByte = getNumCharsPerByte(); if (numCharsPerByte > 0) { if (text.getText().length()> getBytesPerColumn()* numCharsPerByte) { String newValue = text.getText(); text.setText(newValue.substring(0,getBytesPerColumn()* numCharsPerByte)); modifyValue(row, col, text.getText()); // if cursor is at the end of a line, move to next line if (col >= getNumCol()) { col = 1; row++; } else { col++; } fTableCursor.setSelection(row, col); handleCursorMoved(); removeListeners(text); activateCellEditor(newValue.substring(getBytesPerColumn()*numCharsPerByte)); } } break; } } catch (NumberFormatException e1) { MemoryViewUtil.openError(DebugUIMessages.MemoryViewCellModifier_failure_title, DebugUIMessages.MemoryViewCellModifier_data_is_invalid, null); fTableCursor.setSelection(row, col); handleCursorMoved(); removeListeners(text); } } }); } /** * Modify value and send new value to debug adapter * @param row the row * @param col the column * @param newValue the new value * @throws NumberFormatException if the {@link ICellModifier} cannot convert the new value to a string - in cases where it needs to do so */ private void modifyValue(int row, int col, String newValue) throws NumberFormatException { if (newValue.length() == 0) { // do not do anything if user has not entered anything return; } TableItem tableItem = fTableViewer.getTable().getItem(row); Object property = fTableViewer.getColumnProperties()[col]; fTableViewer.getCellModifier().modify(tableItem, (String)property, newValue); } /* (non-Javadoc) * @see org.eclipse.debug.ui.memory.IMemoryRendering#becomesHidden() */ @Override public void becomesHidden() { if (isVisible() == false) { // super should always be called super.becomesHidden(); return; } super.becomesHidden(); if (getMemoryBlock() instanceof IMemoryBlockExtension) { updateRenderingLabel(false); } // once the view tab is disabled, all deltas information becomes invalid. // reset changed information and recompute if data has really changed when // user revisits the same tab. fContentProvider.resetDeltas(); } /* (non-Javadoc) * @see org.eclipse.debug.ui.memory.IMemoryRendering#becomesVisible() */ @Override public void becomesVisible() { // do not do anything if already visible if (isVisible() == true) { // super should always be called super.becomesVisible(); return; } super.becomesVisible(); boolean value = DebugUIPlugin.getDefault().getPreferenceStore().getBoolean(IDebugPreferenceConstants.PREF_DYNAMIC_LOAD_MEM); if (value != isDynamicLoad()) { // this call will cause a reload handleDyanicLoadChanged(); } else { refresh(); } synchronize(); updateRenderingLabel(true); } /** * Resets this memory rendering. * The cursor will be moved to the base address of the memory block. * The table will be positioned to have the base address * at the top. * * @deprecated use <code>resetRendering</code> to reset this rendering. */ @Deprecated public void reset() { try { resetToBaseAddress(); } catch (DebugException e) { MemoryViewUtil.openError(DebugUIMessages.AbstractTableRendering_12, DebugUIMessages.AbstractTableRendering_13, e); // } } /** * Reset this rendering to the base address. * The cursor will be moved to the base address of the memory block. * The table will be positioned to have the base address * at the top. * @throws DebugException is an exception occurs */ private void resetToBaseAddress() throws DebugException { BigInteger baseAddress; if (getMemoryBlock() instanceof IMemoryBlockExtension) { baseAddress = ((IMemoryBlockExtension)getMemoryBlock()).getBigBaseAddress(); } else { baseAddress = BigInteger.valueOf(getMemoryBlock().getStartAddress()); } goToAddress(baseAddress); topVisibleAddressChanged(baseAddress, true); } /** * Returns the currently selected address in this rendering. * * @return the currently selected address in this rendering */ @Override public BigInteger getSelectedAddress() { return fSelectedAddress; } /** * Returns the currently selected content in this rendering as a String. * * @return the currently selected content in this rendering */ @Override public String getSelectedAsString() { if (isAddressOutOfRange(fSelectedAddress)) { return IInternalDebugCoreConstants.EMPTY_STRING; } int col = fTableCursor.getColumn(); TableItem rowItem = fTableCursor.getRow(); int row = fTableViewer.getTable().indexOf(rowItem); if (col == 0) { return rowItem.getText(0); } // check precondition if (col > getBytesPerLine()/getBytesPerColumn()) { return IInternalDebugCoreConstants.EMPTY_STRING; } TableItem tableItem = getTableViewer().getTable().getItem(row); return tableItem.getText(col); } /** * 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 (isAddressOutOfRange(fSelectedAddress)) { return new MemoryByte[0]; } int col = fTableCursor.getColumn(); TableItem rowItem = fTableCursor.getRow(); // check precondition if (col == 0 || col > getBytesPerLine()/getBytesPerColumn()) { return new MemoryByte[0]; } Object data = rowItem.getData(); if (data == null || !(data instanceof TableRenderingLine)) { return new MemoryByte[0]; } TableRenderingLine line = (TableRenderingLine)data; int offset = (col-1)*(getAddressableUnitPerColumn()*getAddressableSize()); int end = offset + (getAddressableUnitPerColumn()*getAddressableSize()); // make a copy of the bytes to ensure that data cannot be changed // by caller MemoryByte[] bytes = line.getBytes(offset, end); MemoryByte[] retBytes = new MemoryByte[bytes.length]; System.arraycopy(bytes, 0, retBytes, 0, bytes.length); return retBytes; } /** * 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; } 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++) { minHeight = Math.min(items[i].getBounds(0).height, minHeight); } return minHeight; } return table.getItemHeight(); } /* (non-Javadoc) * @see org.eclipse.core.runtime.IAdaptable#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 == IMemoryBlockTablePresentation.class) { return (T) getTablePresentationAdapter(); } 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 AbstractTableRendering.this.getLabel(); } @Override public Object getParent(Object o) { return null; } }; } return (T) fWorkbenchAdapter; } if (adapter == IMemoryBlockConnection.class) { if (fConnection == null) { fConnection = new IMemoryBlockConnection() { @Override public void update() { try { fContentProvider.takeContentSnapshot(); if (getMemoryBlock() instanceof IMemoryBlockExtension) { BigInteger address = ((IMemoryBlockExtension)getMemoryBlock()).getBigBaseAddress(); if (address.compareTo(fContentProvider.getContentBaseAddress()) != 0) { // get to new address setSelectedAddress(address); updateSyncSelectedAddress(); fTopRowAddress = address; fContentInput.updateContentBaseAddress(); fContentInput.setLoadAddress(address); } fContentProvider.loadContentForExtendedMemoryBlock(); } else { fContentProvider.loadContentForSimpleMemoryBlock(); } // update UI asynchronously Display display = DebugUIPlugin.getDefault().getWorkbench().getDisplay(); display.asyncExec(new Runnable() { @Override public void run() { updateLabels(); if (getMemoryBlock() instanceof IMemoryBlockExtension) { int topIdx = findAddressIndex(fTopRowAddress); if (topIdx != -1) { setTopIndex(fTableViewer.getTable(),topIdx); } } // cursor needs to be refreshed after reload if (isAddressVisible(fSelectedAddress)) { setCursorAtAddress(fSelectedAddress); fTableCursor.setVisible(true); fTableCursor.redraw(); } else { fTableCursor.setVisible(false); } if (!isDynamicLoad()) { updateSyncPageStartAddress(); } updateSyncTopAddress(); } }); } catch (DebugException e) { displayError(e); } } }; } return (T) fConnection; } return super.getAdapter(adapter); } private boolean hasCustomizedDecorations() { if (getFontProviderAdapter() == null && getColorProviderAdapter() == null && getLabelProviderAdapter() == null) { return false; } return true; } private boolean isBaseAddressChanged() { try { IMemoryBlock mb = getMemoryBlock(); if (mb instanceof IMemoryBlockExtension) { BigInteger baseAddress = ((IMemoryBlockExtension)mb).getBigBaseAddress(); if (baseAddress != null) { if (!baseAddress.equals(fContentInput.getContentBaseAddress())) { return true; } } } } catch (DebugException e1) { return false; } return false; } /** * 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); } private boolean isDynamicLoad() { return fContentProvider.isDynamicLoad(); } private int getPageSizeInUnits() { return fPageSize * getAddressableUnitPerLine(); } private void setSelectedAddress(BigInteger address) { fSelectedAddress = address; } /** * 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); fTableCursor.addMouseTrackListener(listener); } /** * 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 {@link Point} to get the {@link TableItem} from * @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++) { Point start = new Point(items[i].getBounds(0).x, items[i].getBounds(0).y); start = fTableViewer.getTable().toDisplay(start); Point end = new Point(start.x + items[i].getBounds(0).width, start.y + items[i].getBounds(0).height); if (start.y < point.y && point.y < end.y) { return items[i]; } } return null; } /** * Method for figuring out which column the point is located. * @param point the {@link Point} to et 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(); if(fTableViewer.getTable().getItemCount() > 0) { TableItem item = fTableViewer.getTable().getItem(0); Point start, end; for (int i=0; i<colCnt; i++) { start = new Point(item.getBounds(i).x, item.getBounds(i).y); start = fTableViewer.getTable().toDisplay(start); 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; } /** * 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; } /* (non-Javadoc) * @see org.eclipse.debug.ui.memory.IResettableMemoryRendering#resetRendering() */ @Override public void resetRendering() throws DebugException { resetToBaseAddress(); } /** * 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) { BigInteger address = getAddressFromTableItem(item, col); if (address != null) { Object data = item.getData(); if (data instanceof TableRenderingLine) { TableRenderingLine line = (TableRenderingLine) data; if (col > 0) { int start = (col - 1) * getBytesPerColumn(); int end = start + getBytesPerColumn(); MemoryByte[] bytes = line.getBytes(start, end); String str = getToolTipText(address, bytes); if (str != null) { ((Label) toolTipControl).setText(str); } } else { String str = getToolTipText(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 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; } /** * @param modelId the debug model identifier * @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 debug model identifier * @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 int getBufferThreshold(int startOrEnd) { if (startOrEnd == BUFFER_START) { if (BUFFER_THRESHOLD > fPreBuffer) { return fPreBuffer; } return BUFFER_THRESHOLD; } if (BUFFER_THRESHOLD > fPostBuffer) { return fPostBuffer; } return BUFFER_THRESHOLD; } /** * 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); }