blob: 3ba7945c264355d950a1cf879c75d11baca8d943 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2018 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* 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.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 = 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 = 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(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(() -> 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(() -> 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(() -> {
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(() -> {
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 newValue1 = text.getText();
text.setText(newValue1.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(newValue1.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 newValue2 = text.getText();
text.setText(newValue2.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(newValue2.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 = () -> {
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(() -> {
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);
}