blob: 06c0256a8f0a6c2b8ceda4319454edb8669ca25a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.debug.internal.ui.views.memory;
import java.math.BigInteger;
import java.util.ArrayList;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.model.IMemoryBlock;
import org.eclipse.debug.internal.core.memory.IExtendedMemoryBlock;
import org.eclipse.debug.internal.core.memory.IMemoryRendering;
import org.eclipse.debug.internal.core.memory.MemoryBlockManager;
import org.eclipse.debug.internal.ui.DebugUIMessages;
import org.eclipse.debug.internal.ui.DebugUIPlugin;
import org.eclipse.debug.internal.ui.DelegatingModelPresentation;
import org.eclipse.debug.internal.ui.IInternalDebugUIConstants;
import org.eclipse.debug.internal.ui.LazyModelPresentation;
import org.eclipse.debug.internal.ui.preferences.IDebugPreferenceConstants;
import org.eclipse.debug.ui.IDebugModelPresentation;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.preference.IPreferenceStore;
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.ICellModifier;
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.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.ScrollBar;
import org.eclipse.swt.widgets.TabItem;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;
/**
* @since 3.0
*/
public class MemoryViewTab extends AbstractMemoryViewTab implements SelectionListener, ControlListener, KeyListener, ITableMemoryViewTab, ISynchronizedMemoryBlockView{
private static final String PREFIX = "MemoryViewTab."; //$NON-NLS-1$
private static final String ADDRESS = PREFIX + "Address"; //$NON-NLS-1$
private static final String ERROR = PREFIX + "Error"; //$NON-NLS-1$
private static final String FORMAT_IS_INVALID = PREFIX + "Format_is_invalid"; //$NON-NLS-1$
private static final String ADDRESS_IS_OUT_OF_RANGE = PREFIX + "Address_is_out_of_range"; //$NON-NLS-1$
private static final String COLUMN_SIZE = PREFIX + "Column_size"; //$NON-NLS-1$
//private static final String ADD_RENDERING = PREFIX + "Add_rendering"; //$NON-NLS-1$
private static final String UNABLE_TO_GET_BASE_ADDRESS = PREFIX +"Unable_to_retrieve_base_address"; //$NON-NLS-1$
private static final String UNKNOWN = PREFIX + "Unknown"; //$NON-NLS-1$
// menu group names
private static final String MEMORY_ACTIONS_GROUP = IDebugUIConstants.PLUGIN_ID + ".MemoryViewActionsGroup"; //$NON-NLS-1$
private static final String MEMORY_ACTIONS_FORMAT_GROUP = IDebugUIConstants.PLUGIN_ID + ".MemoryViewActionsGroup.format"; //$NON-NLS-1$
//private static final String MEMORY_ACTIONS_RENDERING_GROUP = IDebugUIConstants.PLUGIN_ID + ".MemoryViewActionsGroup.rendering"; //$NON-NLS-1$
private MemoryViewContentProvider contentProvider;
private TableViewer fTableViewer = null;
private boolean fEnabled;
private ViewTabCursorManager fCursorManager;
private IMemoryBlockModelPresentation fMemoryBlockPresentation;
private boolean fNoPresentation = false;
public int TABLE_PREBUFFER = 20;
public int TABLE_POSTBUFFER = 20;
public int TABLE_DEFAULTBUFFER = 20;
private TextViewer fTextViewer = null;
private boolean errorOccurred = false;
protected BigInteger fSelectedAddress = null;
private boolean fTabCreated = false;
private CellEditor fEditors[];
private ICellModifier fCellModifier;
private CopyViewTabToClipboardAction fCopyToClipboardAction;
private GoToAddressAction fGoToAddressAction;
private ResetMemoryBlockAction fResetMemoryBlockAction;
private PrintViewTabAction fPrintViewTabAction;
private Action[] fFormatColumnActions;
private ReformatAction fReformatAction;
private boolean fIsDisposed = false;
private int fBytePerLine; // number of bytes per line: 16
private int fColumnSize; // number of bytes per column: 1,2,4,8
// font change listener
private FontChangeListener fFontChangeListener;
private TabFolderDisposeListener fTabFolderDisposeListener;
private static final int[] ignoreEvents =
{
SWT.ARROW_UP,
SWT.ARROW_DOWN,
SWT.ARROW_LEFT,
SWT.ARROW_RIGHT,
SWT.PAGE_UP,
SWT.PAGE_DOWN,
SWT.HOME,
SWT.END,
SWT.INSERT,
SWT.F1,
SWT.F2,
SWT.F3,
SWT.F4,
SWT.F5,
SWT.F6,
SWT.F7,
SWT.F8,
SWT.F9,
SWT.F10,
SWT.F11,
SWT.F12,
SWT.F13,
SWT.F14,
SWT.F15,
SWT.HELP,
SWT.CAPS_LOCK,
SWT.NUM_LOCK,
SWT.SCROLL_LOCK,
SWT.PAUSE,
SWT.BREAK,
SWT.PRINT_SCREEN,
SWT.ESC,
SWT.CTRL,
SWT.ALT
};
private final class TabFolderDisposeListener implements DisposeListener
{
MemoryViewTab fViewTab;
TabFolderDisposeListener(MemoryViewTab viewTab)
{
fViewTab = viewTab;
}
public void widgetDisposed(DisposeEvent e)
{
if (!fViewTab.fIsDisposed)
{
// remove listeners
JFaceResources.getFontRegistry().removeListener(fFontChangeListener);
removeReferenceFromSynchronizer();
getMemoryBlockViewSynchronizer().removeView(fViewTab);
}
}
}
class FontChangeListener implements IPropertyChangeListener
{
/* (non-Javadoc)
* @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
*/
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);
}
}
}
}
// ** Referring to internal class: DelegatingModelPresentation and LazyModelPresentation
// ** This should be ok when Memory View is contributed to Eclipse platform?
class MemoryViewDelegatingModelPresentation extends DelegatingModelPresentation
{
MemoryViewDelegatingModelPresentation()
{
IExtensionPoint point= Platform.getExtensionRegistry().getExtensionPoint(DebugUIPlugin.getUniqueIdentifier(), IDebugUIConstants.ID_DEBUG_MODEL_PRESENTATION);
if (point != null) {
IExtension[] extensions= point.getExtensions();
for (int i= 0; i < extensions.length; i++) {
IExtension extension= extensions[i];
IConfigurationElement[] configElements= extension.getConfigurationElements();
for (int j= 0; j < configElements.length; j++) {
IConfigurationElement elt= configElements[j];
String id= elt.getAttribute("id"); //$NON-NLS-1$
if (id != null) {
IDebugModelPresentation lp= new MemoryViewLazyModelPresentation(elt);
getLabelProviders().put(id, lp);
}
}
}
}
}
}
class MemoryViewLazyModelPresentation extends LazyModelPresentation implements IMemoryBlockModelPresentation
{
MemoryViewLazyModelPresentation(IConfigurationElement element)
{
super(element);
}
/* (non-Javadoc)
* @see org.eclipse.debug.ui.IMemoryBlockModelPresentation#getTabLabel(org.eclipse.debug.core.model.IMemoryBlock)
*/
public String getTabLabel(IMemoryBlock blk, String renderingId)
{
IDebugModelPresentation presentation = getPresentation();
if (presentation instanceof IMemoryBlockModelPresentation)
{
return ((IMemoryBlockModelPresentation)presentation).getTabLabel(blk, getRenderingId());
}
else
{
return null;
}
}
/* (non-Javadoc)
* @see org.eclipse.debug.ui.IMemoryBlockModelPresentation#getColumnLabels(org.eclipse.debug.core.model.IMemoryBlock, int, int)
*/
public String[] getColumnLabels(IMemoryBlock blk, int bytesPerLine, int columnSize)
{
IDebugModelPresentation presentation = getPresentation();
if (presentation instanceof IMemoryBlockModelPresentation)
{
return ((IMemoryBlockModelPresentation)presentation).getColumnLabels(blk, bytesPerLine, columnSize);
}
else
{
return new String[0];
}
}
/* (non-Javadoc)
* @see org.eclipse.debug.ui.IMemoryBlockModelPresentation#getAddressPresentation(org.eclipse.debug.core.model.IMemoryBlock, java.math.BigInteger)
*/
public String getAddressPresentation(IMemoryBlock blk, BigInteger address)
{
IDebugModelPresentation presentation = getPresentation();
if (presentation instanceof IMemoryBlockModelPresentation)
{
return ((IMemoryBlockModelPresentation)presentation).getAddressPresentation(blk, address);
}
else
{
return null;
}
}
}
public MemoryViewTab(IMemoryBlock newMemory, TabItem newTab, MenuManager menuMgr, IMemoryRendering rendering, AbstractMemoryRenderer renderer) {
super(newMemory, newTab, menuMgr, rendering);
setTabName(newMemory, true);
fTabItem.setControl(createFolderPage(renderer));
if (!(newMemory instanceof IExtendedMemoryBlock))
{
// If not extended memory block, do not create any buffer
// no scrolling
TABLE_PREBUFFER=0;
TABLE_POSTBUFFER=0;
TABLE_DEFAULTBUFFER=0;
}
if (fTableViewer != null)
{
fTableViewer.getTable().setTopIndex(TABLE_PREBUFFER);
}
addViewTabToSynchronizer();
// otherwise, this is a totally new synchronize info
fEnabled = true;
fTabCreated = true;
//synchronize
synchronize();
createActions();
// 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.
packColumns();
// add listeners in the end to make sure that the resize event
// does not affect synchronization
if (fTableViewer != null){
fTableViewer.getTable().addSelectionListener(this);
fTabFolderDisposeListener = new TabFolderDisposeListener(this);
fTabItem.addDisposeListener(fTabFolderDisposeListener);
}
if (fMemoryBlock instanceof IExtendedMemoryBlock)
{
if(((IExtendedMemoryBlock)fMemoryBlock).getBigBaseAddress() == null)
{
DebugException e = new DebugException(DebugUIPlugin.newErrorStatus(DebugUIMessages.getString(UNABLE_TO_GET_BASE_ADDRESS), null));
displayError(e);
}
}
}
private void addViewTabToSynchronizer()
{
getMemoryBlockViewSynchronizer().addView(this, null);
// check if there is already synchronization info available
Object selectedAddress =getSynchronizedProperty( IMemoryViewConstants.PROPERTY_SELECTED_ADDRESS);
Object size =getSynchronizedProperty( IMemoryViewConstants.PROPERTY_COL_SIZE);
Object topAddress =getSynchronizedProperty( IMemoryViewConstants.PROPERTY_TOP_ADDRESS);
// if info is available, some other view tab has already been created
// do not overwirte info int he synchronizer if that's the case
if (selectedAddress == null)
{
updateSyncSelectedAddress(true);
}
if (size == null)
{
updateSyncColSize();
}
if (topAddress == null)
{
updateSyncTopAddress(true);
}
}
/**
* update selected address in synchronizer if update is true.
*/
private void updateSyncSelectedAddress(boolean update) {
if (update)
getMemoryBlockViewSynchronizer().setSynchronizedProperty(getMemoryBlock(), IMemoryViewConstants.PROPERTY_SELECTED_ADDRESS, fSelectedAddress);
}
/**
* update column size in synchronizer
*/
private void updateSyncColSize() {
getMemoryBlockViewSynchronizer().setSynchronizedProperty(getMemoryBlock(), IMemoryViewConstants.PROPERTY_COL_SIZE, new Integer(fColumnSize));
}
/**
* update top visible address in synchronizer
*/
protected void updateSyncTopAddress(boolean updateToSynchronizer) {
if (updateToSynchronizer)
{
getMemoryBlockViewSynchronizer().setSynchronizedProperty(getMemoryBlock(), IMemoryViewConstants.PROPERTY_TOP_ADDRESS, getTopVisibleAddress());
}
}
protected void setTabName(IMemoryBlock newMemory, boolean showAddress)
{
String tabName = null;
if (getMemoryBlockPresentation() != null)
tabName = getMemoryBlockPresentation().getTabLabel(newMemory, getRenderingId());
if (tabName == null)
{
tabName = ""; //$NON-NLS-1$
try {
if (newMemory instanceof IExtendedMemoryBlock)
{
tabName = ((IExtendedMemoryBlock)newMemory).getExpression();
if (tabName.startsWith("&")) //$NON-NLS-1$
tabName = "&" + tabName; //$NON-NLS-1$
if (tabName == null)
{
tabName = DebugUIMessages.getString(UNKNOWN);
}
if (showAddress && ((IExtendedMemoryBlock)newMemory).getBigBaseAddress() != null)
{
tabName += " : 0x"; //$NON-NLS-1$
tabName += ((IExtendedMemoryBlock)newMemory).getBigBaseAddress().toString(16);
}
}
else
{
long address = newMemory.getStartAddress();
tabName = Long.toHexString(address);
}
} catch (DebugException e) {
tabName = DebugUIMessages.getString(UNKNOWN);
DebugUIPlugin.log(e.getStatus());
}
String preName = MemoryBlockManager.getMemoryRenderingManager().getRenderingInfo(getRenderingId()).getName();
if (preName != null)
tabName += " <" + preName + ">"; //$NON-NLS-1$ //$NON-NLS-2$
}
fTabItem.setText(tabName);
}
/**
* Create actions for the view tab
*/
protected void createActions() {
fCopyToClipboardAction = new CopyViewTabToClipboardContextAction(this);
fGoToAddressAction = new GoToAddressAction(this);
fResetMemoryBlockAction = new ResetMemoryBlockContextAction(this);
fPrintViewTabAction = new PrintViewTabContextAction(this);
fFormatColumnActions = new Action[6];
fFormatColumnActions[0] = new FormatColumnAction(1, this);
fFormatColumnActions[1] = new FormatColumnAction(2, this);
fFormatColumnActions[2] = new FormatColumnAction(4, this);
fFormatColumnActions[3] = new FormatColumnAction(8, this);
fFormatColumnActions[4] = new FormatColumnAction(16, this);
fFormatColumnActions[5] = new SetColumnSizeDefaultAction(this);
fReformatAction = new ReformatAction(this);
}
/* (non-Javadoc)
* @see org.eclipse.debug.ui.IMemoryViewTab#fillContextMenu(org.eclipse.jface.action.IMenuManager)
*/
public void fillContextMenu(IMenuManager menu) {
menu.add(new Separator(MEMORY_ACTIONS_GROUP));
menu.add(fResetMemoryBlockAction);
menu.add(fGoToAddressAction);
menu.add(new Separator());
if (fFormatColumnActions.length > 0)
{
// Format view tab actions
IMenuManager formatMenu = new MenuManager(DebugUIMessages.getString(COLUMN_SIZE),
MEMORY_ACTIONS_FORMAT_GROUP);
menu.appendToGroup(MEMORY_ACTIONS_GROUP, formatMenu);
for (int i=0; i<fFormatColumnActions.length; i++)
{
formatMenu.add(fFormatColumnActions[i]);
// add check mark to the action to reflect current format of the view tab
if (fFormatColumnActions[i] instanceof FormatColumnAction)
{
if (((FormatColumnAction)fFormatColumnActions[i]).getColumnSize() == getColumnSize())
{
fFormatColumnActions[i].setChecked(true);
}
else
{
fFormatColumnActions[i].setChecked(false);
}
}
}
}
menu.add(new Separator());
menu.add(fReformatAction);
menu.add(fCopyToClipboardAction);
menu.add(fPrintViewTabAction);
}
private Control createFolderPage(AbstractMemoryRenderer renderer) {
contentProvider = new MemoryViewContentProvider(fMemoryBlock, fTabItem);
fTableViewer= new TableViewer(fTabItem.getParent(), SWT.FULL_SELECTION | SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.HIDE_SELECTION | SWT.BORDER);
fTableViewer.setContentProvider(contentProvider);
if (renderer != null)
{
renderer.setRenderingId(fRenderingId);
MemoryViewTabLabelProvider labelProvider = new MemoryViewTabLabelProvider(this, renderer);
fTableViewer.setLabelProvider(labelProvider);
((AbstractTableViewTabLabelProvider)labelProvider).setViewTab(this);
}
else
{
renderer = new EmptyRenderer();
renderer.setRenderingId(fRenderingId);
renderer.setViewTab(this);
MemoryViewTabLabelProvider labelProvider = new MemoryViewTabLabelProvider(this, renderer);
fTableViewer.setLabelProvider(labelProvider);
DebugUIPlugin.log(DebugUIPlugin.newErrorStatus("Renderer property is not defined for: " + fRenderingId, null)); //$NON-NLS-1$
}
contentProvider.setViewer(fTableViewer);
ScrollBar scroll = ((Table)fTableViewer.getControl()).getVerticalBar();
scroll.addSelectionListener(this);
scroll.setMinimum(-100);
scroll.setMaximum(200);
fTableViewer.getControl().addControlListener(this);
fTableViewer.getControl().addKeyListener(this);
fTableViewer.getTable().setHeaderVisible(true);
fTableViewer.getTable().setLinesVisible(true);
int bytePerLine = IInternalDebugUIConstants.BYTES_PER_LINE;
// get default column size from preference store
IPreferenceStore prefStore = DebugUIPlugin.getDefault().getPreferenceStore();
int columnSize = prefStore.getInt(IDebugPreferenceConstants.PREF_COLUMN_SIZE);
// check synchronized col size
Integer colSize = (Integer)getSynchronizedProperty(IMemoryViewConstants.PROPERTY_COL_SIZE);
if (colSize != null)
{
int syncColSize = colSize.intValue();
if (syncColSize > 0)
{
columnSize = syncColSize;
}
}
// format memory block with specified "bytesPerLine" and "columnSize"
boolean ok = format(bytePerLine, columnSize);
if (!ok)
{
DebugException e = new DebugException(DebugUIPlugin.newErrorStatus(DebugUIMessages.getString(FORMAT_IS_INVALID), null));
displayError(e);
return fTextViewer.getControl();
}
fTableViewer.setInput(fMemoryBlock);
fCellModifier = new MemoryViewCellModifier(this);
fTableViewer.setCellModifier(fCellModifier);
// set to a non-proportional font
fTableViewer.getTable().setFont(JFaceResources.getFont(IInternalDebugUIConstants.FONT_NAME));
int row = 0;
int col = 1;
// set up cursor manager
// manager sets up initial position of the cursor
fCursorManager = new ViewTabCursorManager(this, row, col, fMenuMgr);
if (fMemoryBlock instanceof IExtendedMemoryBlock)
{
BigInteger address = ((IExtendedMemoryBlock)fMemoryBlock).getBigBaseAddress();
if (address == null)
{
address = new BigInteger("0"); //$NON-NLS-1$
}
BigInteger syncAddress = (BigInteger)getSynchronizedProperty(IMemoryViewConstants.PROPERTY_SELECTED_ADDRESS);
// set initial selected address
if (syncAddress != null)
{
setSelectedAddress(syncAddress, false);
}
else
{
setSelectedAddress(address, true);
}
updateCursorPosition();
}
else
{
long address = fMemoryBlock.getStartAddress();
BigInteger syncAddress = (BigInteger)getSynchronizedProperty(IMemoryViewConstants.PROPERTY_SELECTED_ADDRESS);
if (syncAddress != null)
{
// set initial selected address
setSelectedAddress(syncAddress, false);
}
else
{
setSelectedAddress(BigInteger.valueOf(address), true);
}
updateCursorPosition();
}
// add font change listener and update font when the font has been changed
fFontChangeListener = new FontChangeListener();
JFaceResources.getFontRegistry().addListener(fFontChangeListener);
// finish initialization and return text viewer as the control
if (errorOccurred)
{
return fTextViewer.getControl();
}
return fTableViewer.getControl();
}
/**
* Format view tab based on parameters.
* @param bytesPerLine - number of bytes per line, possible values: 16
* @param columnSize - number of bytes per column, possible values: 1, 2, 4, 8
* @return true if format is successful, false, otherwise
*/
public boolean format(int bytesPerLine, int columnSize)
{
// check parameter, bytesPerLine be 16
if (bytesPerLine != IInternalDebugUIConstants.BYTES_PER_LINE)
{
return false;
}
// bytes per cell must be divisible to bytesPerLine
if (bytesPerLine % columnSize != 0)
{
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;
// if the tab is already created and is being reformated
if (fTabCreated)
{
getTopVisibleAddress();
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.getString(ADDRESS));
// create new byte columns
TableColumn [] byteColumns = new TableColumn[bytesPerLine/columnSize];
String[] columnLabels = new String[0];
if (getMemoryBlockPresentation() != null)
columnLabels = getMemoryBlockPresentation().getColumnLabels(getMemoryBlock(), bytesPerLine, columnSize);
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
if (getColumnSize() >= 4)
{
column.setText(Integer.toHexString(i*columnSize).toUpperCase() +
" - " + Integer.toHexString(i*columnSize+columnSize-1).toUpperCase()); //$NON-NLS-1$
}
else
{
column.setText(Integer.toHexString(i*columnSize).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] = MemoryViewLine.P_ADDRESS;
// use column beginning offset to the row address as properties
for (int i=1; i<columnProperties.length-1; i++)
{
columnProperties[i] = Integer.toHexString((i-1)*columnSize);
}
// Empty column for cursor navigation
columnProperties[columnProperties.length-1] = " "; //$NON-NLS-1$
fTableViewer.setColumnProperties(columnProperties);
// create and set cell editors
fTableViewer.setCellEditors(getCellEditors());
if (fTabCreated)
{
refreshTableViewer();
// after refresh, make sure cursor position is up-to-date
if (isAddressVisible(fSelectedAddress))
updateCursorPosition();
}
packColumns();
updateSyncColSize();
return true;
}
/**
*
*/
private void refreshTableViewer() {
int i = fTableViewer.getTable().getTopIndex();
// refresh if the view is already created
fTableViewer.refresh();
// if top index has changed, restore it
if (i != fTableViewer.getTable().getTopIndex())
fTableViewer.getTable().setTopIndex(i);
}
private void setColumnHeadings()
{
String[] columnLabels = new String[0];
if (getMemoryBlockPresentation() != null)
columnLabels = getMemoryBlockPresentation().getColumnLabels(getMemoryBlock(), fBytePerLine, fColumnSize);
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(i*fColumnSize).toUpperCase() +
" - " + Integer.toHexString(i*fColumnSize+fColumnSize-1).toUpperCase()); //$NON-NLS-1$
}
else
{
columns[i].setText(Integer.toHexString(i*fColumnSize).toUpperCase());
}
}
}
}
/**
* Resize column to the preferred size
*/
public void packColumns() {
// pack columns
Table table = fTableViewer.getTable();
TableColumn[] columns = table.getColumns();
for (int i=0 ;i<columns.length-1; i++)
{
columns[i].pack();
}
if (fCursorManager != null)
{
if (isAddressVisible(fSelectedAddress))
fCursorManager.redrawCursors();
}
}
/**
* @return tab item for the view tab
*/
protected TabItem getTab()
{
return fTabItem;
}
/**
* Force focus on th ecursor if the selected address is not out of range
* Cursor cannot be shown if it's out of range. Otherwise, it messes up
* the top index of the table and affects scrolling.
*/
protected void setCursorFocus()
{
if (!isAddressOutOfRange(fSelectedAddress) && fCursorManager != null)
fCursorManager.setCursorFocus();
}
/* (non-Javadoc)
* @see org.eclipse.debug.ui.IMemoryViewTab#getMemoryBlock()
*/
public IMemoryBlock getMemoryBlock()
{
IMemoryBlock mem = fMemoryBlock;
return mem;
}
/* (non-Javadoc)
* @see org.eclipse.ui.IWorkbenchPart#dispose()
*/
public void dispose() {
try {
fIsDisposed = true;
// clean up listeners
if (fTableViewer != null)
{
fTableViewer.getControl().removeControlListener(this);
fTableViewer.getControl().removeKeyListener(this);
fTableViewer.getTable().removeSelectionListener(this);
}
if (contentProvider != null)
contentProvider.dispose();
ScrollBar scroll = ((Table)fTableViewer.getControl()).getVerticalBar();
if (scroll != null)
scroll.removeSelectionListener(this);
// dispose cursor
if (fCursorManager != null)
fCursorManager.dispose();
// remove selection listener for tab folder
fTabItem.removeDisposeListener(fTabFolderDisposeListener);
fTabItem.dispose();
fTextViewer = null;
fTableViewer = 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(fFontChangeListener);
// remove the view tab from the synchronizer
getMemoryBlockViewSynchronizer().removeView(this);
super.dispose();
} catch (Exception e) {}
}
/* (non-Javadoc)
* @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
*/
public void widgetSelected(SelectionEvent event) {
if (event.getSource() instanceof ScrollBar)
{
handleScrollBarSelection(event);
}
}
/**
* Based on cursor position, update table selection.
* If a lead cursor is not available, the cursor is not visible.
* Update will not be performed if the cursor is not visible.
*/
protected void updateTableSelection()
{
// do not update selection if address is out of range
// otherwise, screws up top index
if (isAddressOutOfRange(fSelectedAddress))
return;
int index = findAddressIndex(getTopVisibleAddress());
// update table selection
fTableViewer.getTable().setSelection(fCursorManager.fRow);
// if top index has changed, restore
if (fTableViewer.getTable().getTopIndex() != index)
fTableViewer.getTable().setTopIndex(index);
}
/**
* Calculate and set selected address based on provided row and column
*/
protected void updateSelectedAddress(TableItem row, int col)
{
// get row address
String temp = ((MemoryViewLine)row.getData()).getAddress();
BigInteger rowAddress = new BigInteger(temp, 16);
int offset;
if (col > 0)
{
// get address offset
offset = (col-1) * getColumnSize();
}
else
{
offset = 0;
}
// update selected address
setSelectedAddress(rowAddress.add(BigInteger.valueOf(offset)), true);
}
/* (non-Javadoc)
* @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
*/
public void widgetDefaultSelected(SelectionEvent e) {
}
/* (non-Javadoc)
* @see org.eclipse.swt.events.ControlListener#controlMoved(org.eclipse.swt.events.ControlEvent)
*/
public void controlMoved(ControlEvent e) {
}
/* (non-Javadoc)
* @see org.eclipse.swt.events.ControlListener#controlResized(org.eclipse.swt.events.ControlEvent)
*/
public void controlResized(ControlEvent e) {
//this method gets called many times as the user drags the window to a new size
//TODO: only refresh the data at the end of the resize, if possible
// do not handle resize if the tab is not yet created completely
if (fTabCreated)
resizeTable();
}
/**
* Handles key events in viewer.
*/
protected void handleKeyPressed(KeyEvent evt) {
final KeyEvent event = evt;
// Must run on UI Thread asynchronously
// Otherwise, another event could have been recevied before the reload is completed
Display.getDefault().syncExec(new Runnable()
{
public void run()
{
if (event.stateMask != 0)
{
return;
}
if (event.getSource() instanceof Text)
return;
// allow edit if user hits return
if (event.character == '\r' && event.getSource() instanceof TableCursor)
{
fCursorManager.activateCellEditor(null);
return;
}
try
{
switch (event.keyCode)
{
case SWT.HOME :
case SWT.PAGE_UP :
case SWT.ARROW_UP :
case SWT.ARROW_LEFT:
case SWT.END :
case SWT.PAGE_DOWN :
case SWT.ARROW_DOWN :
case SWT.ARROW_RIGHT:
// If blocking an extended memory block,
// check to see if additional memory needs to be obtained.
if (fMemoryBlock instanceof IExtendedMemoryBlock)
{
// User could have used scroll bar to scroll away
// from the highlighted address.
// When user hits arrow keys or page up/down keys
// we should go back to the selected address and moves the cursor
// based on the key pressed.
if (isAddressOutOfRange(fSelectedAddress))
{
reloadTable(fSelectedAddress, false);
updateSyncTopAddress(true);
updateSyncSelectedAddress(true);
fCursorManager.setCursorFocus();
break;
}
//if we are approaching the limits of the currently loaded memory, reload the table
if (needMoreLines())
{
BigInteger topAddress = getTopVisibleAddress();
//if we're near 0, just go there immediately (hard stop at 0, don't try to scroll/wrap)
if (topAddress.compareTo(BigInteger.valueOf(96)) <= 0)
{
if (topAddress.equals(BigInteger.valueOf(0)))
{
// do not reload if we are already at zero
break;
}
reloadTable(BigInteger.valueOf(0), false);
fCursorManager.setCursorFocus();
}
else
{
//otherwise, just load the next portion of the memory
reloadTable(topAddress, false);
fCursorManager.setCursorFocus();
}
}
else if (!isAddressVisible(fSelectedAddress))
{
// address is in range, but not visible
// just go to the address and make sure
// that the cursor is in focus
goToAddress(fSelectedAddress);
fCursorManager.setCursorFocus();
updateSyncTopAddress(true);
}
else
{
// in place of the commented lines
updateCursorPosition();
fCursorManager.setCursorFocus();
// since cursor is going to be visible
// synchronization event will be fired by the cursor
// when it is selected
}
}
break;
default :
// if it's a valid key for edit
if (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);
fCursorManager.activateCellEditor(initialValue);
}
}
break;
}
}
catch (DebugException e)
{
displayError(e);
DebugUIPlugin.log(e.getStatus());
}
}
});
}
/**
* @return top visible address of this view tab
*/
public BigInteger getTopVisibleAddress() {
if (fTableViewer == null)
return BigInteger.valueOf(0);
Table table = fTableViewer.getTable();
int topIndex = table.getTopIndex();
if (topIndex < 1) { topIndex = 0; }
if (table.getItemCount() > topIndex)
{
MemoryViewLine topItem = (MemoryViewLine)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;
}
else
{
return BigInteger.valueOf(0);
}
}
/**
* Reload table at the topAddress.
* Delta will be re-computed if updateDelta is true.
* @param topAddress
* @param updateDelta
* @throws DebugException
*/
synchronized protected void reloadTable(BigInteger topAddress, boolean updateDelta) throws DebugException{
if (fTableViewer == null)
return;
Table table = (Table)fTableViewer.getControl();
// Calculate top buffer address
// This is where we will start asking for memory from debug adapter.
BigInteger topBufferAddress = topAddress;
if (topBufferAddress.compareTo(BigInteger.valueOf(32)) <= 0) {
TABLE_PREBUFFER = 0;
} else {
TABLE_PREBUFFER = topBufferAddress.divide(BigInteger.valueOf(32)).min(BigInteger.valueOf(TABLE_DEFAULTBUFFER)).intValue();
}
topBufferAddress = topAddress.subtract(BigInteger.valueOf(getBytesPerLine()*TABLE_PREBUFFER));
// calculate number of lines needed
long numLines = 0;
if (fMemoryBlock instanceof IExtendedMemoryBlock)
{
// number of lines is number of visible lines + buffered lines
numLines = getNumberOfVisibleLines()+TABLE_PREBUFFER+TABLE_POSTBUFFER;
}
// tell content provider to get memory and refresh
contentProvider.getMemoryToFitTable(topBufferAddress, numLines, updateDelta);
contentProvider.forceRefresh();
if (fMemoryBlock instanceof IExtendedMemoryBlock)
{
int topIdx = findAddressIndex(topAddress);
if (topIdx != -1)
{
table.setTopIndex(topIdx);
}
// TODO: Revisit this part again
// if allow cursor update when the cursor is
// not visible, causes flashing on the screen
// if not updated... then cursor may not
// show properly (table selection not hidden)
// if selected address is not out of range
// restore cursor
if (isAddressVisible(fSelectedAddress) && findAddressIndex(fSelectedAddress) != -1)
{
getTopVisibleAddress();
getTopVisibleAddress().add(BigInteger.valueOf(getBytesPerLine()*getNumberOfVisibleLines()));
// if the cursor is not visible but in buffered range
// updating and showing the cursor will move the top index of the table
updateCursorPosition();
int newIdx = findAddressIndex(getTopVisibleAddress());
if (newIdx != topIdx && topIdx != -1)
{
table.setTopIndex(topIdx);
}
if (isAddressVisible(fSelectedAddress))
{
fCursorManager.showCursor();
}
else
{
fCursorManager.hideCursor();
}
}
else
{
fCursorManager.hideCursor();
}
}
// try to display the table every time it's reloaded
displayTable();
}
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)
{
MemoryViewLine line = (MemoryViewLine)items[i].getData();
BigInteger lineAddress = new BigInteger(line.getAddress(), 16);
BigInteger endLineAddress = lineAddress.add(BigInteger.valueOf(getBytesPerLine()));
if (lineAddress.compareTo(address) <= 0 && endLineAddress.compareTo(address) > 0)
{
return i;
}
}
}
return -1;
}
/**
* Update cursor position based on selected address.
* @return true if cursor is visible, false otherwise
*/
private boolean updateCursorPosition()
{
// selected address is out of range, simply return false
if (fSelectedAddress.compareTo(contentProvider.getBufferTopAddress()) < 0)
return false;
// calculate selected row address
int numOfRows = fSelectedAddress.subtract(contentProvider.getBufferTopAddress()).intValue()/getBytesPerLine();
BigInteger rowAddress = contentProvider.getBufferTopAddress().add(BigInteger.valueOf(numOfRows * getBytesPerLine()));
// try to find the row of the selected address
int row = findAddressIndex(fSelectedAddress);
if (row == -1)
{
return false;
}
// calculate offset to the row address
BigInteger offset = fSelectedAddress.subtract(rowAddress);
// locate column
int col = ((offset.intValue()/getColumnSize())+1);
// setting cursor selection or table selection changes
// the top index of the table... and may mess up top index in the talbe
// save up old top index
int oldTop = fTableViewer.getTable().getTopIndex();
// update cursor position and table selection
fCursorManager.updateCursorPosition(row, col, isAddressVisible(fSelectedAddress));
updateTableSelection();
// reset top index to make sure the table is not moved
fTableViewer.getTable().setTopIndex(oldTop);
if (isAddressVisible(fSelectedAddress))
{
fCursorManager.showCursor();
fTableViewer.getTable().deselectAll();
}
else
fCursorManager.hideCursor();
return true;
}
/* (non-Javadoc)
* @see org.eclipse.debug.ui.ITableMemoryViewTab#getNumberOfVisibleLines()
*/
public 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;
}
// 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 = table.getItemHeight();
int numberOfLines = height/lineHeight;
return numberOfLines;
}
/* (non-Javadoc)
* @see org.eclipse.debug.ui.IMemoryViewTab#refresh()
*/
public void refresh()
{
try {
// refresh at start address of this memory block
// address may change if expression is evaluated to a different value
IMemoryBlock mem = fMemoryBlock;
BigInteger address;
if (mem instanceof IExtendedMemoryBlock)
{
address = ((IExtendedMemoryBlock)mem).getBigBaseAddress();
if (address == null)
{
DebugException e = new DebugException(DebugUIPlugin.newErrorStatus(DebugUIMessages.getString(UNABLE_TO_GET_BASE_ADDRESS), null));
displayError(e);
return;
}
setTabName(mem, true);
// base address has changed
if (address.compareTo(contentProvider.getContentBaseAddress()) != 0)
{
// get to new address
reloadTable(address, true);
}
else
{
// reload at top of table
// address = contentProvider.getBufferTopAddress().add(BigInteger.valueOf(getBytesPerLine()*TABLE_PREBUFFER));
address = getTopVisibleAddress();
reloadTable(address, true);
}
}
else
{
address = BigInteger.valueOf(mem.getStartAddress());
reloadTable(address, true);
}
if (isAddressVisible(fSelectedAddress))
{
// redraw cursors if cursor is visible
getCursorManager().redrawCursors();
}
} catch (DebugException e) {
displayError(e);
DebugUIPlugin.log(e.getStatus());
}
}
/**
* Handle resize of the table.
*/
private void resizeTable() {
if (!(fMemoryBlock instanceof IExtendedMemoryBlock))
return;
if (!isEnabled())
return;
Display.getDefault().syncExec(new Runnable()
{
public void run()
{
boolean reloaded = false;
// this code is is running on the UI thread with a delay
// The view tab may have been disposed when this actually gets executed.
if (fTableViewer == null)
return;
Table table = fTableViewer.getTable();
// make sure table is still valid
if (table.isDisposed())
return;
int topIndex = table.getTopIndex();
if (topIndex < 0)
{
return;
}
BigInteger oldTopAddress = getTopVisibleAddress();
if (oldTopAddress.compareTo(BigInteger.valueOf(32)) <= 0)
{
TABLE_PREBUFFER = 0;
}
else
{
TABLE_PREBUFFER =
oldTopAddress.divide(BigInteger.valueOf(32)).min(BigInteger.valueOf(TABLE_DEFAULTBUFFER)).intValue();
}
// check pre-condition before we can check on number of lines left in the table
if (table.getItemCount() > topIndex)
{
try
{
//if new window size exceeds the number of lines available in the table, reload the table
if (needMoreLines())
{
reloadTable(oldTopAddress, false);
reloaded = true;
}
if (oldTopAddress.compareTo(BigInteger.valueOf(96)) <= 0)
{
reloadTable(BigInteger.valueOf(0), false);
reloaded = true;
}
}
catch (DebugException e)
{
displayError(e);
DebugUIPlugin.log(e.getStatus());
}
}
if (!reloaded){
// if not reload, still need to update the cursor position
// since the position may change
updateCursorPosition();
fTableViewer.getTable().deselectAll();
if (!getTopVisibleAddress().equals(oldTopAddress))
{
int i = findAddressIndex(oldTopAddress);
if (i != -1)
fTableViewer.getTable().setTopIndex(i);
}
}
updateSyncTopAddress(true);
}
});
}
/**
* Handle scrollling and reload table if necessary
* @param event
*/
private void handleScrollBarSelection(SelectionEvent event)
{
if (!(fMemoryBlock instanceof IExtendedMemoryBlock))
{
// if not instance of extended memory block
// just get current top visible address and fire event
updateSyncTopAddress(true);
}
final SelectionEvent evt = event;
// Must run on UI Thread asynchronously
// Otherwise, another event could have been recevied before the reload is completed
Display.getDefault().asyncExec(new Runnable()
{
public void run()
{
try
{
switch (evt.detail)
{
case 0 : //the end of a drag
case SWT.END :
case SWT.PAGE_DOWN :
case SWT.ARROW_DOWN :
case SWT.HOME :
case SWT.PAGE_UP :
case SWT.ARROW_UP :
if (fMemoryBlock instanceof IExtendedMemoryBlock)
{
updateSyncTopAddress(true);
//if we are approaching the limits of the currently loaded memory, reload the table
if (needMoreLines())
{
BigInteger topAddress = getTopVisibleAddress();
//if we're near 0, just go there immediately (hard stop at 0, don't try to scroll/wrap)
if (topAddress.compareTo(BigInteger.valueOf(96)) <= 0)
{
if (topAddress.equals(BigInteger.valueOf(0)))
{
// do not reload if we are already at zero
break;
}
reloadTable(BigInteger.valueOf(0), false);
}
else
{
//otherwise, just load the next portion of the memory
reloadTable(topAddress, false);
}
}
}
if (isAddressVisible(fSelectedAddress))
{
updateCursorPosition();
fCursorManager.setCursorFocus();
}
break;
default:
break;
}
}
catch (DebugException e)
{
displayError(e);
DebugUIPlugin.log(e.getStatus());
}
}
});
}
/* (non-Javadoc)
* @see org.eclipse.debug.ui.IMemoryViewTab#setEnabled(boolean)
*/
public void setEnabled(boolean enable)
{
super.setEnabled(enable);
fEnabled = enable;
IMemoryBlock mem = fMemoryBlock;
if (fEnabled)
{
BigInteger oldBase = contentProvider.getContentBaseAddress();
// debug adapter may ignore the enable request
// some adapter does not block memory and may not do anything
// with the enable/disable request
// As a result, we need to force a refresh
// and to make sure content is updated
refresh();
if (mem instanceof IExtendedMemoryBlock)
{
BigInteger baseAddress = ((IExtendedMemoryBlock)mem).getBigBaseAddress();
if (baseAddress == null)
{
baseAddress = new BigInteger("0"); //$NON-NLS-1$
}
ArrayList references = (ArrayList)getSynchronizedProperty(IMemoryViewConstants.PROPERTY_ENABLED_REFERENCES);
// if the base address has changed, update cursor
// and this is the first time this memory block is enabled
if (!baseAddress.equals(oldBase) && references.size() == 1)
{
setSelectedAddress(baseAddress, true);
updateCursorPosition();
updateSyncTopAddress(true);
updateSyncSelectedAddress(true);
}
else
{
// otherwise, take synchronized settings
synchronize();
}
}
else
{
synchronize();
}
}
else
{
if (mem instanceof IExtendedMemoryBlock)
{
setTabName(mem, 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.
contentProvider.resetDeltas();
}
}
public boolean isEnabled()
{
return fEnabled;
}
/**
* Display an error in the view tab.
* Make use of the text viewer instead of the table viewer.
* @param e
*/
protected void displayError(DebugException e)
{
StyledText styleText = null;
errorOccurred = true;
if (fTextViewer == null)
{
// create text viewer
fTextViewer = new TextViewer(fTabItem.getParent(), SWT.NONE);
fTabItem.setControl(fTextViewer.getControl());
fTextViewer.setDocument(new Document());
styleText = fTextViewer.getTextWidget();
styleText.setEditable(false);
styleText.setEnabled(false);
}
else if (fTextViewer.getControl() != fTabItem.getControl())
{
// switch to text viewer
fTabItem.setControl(fTextViewer.getControl());
}
styleText = fTextViewer.getTextWidget();
if (styleText != null)
styleText.setText(DebugUIMessages.getString(ERROR) + e);
}
/* (non-Javadoc)
* @see org.eclipse.debug.ui.IMemoryViewTab#isDisplayingError()
*/
public boolean isDisplayingError()
{
if(fTextViewer == null)
return false;
if (fTabItem.getControl() == fTextViewer.getControl())
return true;
else
return false;
}
public void displayTable()
{
if (fTableViewer!= null && fTabItem.getControl() != fTableViewer.getControl())
{
errorOccurred = false;
fTabItem.setControl(fTableViewer.getControl());
}
}
/* (non-Javadoc)
* @see org.eclipse.swt.events.KeyListener#keyPressed(org.eclipse.swt.events.KeyEvent)
*/
public void keyPressed(KeyEvent e)
{
handleKeyPressed(e);
}
/* (non-Javadoc)
* @see org.eclipse.swt.events.KeyListener#keyReleased(org.eclipse.swt.events.KeyEvent)
*/
public void keyReleased(KeyEvent e)
{
}
/**
* @return cell editors for the table
*/
private CellEditor[] getCellEditors() {
Table table = fTableViewer.getTable();
fEditors = new CellEditor[table.getColumnCount()];
for (int i=0; i<fEditors.length; i++)
{
fEditors[i] = new TextCellEditor(table);
}
// combine the listener/validator interfaces so we can handle
// "editing" an address, which really skips the table to that address
// class CellValidatorListener implements ICellEditorValidator {
// TextCellEditor textEditor;
// boolean isAddressValidator;
//
// public CellValidatorListener(CellEditor cellEditor, boolean isAddress) {
// textEditor = (TextCellEditor)cellEditor;
// isAddressValidator = isAddress;
// }
//
// public String isValid(Object value) {
// if ((value instanceof String)) {
// if (((String)value).length() == 0)
// return null; //allow empty strings so hitting "delete" doesn't immediately pop up an error
//
// // make sure the character is 0-9ABCDEF only
// try {
// if (!isAddressValidator) { // don't validate address to allow input of variable in the address field
// BigInteger bigInt = new BigInteger((String)value, 16);
// }
// } catch (NumberFormatException ne) {
// return "not valid";
// }
// }
// return null;
// }
//
// }
//
// //"editing" an address skips the table to that address
//
// for (int i=0; i<fEditors.length; i++)
// {
// fEditors[i].setValidator(new CellValidatorListener(fEditors[i], true));
// }
return fEditors;
}
public TableViewer getTableViewer()
{
return fTableViewer;
}
protected ViewTabCursorManager getCursorManager()
{
return fCursorManager;
}
/**
* This function must be made synchronized.
* Otherwise, another thread could modify the selected address while it is being updated.
* It is the case when user scrolls up/down the table using arrow key.
* When the table reaches its limit, it is being reloaded. However, cursor receives a selection
* event and update the selected address at the same time. It messes up the selected address
* and causes cursor selection to behave unexpectedly.
* @param address
*/
synchronized protected void setSelectedAddress(BigInteger address, boolean updateSynchronizer)
{
fSelectedAddress = address;
updateSyncSelectedAddress(updateSynchronizer);
}
/**
* Return the offset from the base address of the memory block.
* @param memory
* @param lineAddress
* @param lineOffset
* @return
* TODO: this method is never called
*/
protected long getOffset(IMemoryBlock memory, String lineAddress, int lineOffset) {
BigInteger lineAddr = new BigInteger(lineAddress, 16);
BigInteger memoryAddr;
if (memory instanceof IExtendedMemoryBlock)
{
memoryAddr = ((IExtendedMemoryBlock)memory).getBigBaseAddress();
}
else
{
memoryAddr = BigInteger.valueOf(memory.getStartAddress());
}
if (memoryAddr == null)
memoryAddr = new BigInteger("0"); //$NON-NLS-1$
long offset = lineAddr.subtract(memoryAddr).longValue();
return offset + lineOffset;
}
/**
* Reset this view tab to the base address of the memory block
*/
public void resetAtBaseAddress() throws DebugException
{
try
{
IMemoryBlock mem = getMemoryBlock();
if (mem instanceof IExtendedMemoryBlock)
{
// if text editor is activated, removes its focus and commit
// any changes made
setCursorFocus();
// reload table at base address
BigInteger address = ((IExtendedMemoryBlock)mem).getBigBaseAddress();
if (address == null)
{
address = new BigInteger("0"); //$NON-NLS-1$
}
setSelectedAddress(address, true);
reloadTable(address, false);
// make sure cursor has focus when the user chooses to reset
setCursorFocus();
}
else
{
// go to top of the table
BigInteger address = BigInteger.valueOf(mem.getStartAddress());
setSelectedAddress(address, true);
getTableViewer().getTable().setTopIndex(0);
updateCursorPosition();
updateTableSelection();
setCursorFocus();
}
updateSyncTopAddress(true);
}
catch (DebugException e)
{
throw e;
}
}
/* (non-Javadoc)
* @see org.eclipse.debug.ui.IMemoryViewTab#goToAddress(java.math.BigInteger)
*/
public void goToAddress(BigInteger address) throws DebugException
{
goToAddress(address, true);
fCursorManager.setCursorFocus();
}
/**
* @param address
* @throws DebugException
*/
private void goToAddress(BigInteger address, boolean updateSynchronizer) throws DebugException {
try
{
// if address is within the range, highlight
if (!isAddressOutOfRange(address))
{
// Defer update so that top visible address is updated before
// the selected address
// This is to ensure that the other view tabs get the top
// visible address change events first in case the selected
// address is not already visible.
// If this is not done, the other view tab may not show selected address.
setSelectedAddress(address, false);
updateCursorPosition();
updateTableSelection();
// force the cursor to be shown
if (!isAddressVisible(fSelectedAddress))
{
int i = findAddressIndex(fSelectedAddress);
fTableViewer.getTable().showItem(fTableViewer.getTable().getItem(i));
getCursorManager().showCursor();
updateSyncTopAddress(updateSynchronizer);
}
// update selected address in synchronizer
updateSyncSelectedAddress(updateSynchronizer);
}
else
{
// if not extended memory block
// do not allow user to go to an address that's out of range
if (!(fMemoryBlock instanceof IExtendedMemoryBlock))
{
Status stat = new Status(
IStatus.ERROR, DebugUIPlugin.getUniqueIdentifier(),
DebugException.NOT_SUPPORTED, DebugUIMessages.getString(ADDRESS_IS_OUT_OF_RANGE), null
);
DebugException e = new DebugException(stat);
throw e;
}
setSelectedAddress(address, updateSynchronizer);
//otherwise, reload at the address
reloadTable(address, false);
updateSyncTopAddress(updateSynchronizer);
}
}
catch (DebugException e)
{
throw e;
}
}
/**
* @return
*/
public int getColumnSize()
{
return fColumnSize;
}
/**
* @return
*/
public int getBytesPerLine()
{
return fBytePerLine;
}
/* (non-Javadoc)
* @see org.eclipse.debug.ui.IMemoryViewTab#setFont(org.eclipse.swt.graphics.Font)
*/
public void setFont(Font font)
{
int oldIdx = fTableViewer.getTable().getTopIndex();
// BUG in table, if font is changed when table is not starting
// from the top, causes table gridline to be misaligned.
fTableViewer.getTable().setTopIndex(0);
// set font
fTableViewer.getTable().setFont(font);
fCursorManager.setFont(font);
fTableViewer.getTable().setTopIndex(oldIdx);
packColumns();
// update table cursor and force redraw
updateCursorPosition();
}
/**
* @return memory block presentation to allow for customization
*/
protected IMemoryBlockModelPresentation getMemoryBlockPresentation()
{
// only try to create a model presentation once
if (fMemoryBlockPresentation == null && !fNoPresentation)
{
// create model presentation for memory block
DelegatingModelPresentation presentation = new MemoryViewDelegatingModelPresentation();
String id = fMemoryBlock.getModelIdentifier();
fMemoryBlockPresentation = (MemoryViewLazyModelPresentation)presentation.getPresentation(id);
// if a memory block presentation cannot be retrieved
if (fMemoryBlockPresentation == null)
fNoPresentation = true;
}
return fMemoryBlockPresentation;
}
/* (non-Javadoc)
* @see org.eclipse.debug.ui.IMemoryViewTab#setTabLabel(java.lang.String)
*/
public void setTabLabel(String label)
{
if (label != null)
fTabItem.setText(label);
}
/* (non-Javadoc)
* @see org.eclipse.debug.ui.IMemoryViewTab#getTabLabel()
*/
public String getTabLabel()
{
if (fTabItem != null)
return fTabItem.getText();
else
return null;
}
/* (non-Javadoc)
* @see org.eclipse.debug.ui.IMemoryViewTab#getRenderingId()
*/
public String getRenderingId()
{
return fRenderingId;
}
/**
* Handle column size changed event from synchronizer
* @param newColumnSize
*/
private void columnSizeChanged(final int newColumnSize)
{
// ignore event if view tab is disabled
if (!isEnabled())
return;
Display.getDefault().asyncExec(new Runnable()
{
public void run()
{
format(16, newColumnSize);
}
});
}
/* (non-Javadoc)
* @see org.eclipse.debug.ui.ISynchronizedMemoryBlockView#scrollBarSelectionChanged(int)
*/
public void scrollBarSelectionChanged(int newSelection)
{
}
/**
* Handle selected address change event from synchronizer
* @param address
*/
private void selectedAddressChanged(final BigInteger address)
{
// ignore event if view tab is disabled
if (!isEnabled())
{
return;
}
try
{
if (!fSelectedAddress.equals(address))
{
if (getMemoryBlock() instanceof IExtendedMemoryBlock)
{
goToAddress(address, false);
}
else
{
if (!isAddressOutOfRange(address))
{
goToAddress(address, false);
}
}
}
}
catch (DebugException e)
{
displayError(e);
}
}
/**
* Handle top visible address change event from synchronizer
* @param address
*/
private void topVisibleAddressChanged(final BigInteger address)
{
try
{
// do not handle event if view tab is disabled
if (!isEnabled())
return;
if (!address.equals(getTopVisibleAddress()))
{
if (getMemoryBlock() instanceof IExtendedMemoryBlock)
{
if (!isAddressOutOfRange(address))
{
int index = -1;
// within buffer range, just set top index
Table table = getTableViewer().getTable();
for (int i = 0; i < table.getItemCount(); i++)
{
MemoryViewLine line = (MemoryViewLine) table.getItem(i).getData();
if (line != null)
{
BigInteger lineAddress = new BigInteger(line.getAddress(), 16);
if (lineAddress.equals(address))
{
index = i;
break;
}
}
}
if (index >= 3 && table.getItemCount() - (index+getNumberOfVisibleLines()) >= 3)
{
// update cursor position
table.setTopIndex(index);
if (!isAddressVisible(fSelectedAddress))
{
fCursorManager.hideCursor();
}
else
{
updateCursorPosition();
updateTableSelection();
table.setTopIndex(index);
// BUG 64831: to get around SWT problem with
// the table cursor not painted properly after
// table.setTopIndex is called
fCursorManager.getLeadCursor().setVisible(false);
fCursorManager.getLeadCursor().setVisible(true);
}
}
else
{
// approaching limit, reload table
reloadTable(address, false);
}
}
else
{
// approaching limit, reload table
reloadTable(address, false);
}
}
else
{
// IMemoryBlock support
int index = -1;
// within buffer range, just set top index
Table table = getTableViewer().getTable();
for (int i = 0; i < table.getItemCount(); i++)
{
MemoryViewLine line = (MemoryViewLine) table.getItem(i).getData();
if (line != null)
{
BigInteger lineAddress = new BigInteger(line.getAddress(), 16);
if (lineAddress.equals(address))
{
index = i;
break;
}
}
}
if (index >= 0)
{
table.setTopIndex(index);
if (!isAddressVisible(fSelectedAddress))
{
fCursorManager.hideCursor();
}
else
{
updateCursorPosition();
updateTableSelection();
table.setTopIndex(index);
// BUG 64831: to get around SWT problem with
// the table cursor not painted properly after
// table.setTopIndex is called
fCursorManager.getLeadCursor().setVisible(false);
fCursorManager.getLeadCursor().setVisible(true);
}
}
}
}
}
catch (DebugException e)
{
displayError(e);
}
}
/**
* Check if address provided is out of buffered range
* @param address
* @return
*/
private boolean isAddressOutOfRange(BigInteger address)
{
return contentProvider.isAddressOutOfRange(address);
}
/**
* Check if address is visible
* @param address
* @return
*/
protected boolean isAddressVisible(BigInteger address)
{
// if view tab is not yet created
// cursor should always be visible
if (!fTabCreated)
return true;
BigInteger topVisible = getTopVisibleAddress();
BigInteger lastVisible = getTopVisibleAddress().add(BigInteger.valueOf((getNumberOfVisibleLines()) * getBytesPerLine() + getBytesPerLine()));
if (topVisible.compareTo(address) <= 0 && lastVisible.compareTo(address) > 0)
{
return true;
}
else
{
return false;
}
}
/**
* Get properties from synchronizer and synchronize settings
*/
private void synchronize()
{
Integer columnSize = (Integer) getSynchronizedProperty(IMemoryViewConstants.PROPERTY_COL_SIZE);
BigInteger selectedAddress = (BigInteger)getSynchronizedProperty(IMemoryViewConstants.PROPERTY_SELECTED_ADDRESS);
BigInteger topAddress = (BigInteger)getSynchronizedProperty(IMemoryViewConstants.PROPERTY_TOP_ADDRESS);
if (columnSize != null)
{
int colSize = columnSize.intValue();
if (colSize > 0 && colSize != fColumnSize)
{
columnSizeChanged(colSize);
}
}
if (topAddress != null)
{
if (!topAddress.equals(getTopVisibleAddress()))
{
if (!fSelectedAddress.equals(selectedAddress))
{
setSelectedAddress(selectedAddress, true);
}
topVisibleAddressChanged(topAddress);
}
}
if (selectedAddress != null)
{
if (selectedAddress.compareTo(fSelectedAddress) != 0)
{
selectedAddressChanged(selectedAddress);
}
}
}
/* (non-Javadoc)
* @see org.eclipse.debug.ui.ISynchronizedMemoryBlockView#propertyChanged(java.lang.String, java.lang.Object)
*/
public void propertyChanged(String propertyName, Object value)
{
if (isDisplayingError())
return;
if (propertyName.equals(IMemoryViewConstants.PROPERTY_SELECTED_ADDRESS) && value instanceof BigInteger)
{
try {
if (needMoreLines())
{
reloadTable(getTopVisibleAddress(), false);
}
} catch (DebugException e) {
displayError(e);
}
selectedAddressChanged((BigInteger)value);
}
else if (propertyName.equals(IMemoryViewConstants.PROPERTY_COL_SIZE) && value instanceof Integer)
{
columnSizeChanged(((Integer)value).intValue());
}
else if (propertyName.equals(IMemoryViewConstants.PROPERTY_TOP_ADDRESS) && value instanceof BigInteger)
{
try {
if (needMoreLines())
{
reloadTable(getTopVisibleAddress(), false);
}
} catch (DebugException e) {
displayError(e);
}
topVisibleAddressChanged((BigInteger)value);
return;
}
}
/* (non-Javadoc)
* @see org.eclipse.debug.ui.ISynchronizedMemoryBlockView#getProperty(java.lang.String)
*/
public Object getProperty(String propertyId)
{
if (propertyId.equals(IMemoryViewConstants.PROPERTY_SELECTED_ADDRESS))
{
return fSelectedAddress;
}
else if (propertyId.equals(IMemoryViewConstants.PROPERTY_COL_SIZE))
{
return new Integer(fColumnSize);
}
else if (propertyId.equals(IMemoryViewConstants.PROPERTY_TOP_ADDRESS))
{
return getTopVisibleAddress();
}
return null;
}
/* (non-Javadoc)
* @see org.eclipse.debug.ui.IMemoryViewTab#getSelectedAddress()
*/
public BigInteger getSelectedAddress() {
return fSelectedAddress;
}
/* (non-Javadoc)
* @see org.eclipse.debug.ui.IMemoryViewTab#getSelectedContent()
*/
public String getSelectedContent() {
// check precondition
if (fCursorManager.fCol == 0 || fCursorManager.fCol > getBytesPerLine()/getColumnSize())
{
return ""; //$NON-NLS-1$
}
TableItem tableItem = getTableViewer().getTable().getItem(fCursorManager.fRow);
return tableItem.getText(fCursorManager.fCol);
}
/**
* Update labels in the view tab
*/
protected void updateLabels()
{
// update tab labels
setTabName(getMemoryBlock(), true);
if (fTableViewer != null)
{
// update column labels
setColumnHeadings();
refreshTableViewer();
}
}
protected boolean needMoreLines()
{
if (getMemoryBlock() instanceof IExtendedMemoryBlock)
{
Table table = fTableViewer.getTable();
TableItem firstItem = table.getItem(0);
TableItem lastItem = table.getItem(table.getItemCount()-1);
if (firstItem == null || lastItem == null)
return true;
MemoryViewLine first = (MemoryViewLine)firstItem.getData();
MemoryViewLine last = (MemoryViewLine) 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);
lastAddress = lastAddress.add(BigInteger.valueOf(getBytesPerLine()));
BigInteger topVisibleAddress = getTopVisibleAddress();
long numVisibleLines = getNumberOfVisibleLines();
long numOfBytes = numVisibleLines * getBytesPerLine();
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(getBytesPerLine()));
if (numTopLine.compareTo(BigInteger.valueOf(3)) <= 0)
return true;
// if there are only 3 lines left at the bottom, refresh
BigInteger numBottomLine = lastAddress.subtract(lastVisibleAddrss).divide(BigInteger.valueOf(getBytesPerLine()));
if (numBottomLine.compareTo(BigInteger.valueOf(3)) <= 0)
{
return true;
}
return false;
}
else
{
return false;
}
}
private Object getSynchronizedProperty(String propertyId)
{
return getMemoryBlockViewSynchronizer().getSynchronizedProperty(getMemoryBlock(), propertyId);
}
/**
* Checks to see if the event is valid for activating
* cell editing in a view tab
* @param event
* @return
*/
public boolean isValidEditEvent(int event) {
for (int i = 0; i < MemoryViewTab.ignoreEvents.length; i++) {
if (event == MemoryViewTab.ignoreEvents[i])
return false;
}
return true;
}
private IMemoryBlockViewSynchronizer getMemoryBlockViewSynchronizer() {
return DebugUIPlugin.getDefault().getMemoryBlockViewSynchronizer();
}
}