diff options
Diffstat (limited to 'memory/org.eclipse.cdt.debug.ui.memory.floatingpoint/src/org/eclipse/cdt/debug/ui/memory/floatingpoint/Rendering.java')
-rw-r--r-- | memory/org.eclipse.cdt.debug.ui.memory.floatingpoint/src/org/eclipse/cdt/debug/ui/memory/floatingpoint/Rendering.java | 2297 |
1 files changed, 2297 insertions, 0 deletions
diff --git a/memory/org.eclipse.cdt.debug.ui.memory.floatingpoint/src/org/eclipse/cdt/debug/ui/memory/floatingpoint/Rendering.java b/memory/org.eclipse.cdt.debug.ui.memory.floatingpoint/src/org/eclipse/cdt/debug/ui/memory/floatingpoint/Rendering.java new file mode 100644 index 00000000000..bf6ba9cdecd --- /dev/null +++ b/memory/org.eclipse.cdt.debug.ui.memory.floatingpoint/src/org/eclipse/cdt/debug/ui/memory/floatingpoint/Rendering.java @@ -0,0 +1,2297 @@ +/******************************************************************************* + * Copyright (c) 2006, 2010, 2012 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ted R Williams (Wind River Systems, Inc.) - initial implementation + * Randy Rohrbach (Wind River Systems, Inc.) - Copied and modified to create the floating point plugin + *******************************************************************************/ + +package org.eclipse.cdt.debug.ui.memory.floatingpoint; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.MathContext; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Set; +import java.util.Vector; + +import org.eclipse.cdt.debug.ui.memory.floatingpoint.FPutilities.FPDataType; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugEvent; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.IDebugEventSetListener; +import org.eclipse.debug.core.model.IDebugElement; +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.ui.IInternalDebugUIConstants; +import org.eclipse.debug.internal.ui.views.memory.MemoryViewUtil; +import org.eclipse.debug.internal.ui.views.memory.renderings.GoToAddressComposite; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.SWT; +import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; +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.PaintEvent; +import org.eclipse.swt.events.PaintListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.Point; +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.Layout; +import org.eclipse.swt.widgets.ScrollBar; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.IViewPart; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.progress.UIJob; + + +@SuppressWarnings("restriction") +public class Rendering extends Composite implements IDebugEventSetListener +{ + // The IMemoryRendering parent + + private FPRendering fParent; + + // Controls + + protected FPAddressPane fAddressPane; + protected FPDataPane fDataPane; + private GoToAddressComposite fAddressBar; + protected Control fAddressBarControl; + private Selection fSelection = new Selection(); + + // Internal management + + BigInteger fViewportAddress = null; // Default visibility for performance + BigInteger fMemoryBlockStartAddress = null; // Starting address + BigInteger fMemoryBlockEndAddress = null; // Ending address + protected BigInteger fBaseAddress = null; // Base address + protected int fColumnCount = 0; // Auto calculate can be disabled by user, making this user settable + protected int fBytesPerRow = 0; // Number of bytes per row are displayed + int fScrollSelection = 0; // Scroll selection + private BigInteger fCaretAddress = null; // Caret/cursor position + private boolean fCellEditState = false; // Cell editing mode: 'true' = currently editing a cell; 'false' = not editing a cell + private BigInteger cellEditAddress = null; // The address of the cell currently being edited + private BigInteger memoryAddress = null; // The memory address associated with the cell that currently being edited + private StringBuffer fEditBuffer = null; // Character buffer used during editing + static boolean initialDisplayModeSet = false; // Initial display mode been set to the same endianness as the target + + // Constants used to identify the panes + + public final static int PANE_ADDRESS = 1; + public final static int PANE_DATA = 2; + + // Decimal precision used when converting between scroll units and number of memory + // rows. Calculations do not need to be exact; two decimal places is good enough. + + static private final MathContext SCROLL_CONVERSION_PRECISION = new MathContext(2); + + // Constants used to identify text, maybe java should be queried for all available sets + + public final static int TEXT_ISO_8859_1 = 1; + public final static int TEXT_USASCII = 2; + public final static int TEXT_UTF8 = 3; + protected final static int TEXT_UTF16 = 4; + + // Internal constants + + public final static int COLUMNS_AUTO_SIZE_TO_FIT = 0; + + // View internal settings + + private int fCellPadding = 2; + private int fPaneSpacing = 16; + private String fPaddingString = " "; //$NON-NLS-1$ + + // Flag whether the memory cache is dirty + + private boolean fCacheDirty = false; + + // Update modes + + public final static int UPDATE_ALWAYS = 1; + public final static int UPDATE_ON_BREAKPOINT = 2; + public final static int UPDATE_MANUAL = 3; + public int fUpdateMode = UPDATE_ALWAYS; + + // Constants for cell-width calculations + + private static final int DECIMAL_POINT_SIZE = 1; + private static final int SIGN_SIZE = 1; + private static final int EXPONENT_CHARACTER_SIZE = 1; + private static final int EXPONENT_VALUE_SIZE = 3; + + // User settings + + private FPDataType fFPDataType = FPDataType.FLOAT; // Default to float data type + private int fDisplayedPrecision = 8; // The default number of digits of displayed precision + private int fCharsPerColumn = charsPerColumn(); // Figure out the initial cell-width size + private int fColumnsSetting = COLUMNS_AUTO_SIZE_TO_FIT; // Default column setting + private boolean fIsTargetLittleEndian = true; // Default target endian setting + private boolean fIsDisplayLittleEndian = true; // Default display endian setting + private boolean fEditInserMode = false; // Insert mode: true = replace existing number, false = overstrike + + // Constructors + + public Rendering(Composite parent, FPRendering renderingParent) + { + super(parent, SWT.DOUBLE_BUFFERED | SWT.NO_BACKGROUND | SWT.H_SCROLL | SWT.V_SCROLL); + this.setFont(JFaceResources.getFont(IInternalDebugUIConstants.FONT_NAME)); // TODO: internal? + this.fParent = renderingParent; + + // Initialize the viewport start + + if (fParent.getMemoryBlock() != null) + { + fViewportAddress = fParent.getMemoryBlockStartAddress(); + + // The viewport address will be null if memory may be retrieved at any + // address less than this memory block's base. If so use the base address. + + if (fViewportAddress == null) + fViewportAddress = fParent.getBigBaseAddress(); + + fBaseAddress = fViewportAddress; + } + + // Instantiate the panes, TODO default visibility from state or plugin.xml? + + this.fAddressPane = createAddressPane(); + this.fDataPane = createDataPane(); + + fAddressBar = new GoToAddressComposite(); + fAddressBarControl = fAddressBar.createControl(parent); + Button button = fAddressBar.getButton(IDialogConstants.OK_ID); + + if (button != null) + { + button.addSelectionListener(new SelectionAdapter() + { + + @Override + public void widgetSelected(SelectionEvent e) + { + doGoToAddress(); + } + }); + + button = fAddressBar.getButton(IDialogConstants.CANCEL_ID); + if (button != null) + { + button.addSelectionListener(new SelectionAdapter() + { + @Override + public void widgetSelected(SelectionEvent e) + { + setVisibleAddressBar(false); + } + }); + } + } + + fAddressBar.getExpressionWidget().addSelectionListener(new SelectionAdapter() + { + @Override + public void widgetDefaultSelected(SelectionEvent e) + { + doGoToAddress(); + } + }); + + fAddressBar.getExpressionWidget().addKeyListener(new KeyAdapter() + { + @Override + public void keyPressed(KeyEvent e) + { + if (e.keyCode == SWT.ESC) + setVisibleAddressBar(false); + super.keyPressed(e); + } + }); + + this.fAddressBarControl.setVisible(false); + + getHorizontalBar().addSelectionListener(createHorizontalBarSelectionListener()); + getVerticalBar().addSelectionListener(createVerticalBarSelectinListener()); + + this.addPaintListener(new PaintListener() + { + @Override + public void paintControl(PaintEvent pe) + { + pe.gc.setBackground(Rendering.this.getFPRendering().getColorBackground()); + pe.gc.fillRectangle(0, 0, Rendering.this.getBounds().width, Rendering.this.getBounds().height); + } + }); + + setLayout(); + + this.addControlListener(new ControlListener() + { + @Override + public void controlMoved(ControlEvent ce) + { + } + + @Override + public void controlResized(ControlEvent ce) + { + packColumns(); + } + }); + + DebugPlugin.getDefault().addDebugEventListener(this); + } + + // Determine how many characters are allowed in the column + + private int charsPerColumn() + { + return fDisplayedPrecision + DECIMAL_POINT_SIZE + SIGN_SIZE + EXPONENT_CHARACTER_SIZE + EXPONENT_VALUE_SIZE + fCellPadding; + } + + // Establish the visible layout of the view + + protected void setLayout() + { + this.setLayout(new Layout() + { + @Override + public void layout(Composite composite, boolean changed) + { + int xOffset = 0; + + if (Rendering.this.getHorizontalBar().isVisible()) + xOffset = Rendering.this.getHorizontalBar().getSelection(); + + int x = xOffset * -1; + int y = 0; + + if (fAddressBarControl.isVisible()) + { + fAddressBarControl.setBounds(0, 0, Rendering.this.getBounds().width, fAddressBarControl.computeSize(100, 30).y); // FIXME + // y = fAddressBarControl.getBounds().height; + } + + if (fAddressPane.isPaneVisible()) + { + fAddressPane.setBounds(x, y, fAddressPane.computeSize(0, 0).x, Rendering.this.getBounds().height - y); + x = fAddressPane.getBounds().x + fAddressPane.getBounds().width; + } + + if (fDataPane.isPaneVisible()) + { + fDataPane.setBounds(x, y, fDataPane.computeSize(0, 0).x, Rendering.this.getBounds().height - y); + x = fDataPane.getBounds().x + fDataPane.getBounds().width; + } + + ScrollBar horizontal = Rendering.this.getHorizontalBar(); + + horizontal.setVisible(true); + horizontal.setMinimum(0); + horizontal.setMaximum(fDataPane.getBounds().x + fDataPane.getBounds().width + xOffset); + @SuppressWarnings("unused") + int temp = horizontal.getMaximum(); + horizontal.setThumb(getClientArea().width); + horizontal.setPageIncrement(40); // TODO ? + horizontal.setIncrement(20); // TODO ? + } + + @Override + protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) + { + return new Point(100, 100); // dummy data + } + }); + } + + // Handles for the caret/cursor movement keys + + protected void handleDownArrow() + { + fViewportAddress = fViewportAddress.add(BigInteger.valueOf(getAddressableCellsPerRow())); + ensureViewportAddressDisplayable(); + redrawPanes(); + } + + protected void handleUpArrow() + { + fViewportAddress = fViewportAddress.subtract(BigInteger.valueOf(getAddressableCellsPerRow())); + ensureViewportAddressDisplayable(); + redrawPanes(); + } + + protected void handlePageDown() + { + fViewportAddress = fViewportAddress.add(BigInteger.valueOf(getAddressableCellsPerRow() * (Rendering.this.getRowCount() - 1))); + ensureViewportAddressDisplayable(); + redrawPanes(); + } + + protected void handlePageUp() + { + fViewportAddress = fViewportAddress.subtract(BigInteger.valueOf(getAddressableCellsPerRow() * (Rendering.this.getRowCount() - 1))); + ensureViewportAddressDisplayable(); + redrawPanes(); + } + + protected SelectionListener createHorizontalBarSelectionListener() + { + return new SelectionListener() + { + @Override + public void widgetSelected(SelectionEvent se) + { + Rendering.this.layout(); + } + + @Override + public void widgetDefaultSelected(SelectionEvent se) + { + // Do nothing + } + }; + } + + protected SelectionListener createVerticalBarSelectinListener() + { + return new SelectionListener() + { + @Override + public void widgetSelected(SelectionEvent se) + { + switch (se.detail) + { + case SWT.ARROW_DOWN: + handleDownArrow(); + break; + + case SWT.PAGE_DOWN: + handlePageDown(); + break; + + case SWT.ARROW_UP: + handleUpArrow(); + break; + + case SWT.PAGE_UP: + handlePageUp(); + break; + + case SWT.SCROLL_LINE: + // See: BUG 203068 selection event details broken on GTK < 2.6 + + default: + { + if (getVerticalBar().getSelection() == getVerticalBar().getMinimum()) + { + // Set view port start address to the start address of the Memory Block + fViewportAddress = Rendering.this.getMemoryBlockStartAddress(); + } + else if (getVerticalBar().getSelection() == getVerticalBar().getMaximum()) + { + // The view port end address should be less or equal to the the end address of the Memory Block + // Set view port address to be bigger than the end address of the Memory Block for now + // and let ensureViewportAddressDisplayable() to figure out the correct view port start address + fViewportAddress = Rendering.this.getMemoryBlockEndAddress(); + } + else + { + // Figure out the delta, ignore events with no delta + int deltaScroll = getVerticalBar().getSelection() - fScrollSelection; + if (deltaScroll == 0) break; + BigInteger deltaRows = scrollbar2rows(deltaScroll); + BigInteger newAddress = fViewportAddress.add(BigInteger.valueOf(getAddressableCellsPerRow()).multiply(deltaRows)); + fViewportAddress = newAddress; + } + + ensureViewportAddressDisplayable(); + + // Update tooltip; FIXME conversion from slider to scrollbar + // getVerticalBar().setToolTipText(Rendering.this.getAddressString(fViewportAddress)); + + // Update the addresses in the Address pane. + + if (fAddressPane.isPaneVisible()) + fAddressPane.redraw(); + + redrawPanes(); + + break; + } + } + } + + @Override + public void widgetDefaultSelected(SelectionEvent se) + { + // do nothing + } + }; + } + + protected FPAddressPane createAddressPane() + { + return new FPAddressPane(this); + } + + protected FPDataPane createDataPane() + { + return new FPDataPane(this); + } + + public FPRendering getFPRendering() // TODO rename + { + return fParent; + } + + protected void setCaretAddress(BigInteger address) + { + fCaretAddress = address; + } + + protected BigInteger getCaretAddress() + { + // Return the caret address if it has been set. Otherwise return the viewport address. + // When the rendering is first created, the caret is unset until the user clicks somewhere + // in the rendering. It also reset (unset) when the user gives us a new viewport address + + return (fCaretAddress != null) ? fCaretAddress : fViewportAddress; + } + + void doGoToAddress() + { + try + { + BigInteger address = fAddressBar.getGoToAddress(this.getMemoryBlockStartAddress(), this.getCaretAddress()); + getFPRendering().gotoAddress(address); + setVisibleAddressBar(false); + } + catch (NumberFormatException e1) + { + // FIXME log? + } + } + + // Ensure that all addresses displayed are within the addressable range + protected void ensureViewportAddressDisplayable() + { + if (fViewportAddress.compareTo(Rendering.this.getMemoryBlockStartAddress()) < 0) + { + fViewportAddress = Rendering.this.getMemoryBlockStartAddress(); + } + else if (getViewportEndAddress().compareTo(getMemoryBlockEndAddress().add(BigInteger.ONE)) > 0) + { + fViewportAddress = getMemoryBlockEndAddress().subtract(BigInteger.valueOf(getAddressableCellsPerRow() * getRowCount() - 1)); + } + + setScrollSelection(); + } + + public FPIMemorySelection getSelection() + { + return fSelection; + } + + protected int getHistoryDepth() + { + return fViewportCache.getHistoryDepth(); + } + + protected void setHistoryDepth(int depth) + { + fViewportCache.setHistoryDepth(depth); + } + + public void logError(String message, Exception e) + { + Status status = new Status(IStatus.ERROR, fParent.getRenderingId(), DebugException.INTERNAL_ERROR, message, e); + FPRenderingPlugin.getDefault().getLog().log(status); + } + + public void handleFontPreferenceChange(Font font) + { + setFont(font); + + Control controls[] = this.getRenderingPanes(); + for (int index = 0; index < controls.length; index++) + controls[index].setFont(font); + + packColumns(); + layout(true); + } + + public void setPaddingString(String padding) + { + fPaddingString = padding; + refresh(); + } + + public char getPaddingCharacter() + { + return fPaddingString.charAt(0); // use only the first character + } + + static int suspendCount = 0; + + @Override + public void handleDebugEvents(DebugEvent[] events) + { + if (this.isDisposed()) return; + + boolean isChangeOnly = false; + boolean isSuspend = false; + boolean isBreakpointHit = false; + + for (int index = 0; index < events.length; index++) + { + if (events[0].getSource() instanceof IDebugElement) + { + final int kind = events[index].getKind(); + final int detail = events[index].getDetail(); + final IDebugElement source = (IDebugElement) events[index].getSource(); + + /* + * We have to make sure we are comparing memory blocks here. It pretty much is now the + * case that the IDebugTarget is always null. Almost no one in the Embedded Space is + * using anything but CDT/DSF or CDT/TCF at this point. The older CDI stuff will still + * be using the old Debug Model API. But this will generate the same memory block and + * a legitimate IDebugTarget which will match properly. + */ + if( source.equals( getMemoryBlock() ) && source.getDebugTarget() == getMemoryBlock().getDebugTarget() ) + { + if ((detail & DebugEvent.BREAKPOINT) != 0) isBreakpointHit = true; + + if (kind == DebugEvent.SUSPEND) + { + handleSuspendEvent(detail); + isSuspend = true; + } + else if (kind == DebugEvent.CHANGE) + { + handleChangeEvent(); + isChangeOnly = true; + } + } + } + } + + if (isSuspend) + handleSuspend(isBreakpointHit); + else if (isChangeOnly) + handleChange(); + } + + protected void handleSuspend(boolean isBreakpointHit) + { + if (getUpdateMode() == UPDATE_ALWAYS || (getUpdateMode() == UPDATE_ON_BREAKPOINT && isBreakpointHit)) + { + Display.getDefault().asyncExec(new Runnable() + { + @Override + public void run() + { + archiveDeltas(); + refresh(); + } + }); + } + } + + protected void handleChange() + { + if (getUpdateMode() == UPDATE_ALWAYS) + { + Display.getDefault().asyncExec(new Runnable() + { + @Override + public void run() + { + refresh(); + } + }); + } + } + + protected void handleSuspendEvent(int detail) + { + } + + protected void handleChangeEvent() + { + } + + // Return true to enable development debug print statements + + public boolean isDebug() + { + return false; + } + + protected IMemoryBlockExtension getMemoryBlock() + { + IMemoryBlock block = fParent.getMemoryBlock(); + if (block != null) + return (IMemoryBlockExtension) block.getAdapter(IMemoryBlockExtension.class); + + return null; + } + + public BigInteger getBigBaseAddress() + { + return fParent.getBigBaseAddress(); + } + + public int getAddressableSize() + { + return fParent.getAddressableSize(); + } + + protected FPIViewportCache getViewportCache() + { + return fViewportCache; + } + + public FPMemoryByte[] getBytes(BigInteger address, int bytes) throws DebugException + { + return getViewportCache().getBytes(address, bytes); + } + + // Default visibility for performance + + ViewportCache fViewportCache = new ViewportCache(); + + private interface Request + { + } + + class ViewportCache extends Thread implements FPIViewportCache + { + class ArchiveDeltas implements Request + { + } + + class AddressPair implements Request + { + BigInteger startAddress; + BigInteger endAddress; + + public AddressPair(BigInteger start, BigInteger end) + { + startAddress = start; + endAddress = end; + } + + @Override + public boolean equals(Object obj) + { + if (obj == null) + return false; + if (obj instanceof AddressPair) + { + return ((AddressPair) obj).startAddress.equals(startAddress) && ((AddressPair) obj).endAddress.equals(endAddress); + } + + return false; + } + + @Override + public int hashCode() { + return super.hashCode() + startAddress.hashCode() + endAddress.hashCode(); + } + + } + + class MemoryUnit implements Cloneable + { + BigInteger start; + + BigInteger end; + + FPMemoryByte[] bytes; + + @Override + public MemoryUnit clone() + { + MemoryUnit b = new MemoryUnit(); + + b.start = this.start; + b.end = this.end; + b.bytes = new FPMemoryByte[this.bytes.length]; + for (int index = 0; index < this.bytes.length; index++) + b.bytes[index] = new FPMemoryByte(this.bytes[index].getValue()); + + return b; + } + + public boolean isValid() + { + return this.start != null && this.end != null && this.bytes != null; + } + } + + @SuppressWarnings("hiding") + private HashMap<BigInteger, FPMemoryByte[]> fEditBuffer = new HashMap<BigInteger, FPMemoryByte[]>(); + private boolean fDisposed = false; + private Object fLastQueued = null; + private Vector<Object> fQueue = new Vector<Object>(); + protected MemoryUnit fCache = null; + protected MemoryUnit fHistoryCache[] = new MemoryUnit[0]; + protected int fHistoryDepth = 0; + + public ViewportCache() + { + start(); + } + + @Override + public void dispose() + { + fDisposed = true; + synchronized (fQueue) + { + fQueue.notify(); + } + } + + public int getHistoryDepth() + { + return fHistoryDepth; + } + + public void setHistoryDepth(int depth) + { + fHistoryDepth = depth; + fHistoryCache = new MemoryUnit[fHistoryDepth]; + } + + @Override + public void refresh() + { + assert Thread.currentThread().equals(Display.getDefault().getThread()) : FPRenderingMessages.getString("CALLED_ON_NON_DISPATCH_THREAD"); //$NON-NLS-1$ + + if (fCache != null) + { + queueRequest(fViewportAddress, getViewportEndAddress()); + } + } + + @Override + public void archiveDeltas() + { + assert Thread.currentThread().equals(Display.getDefault().getThread()) : FPRenderingMessages.getString("CALLED_ON_NON_DISPATCH_THREAD"); //$NON-NLS-1$ + + if (fCache != null) + { + queueRequestArchiveDeltas(); + } + } + + private void queueRequest(BigInteger startAddress, BigInteger endAddress) + { + AddressPair pair = new AddressPair(startAddress, endAddress); + queue(pair); + } + + private void queueRequestArchiveDeltas() + { + ArchiveDeltas archive = new ArchiveDeltas(); + queue(archive); + } + + private void queue(Object element) + { + synchronized (fQueue) + { + if (!(fQueue.size() > 0 && element.equals(fLastQueued))) + { + fQueue.addElement(element); + fLastQueued = element; + } + fQueue.notify(); + } + } + + @Override + public void run() + { + while (!fDisposed) + { + AddressPair pair = null; + boolean archiveDeltas = false; + synchronized (fQueue) + { + if (fQueue.size() > 0) + { + Request request = (Request) fQueue.elementAt(0); + Class<?> type = request.getClass(); + + while (fQueue.size() > 0 && type.isInstance(fQueue.elementAt(0))) + { + request = (Request) fQueue.elementAt(0); + fQueue.removeElementAt(0); + } + + if (request instanceof ArchiveDeltas) + archiveDeltas = true; + else if (request instanceof AddressPair) + pair = (AddressPair) request; + } + } + + if (archiveDeltas) + { + for (int i = fViewportCache.getHistoryDepth() - 1; i > 0; i--) + fHistoryCache[i] = fHistoryCache[i - 1]; + + fHistoryCache[0] = fCache.clone(); + } + else if (pair != null) + { + populateCache(pair.startAddress, pair.endAddress); + } + else + { + synchronized (fQueue) + { + try + { + if (fQueue.isEmpty()) + { + fQueue.wait(); + } + } catch (Exception e) + { + // do nothing + } + } + } + } + } + + // Cache memory necessary to paint viewport + // TODO: user setting to buffer +/- x lines + // TODO: reuse existing cache? probably only a minor performance gain + + private void populateCache(final BigInteger startAddress, final BigInteger endAddress) + { + try + { + IMemoryBlockExtension memoryBlock = getMemoryBlock(); + + BigInteger lengthInBytes = endAddress.subtract(startAddress); + BigInteger addressableSize = BigInteger.valueOf(getAddressableSize()); + + long units = lengthInBytes.divide(addressableSize) + .add(lengthInBytes.mod(addressableSize).compareTo(BigInteger.ZERO) > 0 ? BigInteger.ONE : BigInteger.ZERO).longValue(); + + // CDT (and maybe other backends) will call setValue() on these MemoryBlock objects. We + // don't want this to happen, because it interferes with this rendering's own change history. + // Ideally, we should strictly use the back end change notification and history, but it is + // only guaranteed to work for bytes within the address range of the MemoryBlock. + + MemoryByte readBytes[] = memoryBlock.getBytesFromAddress(startAddress, units); + FPMemoryByte cachedBytes[] = new FPMemoryByte[readBytes.length]; + + for (int index = 0; index < readBytes.length; index++) + cachedBytes[index] = new FPMemoryByte(readBytes[index].getValue(), readBytes[index].getFlags()); + + // Derive the target endian from the read MemoryBytes. + + if (cachedBytes.length > 0) + if (cachedBytes[0].isEndianessKnown()) + setTargetLittleEndian(!cachedBytes[0].isBigEndian()); + + // The first time we execute this method, set the display endianness to the target endianness. + + if (!initialDisplayModeSet) + { + setDisplayLittleEndian(isTargetLittleEndian()); + initialDisplayModeSet = true; + } + + // Re-order bytes within unit to be a sequential byte stream if the endian is already little + + if (isTargetLittleEndian()) + { + // There isn't an order when the unit size is one, so skip for performance + + if (addressableSize.compareTo(BigInteger.ONE) != 0) + { + int unitSize = addressableSize.intValue(); + FPMemoryByte cachedBytesAsByteSequence[] = new FPMemoryByte[cachedBytes.length]; + for (int unit = 0; unit < units; unit++) + { + for (int unitbyte = 0; unitbyte < unitSize; unitbyte++) + { + cachedBytesAsByteSequence[unit * unitSize + unitbyte] = cachedBytes[unit * unitSize + unitSize - unitbyte]; + } + } + cachedBytes = cachedBytesAsByteSequence; + } + } + + final FPMemoryByte[] cachedBytesFinal = cachedBytes; + + fCache = new MemoryUnit(); + fCache.start = startAddress; + fCache.end = endAddress; + fCache.bytes = cachedBytesFinal; + + Display.getDefault().asyncExec(new Runnable() + { + @Override + public void run() + { + // Generate deltas + + for (int historyIndex = 0; historyIndex < getHistoryDepth(); historyIndex++) + { + if (fHistoryCache[historyIndex] != null && fHistoryCache[historyIndex].isValid()) + { + BigInteger maxStart = startAddress.max(fHistoryCache[historyIndex].start); + BigInteger minEnd = endAddress.min(fHistoryCache[historyIndex].end).subtract(BigInteger.ONE); + + BigInteger overlapLength = minEnd.subtract(maxStart); + if (overlapLength.compareTo(BigInteger.valueOf(0)) > 0) + { + // there is overlap + + int offsetIntoOld = maxStart.subtract(fHistoryCache[historyIndex].start).intValue(); + int offsetIntoNew = maxStart.subtract(startAddress).intValue(); + + for (int i = overlapLength.intValue(); i >= 0; i--) + { + cachedBytesFinal[offsetIntoNew + i].setChanged(historyIndex, + cachedBytesFinal[offsetIntoNew + i].getValue() != fHistoryCache[historyIndex].bytes[offsetIntoOld + i] + .getValue()); + } + + // There are several scenarios where the history cache must be updated from the data cache, so that when a + // cell is edited the font color changes appropriately. The following code deals with the different cases. + + if (historyIndex != 0) continue; + + int dataStart = fCache.start.intValue(); + int dataEnd = fCache.end.intValue(); + int dataLength = fCache.bytes.length; + + int historyStart = fHistoryCache[0].start.intValue(); + int historyEnd = fHistoryCache[0].end.intValue(); + int historyLength = fHistoryCache[0].bytes.length; + + // Case 1: The data cache is smaller than the history cache; the data cache's + // address range is fully covered by the history cache. Do nothing. + + if ((dataStart >= historyStart) && (dataEnd <= historyEnd)) + continue; + + // Case 2: The data and history cache's do not overlap at all + + if (((dataStart < historyStart) && (dataEnd < historyStart)) || (dataStart > historyEnd)) + { + // Create a new history cache: Copy the data cache bytes to the history cache + + MemoryUnit newHistoryCache = new MemoryUnit(); + + newHistoryCache.start = fCache.start; + newHistoryCache.end = fCache.end; + int newHistoryCacheSize = fCache.bytes.length; + newHistoryCache.bytes = new FPMemoryByte[newHistoryCacheSize]; + + for (int index = 0; index < newHistoryCacheSize; index++) + newHistoryCache.bytes[index] = new FPMemoryByte(fCache.bytes[index].getValue()); + + fHistoryCache[0] = newHistoryCache; + + continue; + } + + // Case 3: The data cache starts at a lower address than the history cache, but overlaps the history cache + + if ((dataStart < historyStart) && ((dataEnd >= historyStart) && (dataEnd <= historyEnd))) + { + // Create a new history cache with the missing data from the main cache and append the old history to it. + + int missingDataByteCount = historyStart - dataStart; + int historyCacheSize = historyLength; + int newHistoryCacheSize = missingDataByteCount + historyLength; + + if (missingDataByteCount <= 0 && historyCacheSize <= 0) break; + + MemoryUnit newHistoryCache = new MemoryUnit(); + + newHistoryCache.start = fCache.start; + newHistoryCache.end = fHistoryCache[0].end; + newHistoryCache.bytes = new FPMemoryByte[newHistoryCacheSize]; + + // Copy the missing bytes from the beginning of the main cache to the history cache. + + for (int index = 0; index < missingDataByteCount; index++) + newHistoryCache.bytes[index] = new FPMemoryByte(fCache.bytes[index].getValue()); + + // Copy the remaining bytes from the old history cache to the new history cache + + for (int index = 0; index < historyCacheSize; index++) + newHistoryCache.bytes[index + missingDataByteCount] = + new FPMemoryByte(fHistoryCache[0].bytes[index].getValue()); + + fHistoryCache[0] = newHistoryCache; + + continue; + } + + // Case 4: The data cache starts at a higher address than the history cache + + if (((dataStart >= historyStart) && (dataStart <= historyEnd)) && (dataEnd > historyEnd)) + { + // Append the missing main cache bytes to the history cache. + + int missingDataByteCount = dataEnd - historyEnd; + int historyCacheSize = historyEnd - historyStart; + int newHistoryCacheSize = missingDataByteCount + historyLength; + + if (missingDataByteCount > 0 && historyCacheSize > 0) + { + MemoryUnit newHistoryCache = new MemoryUnit(); + + newHistoryCache.start = fHistoryCache[0].start; + newHistoryCache.end = fCache.end; + newHistoryCache.bytes = new FPMemoryByte[newHistoryCacheSize]; + + // Copy the old history bytes to the new history cache + + System.arraycopy(fHistoryCache[0].bytes, 0, newHistoryCache.bytes, 0, historyLength); + + // Copy the bytes from the main cache that are not in the history cache to the end of the new history cache. + + for (int index = 0; index < missingDataByteCount; index++) + { + int srcIndex = dataLength - missingDataByteCount + index; + int dstIndex = historyLength + index; + newHistoryCache.bytes[dstIndex] = new FPMemoryByte(fCache.bytes[srcIndex].getValue()); + } + + fHistoryCache[0] = newHistoryCache; + + continue; + } + } + + // Case 5 - The data cache is greater than the history cache and fully covers it + + if (dataStart < historyStart && dataEnd > historyEnd) + { + int start = 0; + int end = 0; + + // Create a new history cache to reflect the entire data cache + + MemoryUnit newHistoryCache = new MemoryUnit(); + + newHistoryCache.start = fCache.start; + newHistoryCache.end = fCache.end; + int newHistoryCacheSize = fCache.bytes.length; + newHistoryCache.bytes = new FPMemoryByte[newHistoryCacheSize]; + + int topByteCount = historyStart - dataStart; + int bottomByteCount = dataEnd - historyEnd; + + // Copy the bytes from the beginning of the data cache to the new history cache + + for (int index = 0; index < topByteCount; index++) + newHistoryCache.bytes[index] = new FPMemoryByte(fCache.bytes[index].getValue()); + + // Copy the old history cache bytes to the new history cache + + start = topByteCount; + end = topByteCount + historyLength; + + for (int index = start; index < end; index++) + newHistoryCache.bytes[index] = new FPMemoryByte(fCache.bytes[index].getValue()); + + // Copy the bytes from the end of the data cache to the new history cache + + start = topByteCount + historyLength; + end = topByteCount + historyLength + bottomByteCount; + + for (int index = start; index < end; index++) + newHistoryCache.bytes[index] = new FPMemoryByte(fCache.bytes[index].getValue()); + + fHistoryCache[0] = newHistoryCache; + + continue; + } + } + } + } + + // If the history does not exist, populate the history with the just populated + // cache. This solves the use case of (1) connect to target; (2) edit memory + // before the first suspend debug event; (3) paint differences in changed color. + + if (fHistoryCache[0] == null) + fHistoryCache[0] = fCache.clone(); + + Rendering.this.redrawPanes(); + } + }); + + } + catch (Exception e) + { + // User can scroll to any memory, whether it's valid on the target or not. It doesn't make + // much sense to fill up the Eclipse error log with such "failures." So, comment out for now. + // logError(FPRenderingMessages.getString("FAILURE_READ_MEMORY"), e); //$NON-NLS-1$ + } + } + + // Bytes will be fetched from cache + + @Override + public FPMemoryByte[] getBytes(BigInteger address, int bytesRequested) throws DebugException + { + assert Thread.currentThread().equals(Display.getDefault().getThread()) : FPRenderingMessages.getString("CALLED_ON_NON_DISPATCH_THREAD"); //$NON-NLS-1$ + + if (containsEditedCell(address)) // Cell size cannot be switched during an edit + return getEditedMemory(address); + + boolean contains = false; + if (fCache != null && fCache.start != null) + { + // See if all of the data requested is in the cache + + BigInteger dataEnd = address.add(BigInteger.valueOf(bytesRequested)); + + if (fCache.start.compareTo(address) <= 0 && fCache.end.compareTo(dataEnd) >= 0 && fCache.bytes.length > 0) + contains = true; + } + + if (contains) + { + int offset = address.subtract(fCache.start).intValue(); + FPMemoryByte bytes[] = new FPMemoryByte[bytesRequested]; + + for (int index = 0; index < bytes.length; index++) + bytes[index] = fCache.bytes[offset + index]; + + return bytes; + } + + FPMemoryByte bytes[] = new FPMemoryByte[bytesRequested]; + + for (int index = 0; index < bytes.length; index++) + { + bytes[index] = new FPMemoryByte(); + bytes[index].setReadable(false); + } + + fViewportCache.queueRequest(fViewportAddress, getViewportEndAddress()); + + return bytes; + } + + @Override + public boolean containsEditedCell(BigInteger address) + { + assert Thread.currentThread().equals(Display.getDefault().getThread()) : FPRenderingMessages.getString("CALLED_ON_NON_DISPATCH_THREAD"); //$NON-NLS-1$ + return fEditBuffer.containsKey(address); + } + + public FPMemoryByte[] getEditedMemory(BigInteger address) + { + assert Thread.currentThread().equals(Display.getDefault().getThread()) : FPRenderingMessages.getString("CALLED_ON_NON_DISPATCH_THREAD"); //$NON-NLS-1$ + return fEditBuffer.get(address); + } + + @Override + public void clearEditBuffer() + { + assert Thread.currentThread().equals(Display.getDefault().getThread()) : FPRenderingMessages.getString("CALLED_ON_NON_DISPATCH_THREAD"); //$NON-NLS-1$ + fEditBuffer.clear(); + Rendering.this.redrawPanes(); + } + + @Override + public void writeEditBuffer() + { + assert Thread.currentThread().equals(Display.getDefault().getThread()) : FPRenderingMessages.getString("CALLED_ON_NON_DISPATCH_THREAD"); //$NON-NLS-1$ + + Set<BigInteger> keySet = fEditBuffer.keySet(); + Iterator<BigInteger> iterator = keySet.iterator(); + + while (iterator.hasNext()) + { + BigInteger address = iterator.next(); + FPMemoryByte[] bytes = fEditBuffer.get(address); + + byte byteValue[] = new byte[bytes.length]; + + for (int index = 0; index < bytes.length; index++) + byteValue[index] = bytes[index].getValue(); + + try + { + IMemoryBlockExtension block = getMemoryBlock(); + BigInteger offset = address.subtract(block.getBigBaseAddress()); + block.setValue(offset, byteValue); + } + catch (Exception e) + { + MemoryViewUtil.openError(FPRenderingMessages.getString("FAILURE_WRITE_MEMORY"), "", e); //$NON-NLS-1$ //$NON-NLS-2$ + logError(FPRenderingMessages.getString("FAILURE_WRITE_MEMORY"), e); //$NON-NLS-1$ + } + } + + clearEditBuffer(); + } + + @Override + public void setEditedValue(BigInteger address, FPMemoryByte[] bytes) + { + assert Thread.currentThread().equals(Display.getDefault().getThread()) : FPRenderingMessages.getString("CALLED_ON_NON_DISPATCH_THREAD"); //$NON-NLS-1$ + fEditBuffer.put(address, bytes); + Rendering.this.redrawPanes(); + } + } + + public void setVisibleAddressBar(boolean visible) + { + fAddressBarControl.setVisible(visible); + if (visible) + { + String selectedStr = "0x" + getCaretAddress().toString(16); //$NON-NLS-1$ + Text text = fAddressBar.getExpressionWidget(); + text.setText(selectedStr); + text.setSelection(0, text.getCharCount()); + fAddressBar.getExpressionWidget().setFocus(); + } + + layout(true); + layoutPanes(); + } + + public void setDirty(boolean needRefresh) + { + fCacheDirty = needRefresh; + } + + public boolean isDirty() + { + return fCacheDirty; + } + + @Override + public void dispose() + { + DebugPlugin.getDefault().removeDebugEventListener(this); + if (fViewportCache != null) + { + fViewportCache.dispose(); + fViewportCache = null; + } + super.dispose(); + } + + class Selection implements FPIMemorySelection + { + private BigInteger fStartHigh = null; + private BigInteger fStartLow = null; + + private BigInteger fEndHigh = null; + private BigInteger fEndLow = null; + + @Override + public void clear() + { + fEndHigh = fEndLow = fStartHigh = fStartLow = null; + redrawPanes(); + } + + @Override + public boolean hasSelection() + { + return fStartHigh != null && fStartLow != null && fEndHigh != null && fEndLow != null; + } + + @Override + public boolean isSelected(BigInteger address) + { + // Do we have valid start and end addresses? + + if (getEnd() == null || getStart() == null) return false; + + // If end is greater than start + + if (getEnd().compareTo(getStart()) >= 0) + { + // If address is greater-than-or-equal-to start and less then end, return true + if (address.compareTo(getStart()) >= 0 && address.compareTo(getEnd()) < 0) return true; + } + + // If start is greater than end + + else if (getStart().compareTo(getEnd()) >= 0) + { + // If address is greater-than-or-equal-to zero and less than start, return true + if (address.compareTo(getEnd()) >= 0 && address.compareTo(getStart()) < 0) return true; + } + + return false; + } + + // Set selection start + + @Override + public void setStart(BigInteger high, BigInteger low) + { + if (high == null && low == null) + { + if (fStartHigh != null && fStartLow != null) + { + fStartHigh = null; + fStartLow = null; + redrawPanes(); + } + + return; + } + + boolean changed = false; + + if (fStartHigh == null || !high.equals(fStartHigh)) + { + fStartHigh = high; + changed = true; + } + + if (fStartLow == null || !low.equals(fStartLow)) + { + fStartLow = low; + changed = true; + } + + if (changed) redrawPanes(); + } + + // Set selection end + + @Override + public void setEnd(BigInteger high, BigInteger low) + { + if (high == null && low == null) + { + if (fEndHigh != null && fEndLow != null) + { + fEndHigh = null; + fEndLow = null; + redrawPanes(); + } + + return; + } + + boolean changed = false; + + if (fEndHigh == null || !high.equals(fEndHigh)) + { + fEndHigh = high; + changed = true; + } + + if (fEndLow == null || !low.equals(fEndLow)) + { + fEndLow = low; + changed = true; + } + + if (changed) redrawPanes(); + } + + @Override + public BigInteger getHigh() + { + if (!hasSelection()) return null; + return getStart().max(getEnd()); + } + + @Override + public BigInteger getLow() + { + if (!hasSelection()) return null; + return getStart().min(getEnd()); + } + + @Override + public BigInteger getStart() + { + // If there is no start, return null + if (fStartHigh == null) return null; + + // If there is no end, return the high address of the start + if (fEndHigh == null) return fStartHigh; + + // If Start High/Low equal End High/Low, return a low start and high end + if (fStartHigh.equals(fEndHigh) && fStartLow.equals(fEndLow)) return fStartLow; + + BigInteger differenceEndToStartHigh = fEndHigh.subtract(fStartHigh).abs(); + BigInteger differenceEndToStartLow = fEndHigh.subtract(fStartLow).abs(); + + // Return the start high or start low based on which creates a larger selection + if (differenceEndToStartHigh.compareTo(differenceEndToStartLow) > 0) + return fStartHigh; + + return fStartLow; + } + + @Override + public BigInteger getStartLow() + { + return fStartLow; + } + + @Override + public BigInteger getEnd() + { + // If there is no end, return null + if (fEndHigh == null) return null; + + // *** Temporary for debugging *** + + if (fStartHigh == null || fStartLow == null) + { + return null; + } + + // If Start High/Low equal End High/Low, return a low start and high end + if (fStartHigh.equals(fEndHigh) && fStartLow.equals(fEndLow)) return fStartHigh; + + BigInteger differenceStartToEndHigh = fStartHigh.subtract(fEndHigh).abs(); + BigInteger differenceStartToEndLow = fStartHigh.subtract(fEndLow).abs(); + + // Return the start high or start low based on which creates a larger selection + if (differenceStartToEndHigh.compareTo(differenceStartToEndLow) >= 0) + return fEndHigh; + + return fEndLow; + } + } + + public void setPaneVisible(int pane, boolean visible) + { + switch (pane) + { + case PANE_ADDRESS: + fAddressPane.setPaneVisible(visible); + break; + case PANE_DATA: + fDataPane.setPaneVisible(visible); + break; + } + + fireSettingsChanged(); + layoutPanes(); + } + + public boolean getPaneVisible(int pane) + { + switch (pane) + { + case PANE_ADDRESS: + return fAddressPane.isPaneVisible(); + case PANE_DATA: + return fDataPane.isPaneVisible(); + default: + return false; + } + } + + protected void packColumns() + { + int availableWidth = Rendering.this.getSize().x; + + if (fAddressPane.isPaneVisible()) + { + availableWidth -= fAddressPane.computeSize(0, 0).x; + availableWidth -= Rendering.this.getRenderSpacing() * 2; + } + + int combinedWidth = 0; + + if (fDataPane.isPaneVisible()) combinedWidth += fDataPane.getCellWidth(); + + if (getColumnsSetting() == Rendering.COLUMNS_AUTO_SIZE_TO_FIT) + { + if (combinedWidth == 0) + fColumnCount = 0; + else + { + fColumnCount = availableWidth / combinedWidth; + if (fColumnCount == 0) fColumnCount = 1; // Paint one column even if only part can show in view + } + } + else + fColumnCount = getColumnsSetting(); + + try + { + // Update the number of bytes per row; the max/min scroll range and the current thumbnail position. + + fBytesPerRow = getCharsPerColumn() * getColumnCount(); + getVerticalBar().setMinimum(1); + + // scrollbar maximum range is Integer.MAX_VALUE. + + getVerticalBar().setMaximum(getMaxScrollRange().min(BigInteger.valueOf(Integer.MAX_VALUE)).intValue()); + getVerticalBar().setIncrement(1); + getVerticalBar().setPageIncrement(this.getRowCount() - 1); + + // FIXME: conversion of slider to scrollbar + // fScrollBar.setToolTipText(Rendering.this.getAddressString(fViewportAddress)); + + setScrollSelection(); + } + catch (Exception e) + { + // FIXME precautionary + } + + Rendering.this.redraw(); + Rendering.this.redrawPanes(); + } + + public FPAbstractPane[] getRenderingPanes() + { + return new FPAbstractPane[] { fAddressPane, fDataPane }; + } + + public int getCellPadding() + { + return fCellPadding; + } + + protected int getRenderSpacing() + { + return fPaneSpacing; + } + + public void refresh() + { + if (!this.isDisposed()) + { + if (this.isVisible() && getViewportCache() != null) + { + getViewportCache().refresh(); + } + else + { + setDirty(true); + fParent.updateRenderingLabels(); + } + } + } + + protected void archiveDeltas() + { + this.getViewportCache().archiveDeltas(); + } + + public void gotoAddress(BigInteger address) + { + // Ensure that the GoTo address is within the addressable range + + if ((address.compareTo(this.getMemoryBlockStartAddress()) < 0) || (address.compareTo(this.getMemoryBlockEndAddress()) > 0)) + return; + + fViewportAddress = address; + + // Reset the caret and selection state (no caret and no selection) + + fCaretAddress = null; + fSelection = new Selection(); + + redrawPanes(); + } + + public void setViewportStartAddress(BigInteger newAddress) + { + fViewportAddress = newAddress; + } + + public BigInteger getViewportStartAddress() + { + return fViewportAddress; + } + + public BigInteger getViewportEndAddress() + { + return fViewportAddress.add(BigInteger.valueOf(this.getBytesPerRow() * getRowCount() / getAddressableSize())); + } + + public String getAddressString(BigInteger address) + { + StringBuffer addressString = new StringBuffer(address.toString(16).toUpperCase()); + + for (int chars = getAddressBytes() * 2 - addressString.length(); chars > 0; chars--) + addressString.insert(0, '0'); + + addressString.insert(0, "0x"); //$NON-NLS-1$ + + return addressString.toString(); + } + + protected int getAddressBytes() + { + return fParent.getAddressSize(); + } + + public Control getAddressBarControl() + { + return fAddressBarControl; + } + + public int getColumnCount() + { + return fColumnCount; + } + + public int getColumnsSetting() + { + return fColumnsSetting; + } + + protected void setBytesPerRow(int count) + { + fBytesPerRow = count; + } + + protected void setColumnCount(int count) + { + fColumnCount = count; + } + + public void setColumnsSetting(int columns) + { + if (fColumnsSetting != columns) + { + fColumnsSetting = columns; + fireSettingsChanged(); + layoutPanes(); + } + } + + protected void ensureVisible(BigInteger address) + { + BigInteger viewportStart = this.getViewportStartAddress(); + BigInteger viewportEnd = this.getViewportEndAddress(); + + boolean isAddressBeforeViewportStart = address.compareTo(viewportStart) < 0; + boolean isAddressAfterViewportEnd = address.compareTo(viewportEnd) > 0; + + if (isAddressBeforeViewportStart || isAddressAfterViewportEnd) gotoAddress(address); + } + + protected int getRowCount() + { + int rowCount = 0; + Control panes[] = getRenderingPanes(); + for (int i = 0; i < panes.length; i++) + if (panes[i] instanceof FPAbstractPane) + rowCount = Math.max(rowCount, ((FPAbstractPane) panes[i]).getRowCount()); + + return rowCount; + } + + protected int getBytesPerRow() + { + return fBytesPerRow; + } + + protected int getAddressableCellsPerRow() + { + return getBytesPerRow() / getAddressableSize(); + } + + public int getAddressesPerColumn() + { + return this.getCharsPerColumn() / getAddressableSize(); + } + + /** + * @return Set current scroll selection + */ + protected void setScrollSelection() + { + BigInteger selection = getViewportStartAddress().divide(BigInteger.valueOf(getAddressableCellsPerRow())); + + fScrollSelection = rows2scrollbar(selection); + getVerticalBar().setSelection(fScrollSelection); + } + + /** + * compute the maximum scrolling range. + * + * @return number of lines that rendering can display + */ + private BigInteger getMaxScrollRange() + { + BigInteger difference = getMemoryBlockEndAddress().subtract(getMemoryBlockStartAddress()).add(BigInteger.ONE); + BigInteger maxScrollRange = difference.divide(BigInteger.valueOf(getAddressableCellsPerRow())); + if (maxScrollRange.multiply(BigInteger.valueOf(getAddressableCellsPerRow())).compareTo(difference) != 0) + maxScrollRange = maxScrollRange.add(BigInteger.ONE); + + // Support targets with an addressable size greater than 1 + + maxScrollRange = maxScrollRange.divide(BigInteger.valueOf(getAddressableSize())); + return maxScrollRange; + } + + /** + * The scroll range is limited by SWT. Because it can be less than the number + * of rows (of memory) that we need to display, we need an arithmetic mapping. + * + * @return ratio this function returns how many rows a scroll bar unit + * represents. The number will be some fractional value, up to but + * not exceeding the value 1. I.e., when the scroll range exceeds + * the row range, we use a 1:1 mapping. + */ + private final BigDecimal getScrollRatio() + { + BigInteger maxRange = getMaxScrollRange(); + if (maxRange.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) + { + return new BigDecimal(maxRange).divide(BigDecimal.valueOf(Integer.MAX_VALUE), SCROLL_CONVERSION_PRECISION); + } + + return BigDecimal.ONE; + } + + /** + * Convert memory row units to scroll bar units. The scroll range is limited + * by SWT. Because it can be less than the number of rows (of memory) that + * we need to display, we need an arithmetic mapping. + * + * @param rows + * units of memory + * @return scrollbar units + */ + private int rows2scrollbar(BigInteger rows) + { + return new BigDecimal(rows).divide(getScrollRatio(), SCROLL_CONVERSION_PRECISION).intValue(); + } + + /** + * Convert scroll bar units to memory row units. The scroll range is limited + * by SWT. Because it can be less than the number of rows (of memory) that + * we need to display, we need an arithmetic mapping. + * + * @param scrollbarUnits + * scrollbar units + * @return number of rows of memory + */ + BigInteger scrollbar2rows(int scrollbarUnits) + { + return getScrollRatio().multiply(BigDecimal.valueOf(scrollbarUnits), SCROLL_CONVERSION_PRECISION).toBigInteger(); + } + + /** + * @return start address of the memory block + */ + protected BigInteger getMemoryBlockStartAddress() + { + if (fMemoryBlockStartAddress == null) + fMemoryBlockStartAddress = fParent.getMemoryBlockStartAddress(); + if (fMemoryBlockStartAddress == null) + fMemoryBlockStartAddress = BigInteger.ZERO; + + return fMemoryBlockStartAddress; + } + + /** + * @return end address of the memory block + */ + protected BigInteger getMemoryBlockEndAddress() + { + if (fMemoryBlockEndAddress == null) + fMemoryBlockEndAddress = fParent.getMemoryBlockEndAddress(); + + return fMemoryBlockEndAddress; + } + + public FPDataType getFPDataType() + { + return fFPDataType; + } + + public void setFPDataType(FPDataType numberType) + { + if (fFPDataType == numberType) return; + + fFPDataType = numberType; + fireSettingsChanged(); + layoutPanes(); + } + + public int getUpdateMode() + { + return fUpdateMode; + } + + public void setUpdateMode(int fUpdateMode) + { + this.fUpdateMode = fUpdateMode; + } + + protected String getCharacterSet(int mode) + { + switch (mode) + { + case Rendering.TEXT_UTF8: + return "UTF8"; //$NON-NLS-1$ + + case Rendering.TEXT_UTF16: + return "UTF16"; //$NON-NLS-1$ + + case Rendering.TEXT_USASCII: + return "US-ASCII"; //$NON-NLS-1$ + + case Rendering.TEXT_ISO_8859_1: + default: + return "ISO-8859-1"; //$NON-NLS-1$ + } + } + + public int getBytesPerCharacter() + { + return 1; + } + + public boolean isTargetLittleEndian() + { + return fIsTargetLittleEndian; + } + + public void setTargetLittleEndian(boolean littleEndian) + { + if (fIsTargetLittleEndian == littleEndian) return; + + fParent.setTargetMemoryLittleEndian(littleEndian); + fIsTargetLittleEndian = littleEndian; + + Display.getDefault().asyncExec(new Runnable() + { + @Override + public void run() + { + fireSettingsChanged(); + layoutPanes(); + } + }); + } + + public boolean isDisplayLittleEndian() + { + return fIsDisplayLittleEndian; + } + + public void setDisplayLittleEndian(boolean isLittleEndian) + { + if (fIsDisplayLittleEndian == isLittleEndian) return; + fIsDisplayLittleEndian = isLittleEndian; + fireSettingsChanged(); + + Display.getDefault().asyncExec(new Runnable() + { + @Override + public void run() + { + layoutPanes(); + } + }); + } + + public int getCharsPerColumn() + { + return fCharsPerColumn; + } + + public int getDisplayedPrecision() + { + return fDisplayedPrecision; + } + + // Set the number of precision digits that are displayed in the view + + public void setDisplayedPrecision(int displayedPrecision) + { + if (fDisplayedPrecision != displayedPrecision) + { + fDisplayedPrecision = displayedPrecision; + fCharsPerColumn = charsPerColumn(); + fireSettingsChanged(); + layoutPanes(); + } + } + + protected void redrawPane(int paneId) + { + if (!isDisposed() && this.isVisible()) + { + FPAbstractPane pane = null; + + if (paneId == Rendering.PANE_ADDRESS) + { + pane = fAddressPane; + } + else if (paneId == Rendering.PANE_DATA) + { + pane = fDataPane; + } + + if (pane != null && pane.isPaneVisible()) + { + pane.redraw(); + pane.setRowCount(); + if (pane.isFocusControl()) pane.updateTheCaret(); + } + } + + fParent.updateRenderingLabels(); + } + + protected void redrawPanes() + { + if (!isDisposed() && this.isVisible()) + { + if (fAddressPane.isPaneVisible()) + { + fAddressPane.redraw(); + fAddressPane.setRowCount(); + if (fAddressPane.isFocusControl()) fAddressPane.updateTheCaret(); + } + + if (fDataPane.isPaneVisible()) + { + fDataPane.redraw(); + fDataPane.setRowCount(); + if (fDataPane.isFocusControl()) fDataPane.updateTheCaret(); + } + } + + fParent.updateRenderingLabels(); + } + + void layoutPanes() + { + packColumns(); + layout(true); + + redraw(); + redrawPanes(); + } + + void fireSettingsChanged() + { + fAddressPane.settingsChanged(); + fDataPane.settingsChanged(); + } + + protected void copyAddressToClipboard() + { + Clipboard clip = null; + + try + { + clip = new Clipboard(getDisplay()); + + String addressString = "0x" + getCaretAddress().toString(16); //$NON-NLS-1$ + + TextTransfer plainTextTransfer = TextTransfer.getInstance(); + clip.setContents(new Object[] { addressString }, new Transfer[] { plainTextTransfer }); + } + finally + { + if (clip != null) + { + clip.dispose(); + } + } + } + + // Given an array of bytes, the data type and endianness, return a scientific notation string representation + + public String sciNotationString(FPMemoryByte byteArray[], FPDataType fpDataType, boolean isLittleEndian) + { + StringBuffer textString = null; + + // Check the byte array for readability + + for (int index = 0; index < byteArray.length; index++) + if (!byteArray[index].isReadable()) + return FPutilities.fillString(fCharsPerColumn, '?'); + + // Convert the byte array to a floating point value and check to see if it's within the range + // of the data type. If the valid range is exceeded, set string to "-Infinity" or "Infinity". + + ByteOrder byteOrder = isLittleEndian ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN; + + if (fpDataType == FPDataType.FLOAT) + { + float floatValue = ByteBuffer.wrap(FPutilities.memoryBytesToByteArray(byteArray)).order(byteOrder).getFloat(); + floatValue = FPutilities.floatLimitCheck(floatValue); + + if (floatValue == Float.NEGATIVE_INFINITY) + textString = new StringBuffer("-Infinity"); //$NON-NLS-1$ + + if (floatValue == Float.POSITIVE_INFINITY) + textString = new StringBuffer(" Infinity"); //$NON-NLS-1$ + } + + if (fpDataType == FPDataType.DOUBLE) + { + double doubleValue = ByteBuffer.wrap(FPutilities.memoryBytesToByteArray(byteArray)).order(byteOrder).getDouble(); + doubleValue = FPutilities.doubleLimitCheck(doubleValue); + + if (doubleValue == Double.NEGATIVE_INFINITY) + textString = new StringBuffer("-Infinity"); //$NON-NLS-1$ + + if (doubleValue == Double.POSITIVE_INFINITY) + textString = new StringBuffer(" Infinity"); //$NON-NLS-1$ + } + + // If we do not already have a StringBuffer value, Convert the value to + // a string. In any case, pad the string with spaces to the cell width. + + if (textString == null) + textString = new StringBuffer(FPutilities.byteArrayToSciNotation(fpDataType, isLittleEndian, byteArray, fDisplayedPrecision)); + + return (textString.append(FPutilities.fillString(fCharsPerColumn - textString.length(), getPaddingCharacter()))).toString(); + } + + // Convert the floating point edit buffer string to a byte array and update memory + + public void convertAndUpdateCell(BigInteger memoryAddress, String editBuffer) + { + if (editBuffer != null && editBuffer.length() > 0) + { + // Convert the edit buffer string to byte arrays + + byte[] targetByteArray = new byte[getFPDataType().getByteLength()]; + + targetByteArray = FPutilities.floatingStringToByteArray(getFPDataType(), editBuffer, getFPDataType().getBitsize()); + + // If we're in little endian mode, reverse the bytes, which is required + // due to lower-level internal conversion when written to memory. + + if (fIsDisplayLittleEndian) + targetByteArray = FPutilities.reverseByteOrder(targetByteArray); + + FPMemoryByte[] newMemoryBytes = new FPMemoryByte[targetByteArray.length]; + + for (int index = 0; index < targetByteArray.length; index++) + { + newMemoryBytes[index] = new FPMemoryByte(targetByteArray[index]); + newMemoryBytes[index].setBigEndian(!isTargetLittleEndian()); + newMemoryBytes[index].setEdited(true); + newMemoryBytes[index].setChanged(true); + } + + // Apply the change and make it visible in the view + + getViewportCache().setEditedValue(memoryAddress, newMemoryBytes); + getViewportCache().writeEditBuffer(); + redraw(); + } + else + { + // The edit buffer string is null or has a zero length. + + final String errorText = NLS.bind(FPRenderingMessages.getString("FPRendering.ERROR_FPENTRY_POPUP_TEXT"), "<blanks>"); //$NON-NLS-1$ //$NON-NLS-2$ + + try + { + // Restore the previous value + setEditBuffer(new StringBuffer(fDataPane.bytesToSciNotation(getBytes(fCaretAddress, getFPDataType().getByteLength())))); + } + catch (DebugException e) + { + e.printStackTrace(); + } + + // Put together the pop-up window components and show the user the error + + String statusString = FPRenderingMessages.getString("FPRendering.ERROR_FPENTRY_STATUS"); //$NON-NLS-1$ + Status status = new Status(IStatus.ERROR, FPRenderingPlugin.getUniqueIdentifier(), statusString); + FPutilities.popupMessage(FPRenderingMessages.getString("FPRendering.ERROR_FPENTRY_POPUP_TITLE"), errorText, status); //$NON-NLS-1$ + } + } + + // Getter/setter for Insert Mode state + + public void setInsertMode(boolean insertMode) + { + this.fEditInserMode = insertMode; + } + + public boolean insertMode() + { + return fEditInserMode; + } + + // Getter/setter for cell edit state: true = we're in cell edit mode; false = we're not in cell edit mode + + public boolean isEditingCell() + { + return fCellEditState; + } + + public void setEditingCell(boolean state) + { + this.fCellEditState = state; + } + + // Getter/setter for the address of the cell currently being edited + + public BigInteger getCellEditAddress() + { + return cellEditAddress; + } + + public void setCellEditAddress(BigInteger cellEditAddress) + { + this.cellEditAddress = cellEditAddress; + } + + // Getter/Setter for the memory address of the cell currently being edited + + public BigInteger getMemoryAddress() + { + return memoryAddress; + } + + public void setMemoryAddress(BigInteger memoryAddress) + { + this.memoryAddress = memoryAddress; + } + + // Getter/setter for storing the raw/uninterpreted/not-converted-to-scientific-notation text entry string + + public StringBuffer getEditBuffer() + { + return fEditBuffer; + } + + public void setEditBuffer(StringBuffer cellChars) + { + this.fEditBuffer = cellChars; + } + + // Enter cell-edit mode + + public void startCellEditing(BigInteger cellAddress, BigInteger memoryAddress, String editString) + { + setEditBuffer(new StringBuffer(editString)); + setCellEditAddress(cellAddress); + setMemoryAddress(memoryAddress); + setEditingCell(true); + } + + // Exit cell-edit mode + + public void endCellEditing() + { + setEditingCell(false); + setCellEditAddress(null); + setMemoryAddress(null); + setEditBuffer(null); + + } + + // Floating point number edit mode status-line display: 'true' = display edit mode, 'false' clear edit mode + + public void displayEditModeIndicator(final boolean indicatorON) + { + UIJob job = new UIJob("FP Renderer Edit Indicator") //$NON-NLS-1$ + { + @Override + public IStatus runInUIThread(IProgressMonitor monitor) + { + String statusLineMessage; + IViewPart viewInstance = null; + + if (indicatorON) + { + // Construct the edit mode message + statusLineMessage = NLS.bind(FPRenderingMessages.getString("FPRendering.EDIT_MODE"), //$NON-NLS-1$ + (insertMode() ? FPRenderingMessages.getString("FPRendering.EDIT_MODE_INSERT") : //$NON-NLS-1$ + FPRenderingMessages.getString("FPRendering.EDIT_MODE_OVERWRITE"))); //$NON-NLS-1$ + } + else + { + // 'null' = clear the message + statusLineMessage = null; + } + + // Get the window and page references + + IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if (window == null) return Status.OK_STATUS; + + IWorkbenchPage page = window.getActivePage(); + if (page == null) return Status.OK_STATUS; + + // Update (or clear) the Workbench status line when the Memory and Memory Browser views are in focus + + viewInstance = page.findView("org.eclipse.debug.ui.MemoryView"); // TODO: programmatically retrieve ID //$NON-NLS-1$ + + if (viewInstance != null) + viewInstance.getViewSite().getActionBars().getStatusLineManager().setMessage(statusLineMessage); + + viewInstance = page.findView("org.eclipse.cdt.debug.ui.memory.memorybrowser.MemoryBrowser"); // TODO: programmatically retrieve ID //$NON-NLS-1$ + + if (viewInstance != null) + viewInstance.getViewSite().getActionBars().getStatusLineManager().setMessage(statusLineMessage); + + return Status.OK_STATUS; + } + }; + + job.setSystem(true); + job.schedule(); + } + + // Calculate memory address from the cell address + + public BigInteger cellToMemoryAddress(BigInteger cellAddress) + { + BigInteger vpStart = getViewportStartAddress(); + BigInteger colChars = BigInteger.valueOf(getCharsPerColumn()); + BigInteger dtBytes = BigInteger.valueOf(getFPDataType().getByteLength()); + + return vpStart.add(((cellAddress.subtract(vpStart)).divide(colChars)).multiply(dtBytes)); + } +} |