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