| /******************************************************************************* |
| * Copyright (c) 2006, 2016 Wind River Systems, Inc. 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: |
| * Ted R Williams (Wind River Systems, Inc.) - initial implementation |
| * Teodor Madan (Freescale) - Fix PageDn/PageUp |
| * Alvaro Sanchez-leon (Ericsson) - Add hovering support to the traditional memory render (Bug 489505) |
| *******************************************************************************/ |
| |
| package org.eclipse.cdt.debug.ui.memory.traditional; |
| |
| import java.math.BigInteger; |
| |
| import org.eclipse.debug.core.DebugException; |
| import org.eclipse.debug.core.model.MemoryByte; |
| import org.eclipse.jface.preference.IPreferenceStore; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.dnd.DND; |
| import org.eclipse.swt.events.FocusEvent; |
| import org.eclipse.swt.events.FocusListener; |
| import org.eclipse.swt.events.KeyEvent; |
| import org.eclipse.swt.events.KeyListener; |
| import org.eclipse.swt.events.MouseEvent; |
| import org.eclipse.swt.events.MouseListener; |
| import org.eclipse.swt.events.MouseMoveListener; |
| import org.eclipse.swt.events.MouseTrackAdapter; |
| import org.eclipse.swt.events.PaintEvent; |
| import org.eclipse.swt.events.PaintListener; |
| import org.eclipse.swt.graphics.Font; |
| import org.eclipse.swt.graphics.FontMetrics; |
| import org.eclipse.swt.graphics.GC; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.graphics.Rectangle; |
| import org.eclipse.swt.widgets.Canvas; |
| import org.eclipse.swt.widgets.Caret; |
| import org.eclipse.swt.widgets.Event; |
| import org.eclipse.swt.widgets.ScrollBar; |
| |
| public abstract class AbstractPane extends Canvas { |
| protected Rendering fRendering; |
| |
| // selection state |
| protected boolean fSelectionStarted = false; |
| protected boolean fSelectionInProgress = false; |
| |
| protected BigInteger fSelectionStartAddress = null; |
| |
| protected int fSelectionStartAddressSubPosition; |
| |
| // caret |
| protected Caret fCaret = null; |
| |
| // character may not fall on byte boundary |
| protected int fSubCellCaretPosition = 0; |
| protected int fOldSubCellCaretPosition = 0; |
| |
| protected boolean fCaretEnabled = false; |
| |
| protected BigInteger fCaretAddress = null; |
| |
| // storage |
| protected int fRowCount = 0; |
| |
| protected boolean fPaneVisible = true; |
| |
| class AbstractPaneMouseListener implements MouseListener { |
| @Override |
| public void mouseUp(MouseEvent me) { |
| positionCaret(me.x, me.y); |
| |
| fCaret.setVisible(true); |
| |
| if (fSelectionInProgress && me.button == 1) { |
| endSelection(me.x, me.y); |
| } |
| |
| fSelectionInProgress = fSelectionStarted = false; |
| } |
| |
| @Override |
| public void mouseDown(MouseEvent me) { |
| AbstractPane.this.forceFocus(); |
| |
| positionCaret(me.x, me.y); |
| |
| fCaret.setVisible(false); |
| |
| if (me.button == 1) { |
| // if shift is down and we have an existing start address, |
| // append selection |
| if ((me.stateMask & SWT.SHIFT) != 0 && fRendering.getSelection().getStart() != null) { |
| |
| // if the pane doesn't have a selection start (the |
| // selection was created in a different pane) |
| // then initialize the pane's selection start to the |
| // rendering's selection start |
| if (AbstractPane.this.fSelectionStartAddress == null) |
| AbstractPane.this.fSelectionStartAddress = fRendering.getSelection().getStart(); |
| |
| AbstractPane.this.fSelectionStarted = true; |
| |
| AbstractPane.this.appendSelection(me.x, me.y); |
| |
| } else { |
| // start a new selection |
| |
| AbstractPane.this.startSelection(me.x, me.y); |
| } |
| } |
| } |
| |
| @Override |
| public void mouseDoubleClick(MouseEvent me) { |
| handleMouseDoubleClick(me); |
| } |
| |
| } |
| |
| class AbstractPaneMouseMoveListener implements MouseMoveListener { |
| @Override |
| public void mouseMove(MouseEvent me) { |
| if (fSelectionStarted) { |
| fSelectionInProgress = true; |
| appendSelection(me.x, me.y); |
| } |
| } |
| } |
| |
| class AbstractPaneFocusListener implements FocusListener { |
| @Override |
| public void focusLost(FocusEvent fe) { |
| IPreferenceStore store = TraditionalRenderingPlugin.getDefault().getPreferenceStore(); |
| if (TraditionalRenderingPreferenceConstants.MEM_EDIT_BUFFER_SAVE_ON_ENTER_ONLY |
| .equals(store.getString(TraditionalRenderingPreferenceConstants.MEM_EDIT_BUFFER_SAVE))) { |
| fRendering.getViewportCache().clearEditBuffer(); |
| } else { |
| fRendering.getViewportCache().writeEditBuffer(); |
| } |
| |
| // clear the pane local selection start |
| AbstractPane.this.fSelectionStartAddress = null; |
| } |
| |
| @Override |
| public void focusGained(FocusEvent fe) { |
| } |
| |
| } |
| |
| class AbstractPaneKeyListener implements KeyListener { |
| @Override |
| public void keyPressed(KeyEvent ke) { |
| fOldSubCellCaretPosition = fSubCellCaretPosition; |
| if ((ke.stateMask & SWT.SHIFT) != 0) { |
| switch (ke.keyCode) { |
| case SWT.ARROW_RIGHT: |
| case SWT.ARROW_LEFT: |
| case SWT.ARROW_UP: |
| case SWT.ARROW_DOWN: |
| case SWT.PAGE_DOWN: |
| case SWT.PAGE_UP: |
| if (fRendering.getSelection().getStart() == null) { |
| fRendering.getSelection().setStart( |
| fCaretAddress.add(BigInteger.valueOf(fRendering.getAddressesPerColumn())), |
| fCaretAddress); |
| } |
| break; |
| } |
| } |
| |
| if (ke.keyCode == SWT.ARROW_RIGHT) { |
| handleRightArrowKey(); |
| } else if (ke.keyCode == SWT.ARROW_LEFT || ke.keyCode == SWT.BS) { |
| handleLeftArrowKey(); |
| } else if (ke.keyCode == SWT.ARROW_DOWN) { |
| handleDownArrowKey(); |
| } else if (ke.keyCode == SWT.ARROW_UP) { |
| handleUpArrowKey(); |
| } else if (ke.keyCode == SWT.PAGE_DOWN) { |
| handlePageDownKey(); |
| } else if (ke.keyCode == SWT.PAGE_UP) { |
| handlePageUpKey(); |
| } else if (ke.keyCode == SWT.HOME && (ke.stateMask & SWT.CTRL) != 0) { |
| fRendering.gotoAddress(fRendering.getMemoryBlockStartAddress()); |
| } else if (ke.keyCode == SWT.END && (ke.stateMask & SWT.CTRL) != 0) { |
| fRendering.gotoAddress(fRendering.getMemoryBlockEndAddress()); |
| } else if (ke.keyCode == SWT.HOME && (ke.stateMask & SWT.CTRL) == 0) { |
| handleHomeKey(); |
| } else if (ke.keyCode == SWT.END && (ke.stateMask & SWT.CTRL) == 0) { |
| handleEndKey(); |
| } else if (ke.keyCode == SWT.ESC) { |
| fRendering.getViewportCache().clearEditBuffer(); |
| } else if (ke.character == '\r') { |
| fRendering.getViewportCache().writeEditBuffer(); |
| } else if (Rendering.isValidEditCharacter(ke.character)) { |
| if (fRendering.getSelection().hasSelection()) { |
| setCaretAddress(fRendering.getSelection().getLow()); |
| fSubCellCaretPosition = 0; |
| } |
| |
| editCell(fCaretAddress, fSubCellCaretPosition, ke.character); |
| } else if (ke.keyCode == SWT.TAB && (ke.stateMask & SWT.SHIFT) != 0) { |
| // move backward cursor to the first position in the following pane |
| switchTo(fRendering.incrPane(AbstractPane.this, -1)); |
| } else if (ke.keyCode == SWT.TAB) { |
| // move forward cursor to the first position in the following pane |
| switchTo(fRendering.incrPane(AbstractPane.this, 1)); |
| } |
| |
| if ((ke.stateMask & SWT.SHIFT) != 0) { |
| switch (ke.keyCode) { |
| case SWT.ARROW_RIGHT: |
| case SWT.ARROW_LEFT: |
| case SWT.ARROW_UP: |
| case SWT.ARROW_DOWN: |
| case SWT.PAGE_DOWN: |
| case SWT.PAGE_UP: |
| fRendering.getSelection().setEnd( |
| fCaretAddress.add(BigInteger.valueOf(fRendering.getAddressesPerColumn())), fCaretAddress); |
| break; |
| } |
| } else if (ke.keyCode != SWT.SHIFT && ke.keyCode != SWT.CTRL && ke.keyCode != SWT.COMMAND) |
| // if shift key, keep selection, we might add to it |
| { |
| fRendering.getSelection().clear(); |
| } |
| } |
| |
| @Override |
| public void keyReleased(KeyEvent ke) { |
| // do nothing |
| } |
| } |
| |
| class AbstractPanePaintListener implements PaintListener { |
| @Override |
| public void paintControl(PaintEvent pe) { |
| AbstractPane.this.paint(pe); |
| } |
| } |
| |
| public AbstractPane(Rendering rendering) { |
| super(rendering, SWT.DOUBLE_BUFFERED); |
| |
| fRendering = rendering; |
| |
| try { |
| fCaretAddress = rendering.getBigBaseAddress(); |
| } catch (Exception e) { |
| // do nothing |
| } |
| |
| // pref |
| |
| this.setFont(fRendering.getFont()); |
| |
| GC gc = new GC(this); |
| gc.setFont(this.getFont()); |
| fCaret = new Caret(this, SWT.NONE); |
| fCaret.setSize(1, gc.stringExtent("|").y); //$NON-NLS-1$ |
| gc.dispose(); |
| |
| this.addPaintListener(createPaintListener()); |
| |
| this.addMouseListener(createMouseListener()); |
| |
| this.addMouseMoveListener(createMouseMoveListener()); |
| |
| this.addMouseTrackListener(createMouseHoverListener()); |
| |
| this.addKeyListener(createKeyListener()); |
| |
| this.addFocusListener(createFocusListener()); |
| } |
| |
| protected MouseListener createMouseListener() { |
| return new AbstractPaneMouseListener(); |
| } |
| |
| protected MouseMoveListener createMouseMoveListener() { |
| return new AbstractPaneMouseMoveListener(); |
| } |
| |
| /** |
| * @since 1.4 |
| */ |
| protected MouseTrackAdapter createMouseHoverListener() { |
| // This method provides an empty implementation of MouseTrackAdapter |
| // The non empty implementation instance is left to the subclasses that do support Hovering |
| return new MouseTrackAdapter() { |
| }; |
| } |
| |
| protected FocusListener createFocusListener() { |
| return new AbstractPaneFocusListener(); |
| } |
| |
| protected KeyListener createKeyListener() { |
| return new AbstractPaneKeyListener(); |
| } |
| |
| protected PaintListener createPaintListener() { |
| return new AbstractPanePaintListener(); |
| } |
| |
| protected void handleRightArrowKey() { |
| fSubCellCaretPosition++; |
| if (fSubCellCaretPosition >= getCellCharacterCount()) { |
| fSubCellCaretPosition = 0; |
| // Ensure that caret is within the addressable range |
| BigInteger newCaretAddress = fCaretAddress |
| .add(BigInteger.valueOf(getNumberOfBytesRepresentedByColumn() / fRendering.getAddressableSize())); |
| if (newCaretAddress.compareTo(fRendering.getMemoryBlockEndAddress()) > 0) { |
| fSubCellCaretPosition = getCellCharacterCount(); |
| } else { |
| setCaretAddress(newCaretAddress); |
| } |
| } |
| updateCaret(); |
| ensureCaretWithinViewport(); |
| } |
| |
| protected void handleLeftArrowKey() { |
| fSubCellCaretPosition--; |
| if (fSubCellCaretPosition < 0) { |
| fSubCellCaretPosition = getCellCharacterCount() - 1; |
| // Ensure that caret is within the addressable range |
| BigInteger newCaretAddress = fCaretAddress.subtract( |
| BigInteger.valueOf(getNumberOfBytesRepresentedByColumn() / fRendering.getAddressableSize())); |
| if (newCaretAddress.compareTo(fRendering.getMemoryBlockStartAddress()) < 0) { |
| fSubCellCaretPosition = 0; |
| } else { |
| setCaretAddress(newCaretAddress); |
| } |
| |
| } |
| updateCaret(); |
| ensureCaretWithinViewport(); |
| } |
| |
| protected void handleDownArrowKey() { |
| // Ensure that caret is within the addressable range |
| BigInteger newCaretAddress = fCaretAddress.add(BigInteger.valueOf(fRendering.getAddressableCellsPerRow())); |
| setCaretAddress(newCaretAddress); |
| |
| updateCaret(); |
| ensureCaretWithinViewport(); |
| } |
| |
| protected void handleUpArrowKey() { |
| // Ensure that caret is within the addressable range |
| BigInteger newCaretAddress = fCaretAddress.subtract(BigInteger.valueOf(fRendering.getAddressableCellsPerRow())); |
| setCaretAddress(newCaretAddress); |
| |
| updateCaret(); |
| ensureCaretWithinViewport(); |
| } |
| |
| protected void handlePageDownKey() { |
| // Ensure that caret is within the addressable range |
| BigInteger newCaretAddress = fCaretAddress |
| .add(BigInteger.valueOf(fRendering.getAddressableCellsPerRow() * (fRendering.getRowCount() - 1))); |
| |
| setCaretAddress(newCaretAddress); |
| |
| updateCaret(); |
| ensureCaretWithinViewport(); |
| } |
| |
| protected void handlePageUpKey() { |
| // Ensure that caret is within the addressable range |
| BigInteger newCaretAddress = fCaretAddress |
| .subtract(BigInteger.valueOf(fRendering.getAddressableCellsPerRow() * (fRendering.getRowCount() - 1))); |
| setCaretAddress(newCaretAddress); |
| |
| updateCaret(); |
| ensureCaretWithinViewport(); |
| } |
| |
| /** |
| * @since 1.3 |
| */ |
| protected void handleEndKey() { |
| // calculate offset from the end of the row |
| BigInteger lastCellAddress = fRendering.getViewportEndAddress().subtract(BigInteger.ONE); |
| |
| int cellOffset = fCaretAddress.subtract(lastCellAddress).intValue(); |
| int row = cellOffset / fRendering.getAddressableCellsPerRow(); |
| setCaretAddress(lastCellAddress.add(BigInteger.valueOf(row * fRendering.getAddressableCellsPerRow()))); |
| |
| updateCaret(); |
| ensureCaretWithinViewport(); |
| } |
| |
| /** |
| * @since 1.3 |
| */ |
| protected void handleHomeKey() { |
| // calculate offset from the beginning of the row |
| int cellOffset = fCaretAddress.subtract(fRendering.getViewportStartAddress()).intValue(); |
| int row = cellOffset / fRendering.getAddressableCellsPerRow(); |
| setCaretAddress(fRendering.getViewportStartAddress() |
| .add(BigInteger.valueOf(row * fRendering.getAddressableCellsPerRow()))); |
| |
| updateCaret(); |
| ensureCaretWithinViewport(); |
| } |
| |
| protected void handleMouseDoubleClick(MouseEvent me) { |
| try { |
| BigInteger address = getViewportAddress(me.x / getCellWidth(), me.y / getCellHeight()); |
| |
| fRendering.getSelection().clear(); |
| fRendering.getSelection().setStart(address.add(BigInteger.valueOf(fRendering.getAddressesPerColumn())), |
| address); |
| fRendering.getSelection().setEnd(address.add(BigInteger.valueOf(fRendering.getAddressesPerColumn())), |
| address); |
| } catch (DebugException de) { |
| // do nothing |
| } |
| } |
| |
| private void switchTo(AbstractPane pane) { |
| pane.setCaretAddress(this.fCaretAddress); |
| pane.fOldSubCellCaretPosition = 0; |
| pane.fSubCellCaretPosition = 0; |
| pane.updateCaret(); |
| pane.ensureCaretWithinViewport(); |
| pane.setFocus(); |
| } |
| |
| protected boolean isPaneVisible() { |
| return fPaneVisible; |
| } |
| |
| protected void setPaneVisible(boolean visible) { |
| fPaneVisible = visible; |
| this.setVisible(visible); |
| } |
| |
| protected int getNumberOfBytesRepresentedByColumn() { |
| return fRendering.getBytesPerColumn(); |
| } |
| |
| protected void editCell(BigInteger address, int subCellPosition, char character) { |
| // do nothing |
| } |
| |
| // Set the caret address |
| protected void setCaretAddress(BigInteger caretAddress) { |
| // Ensure that caret is within the addressable range |
| if ((caretAddress.compareTo(fRendering.getMemoryBlockStartAddress()) >= 0) |
| && (caretAddress.compareTo(fRendering.getMemoryBlockEndAddress()) <= 0)) { |
| fCaretAddress = caretAddress; |
| } else if (caretAddress.compareTo(fRendering.getMemoryBlockStartAddress()) < 0) { |
| // calculate offset from the beginning of the row |
| int cellOffset = fCaretAddress.subtract(fRendering.getViewportStartAddress()).intValue(); |
| int row = cellOffset / (fRendering.getBytesPerRow() / fRendering.getBytesPerCharacter()); |
| |
| cellOffset -= row * fRendering.getBytesPerRow() / fRendering.getBytesPerCharacter(); |
| |
| fCaretAddress = fRendering.getMemoryBlockStartAddress() |
| .add(BigInteger.valueOf(cellOffset / fRendering.getAddressableSize())); |
| } else if (caretAddress.compareTo(fRendering.getMemoryBlockEndAddress()) > 0) { |
| // calculate offset from the end of the row |
| int cellOffset = fCaretAddress.subtract(fRendering.getViewportEndAddress()).intValue() + 1; |
| int row = cellOffset / (fRendering.getBytesPerRow() / fRendering.getBytesPerCharacter()); |
| |
| cellOffset -= row * fRendering.getBytesPerRow() / fRendering.getBytesPerCharacter(); |
| |
| fCaretAddress = fRendering.getMemoryBlockEndAddress() |
| .add(BigInteger.valueOf(cellOffset / fRendering.getAddressableSize())); |
| } |
| |
| fRendering.setCaretAddress(fCaretAddress); |
| } |
| |
| protected boolean isOdd(int value) { |
| return (value / 2) * 2 == value; |
| } |
| |
| @SuppressWarnings("all") |
| protected void updateCaret() { |
| try { |
| if (fCaretAddress != null) { |
| Point cellPosition = getCellLocation(fCaretAddress); |
| if (cellPosition != null) |
| fCaret.setLocation(cellPosition.x + fSubCellCaretPosition * getCellCharacterWidth(), |
| cellPosition.y); |
| } |
| } catch (Exception e) { |
| fRendering.logError(TraditionalRenderingMessages.getString("TraditionalRendering.FAILURE_POSITION_CURSOR"), //$NON-NLS-1$ |
| e); |
| } |
| } |
| |
| protected void ensureCaretWithinViewport() // TODO getAddressableSize() > 1 ? |
| { |
| BigInteger vpStart = fRendering.getViewportStartAddress(); |
| BigInteger vpEnd = fRendering.getViewportEndAddress(); |
| |
| Rectangle vpBounds = fRendering.getBounds(); |
| Rectangle apBounds = fRendering.fAddressPane.getBounds(); |
| Rectangle dpBounds = fRendering.fBinaryPane.getBounds(); |
| Rectangle tpBounds = fRendering.fTextPane.getBounds(); |
| |
| ScrollBar hBar = fRendering.getHorizontalBar(); |
| |
| Point adjustedCaret = null; |
| |
| int leftPaneEdge = 0; |
| int rightPaneEdge = 0; |
| int eolLocation = 0; |
| int bolSelection = 0; |
| int eolSelection = 0; |
| |
| // Determine if we're in the address, data (binary) or text panes; return if none of 'em. |
| |
| if (this instanceof AddressPane) { |
| adjustedCaret = new Point(fCaret.getLocation().x, fCaret.getLocation().y); |
| } else if (this instanceof DataPane) { |
| leftPaneEdge = Math.max(vpBounds.x, dpBounds.x); |
| rightPaneEdge = vpBounds.x + vpBounds.width; |
| bolSelection = hBar.getMinimum(); |
| eolSelection = apBounds.width + dpBounds.width - (vpBounds.width / 2); |
| eolLocation = apBounds.width + dpBounds.width - 10; |
| adjustedCaret = new Point(fCaret.getLocation().x + dpBounds.x + 16, fCaret.getLocation().y); |
| } else if (this instanceof TextPane) { |
| leftPaneEdge = apBounds.width + dpBounds.width; |
| rightPaneEdge = leftPaneEdge + (vpBounds.width - tpBounds.x); |
| bolSelection = apBounds.width + dpBounds.width - 36; |
| eolSelection = hBar.getMaximum(); |
| eolLocation = apBounds.width + dpBounds.width + tpBounds.width - 22; |
| adjustedCaret = new Point(fCaret.getLocation().x + apBounds.width + dpBounds.width, fCaret.getLocation().y); |
| } else |
| return; |
| |
| if (fCaretAddress.compareTo(vpStart) < 0 || fCaretAddress.compareTo(vpEnd) >= 0) { |
| // The caret was moved outside the viewport bounds: Scroll the |
| // viewport up or down by a row, depending on where the caret is |
| |
| boolean upArrow = fCaretAddress.compareTo(vpStart) <= 0; |
| int rows = (upArrow ? -1 : 1); |
| if (upArrow) { |
| rows -= vpStart.subtract(fCaretAddress).intValue() / fRendering.getAddressableCellsPerRow(); |
| } else { |
| rows += fCaretAddress.subtract(vpEnd).intValue() / fRendering.getAddressableCellsPerRow(); |
| } |
| ScrollBar vBar = fRendering.getVerticalBar(); |
| vBar.setSelection(vBar.getSelection() + rows); |
| vBar.notifyListeners(SWT.Selection, new Event()); |
| |
| // Check to see if we're at the beginning or end of a line, and |
| // move the scrollbar, if necessary, to keep the caret in view. |
| |
| int currentCaretLocation = fCaret.getLocation().x + dpBounds.x + 16; |
| int lowEolLimit = eolLocation - 1; |
| int highEolLimit = eolLocation + 1; |
| |
| if (fCaret.getLocation().x == 2) { |
| hBar.setSelection(bolSelection); |
| hBar.notifyListeners(SWT.Selection, new Event()); |
| } else if (upArrow && ((currentCaretLocation >= lowEolLimit && currentCaretLocation <= highEolLimit))) { |
| hBar.setSelection(eolSelection); |
| hBar.notifyListeners(SWT.Selection, new Event()); |
| } |
| } else if (!vpBounds.contains(adjustedCaret)) { |
| // Left or Right arrow: The caret is now outside the viewport and beyond the pane. Calculate |
| // a new pane position at [up to] 33% left or right in the viewport, to center the caret; use a |
| // positive or negative offset depending on which direction we're scrolling. |
| |
| int hBarOffset = (rightPaneEdge - leftPaneEdge) / 3; |
| int newHBarSel = hBar.getSelection() + (adjustedCaret.x > rightPaneEdge ? hBarOffset : -hBarOffset); |
| |
| if (fCaret.getLocation().x == 2) { |
| // Beginning of a line |
| hBar.setSelection(bolSelection); |
| } else if (adjustedCaret.x == eolLocation) { |
| // End of a line |
| hBar.setSelection(eolSelection); |
| } else if (adjustedCaret.x > rightPaneEdge) { |
| // Caret was moved by the user beyond the right edge of the pane |
| hBar.setSelection(newHBarSel < hBar.getMaximum() ? newHBarSel : hBar.getMaximum()); |
| } else if (adjustedCaret.x < leftPaneEdge) { |
| // Caret was moved by the user beyond the left edge of the pane |
| hBar.setSelection(newHBarSel > hBar.getMinimum() ? newHBarSel : hBar.getMinimum()); |
| } else |
| return; |
| |
| hBar.notifyListeners(SWT.Selection, new Event()); |
| } else { |
| // Caret is inside the viewport |
| return; |
| } |
| |
| fRendering.ensureViewportAddressDisplayable(); |
| fRendering.setCaretAddress(fCaretAddress); |
| } |
| |
| protected void advanceCursor() { |
| handleRightArrowKey(); |
| } |
| |
| protected void positionCaret(int x, int y) { |
| // do nothing |
| } |
| |
| protected int getRowCount() { |
| return fRowCount; |
| } |
| |
| protected void setRowCount() { |
| fRowCount = getBounds().height / getCellHeight(); |
| } |
| |
| protected void settingsChanged() { |
| fSubCellCaretPosition = 0; |
| } |
| |
| protected void startSelection(int x, int y) { |
| try { |
| BigInteger address = getViewportAddress(x / getCellWidth(), y / getCellHeight()); |
| |
| if (address != null) { |
| this.fSelectionStartAddress = address; |
| Point cellPosition = getCellLocation(address); |
| |
| if (cellPosition != null) { |
| int offset = x - cellPosition.x; |
| fSelectionStartAddressSubPosition = offset / getCellCharacterWidth(); |
| } |
| fRendering.getSelection().clear(); |
| fRendering.getSelection().setStart( |
| address.add( |
| BigInteger.valueOf(fRendering.getBytesPerColumn() / fRendering.getAddressableSize())), |
| address); |
| |
| fSelectionStarted = true; |
| |
| new CopyDefaultAction(fRendering, DND.SELECTION_CLIPBOARD).run(); |
| } |
| } catch (DebugException e) { |
| fRendering.logError(TraditionalRenderingMessages.getString("TraditionalRendering.FAILURE_START_SELECTION"), //$NON-NLS-1$ |
| e); |
| } |
| } |
| |
| protected void endSelection(int x, int y) { |
| appendSelection(x, y); |
| |
| fSelectionInProgress = false; |
| } |
| |
| protected void appendSelection(int x, int y) { |
| try { |
| if (this.fSelectionStartAddress == null) |
| return; |
| |
| BigInteger address = getViewportAddress(x / getCellWidth(), y / getCellHeight()); |
| |
| if (address.compareTo(this.fSelectionStartAddress) == 0) { |
| // deal with sub cell selection |
| Point cellPosition = getCellLocation(address); |
| int offset = x - cellPosition.x; |
| int subCellCharacterPosition = offset / getCellCharacterWidth(); |
| |
| if (Math.abs(subCellCharacterPosition - this.fSelectionStartAddressSubPosition) > this |
| .getCellCharacterCount() / 4) { |
| fRendering.getSelection() |
| .setEnd(address.add(BigInteger.valueOf(fRendering.getAddressesPerColumn())), address); |
| } else { |
| fRendering.getSelection().setEnd(null, null); |
| } |
| } else { |
| fRendering.getSelection().setEnd(address.add(BigInteger.valueOf(fRendering.getAddressesPerColumn())), |
| address); |
| } |
| |
| if (fRendering.getSelection().getEnd() != null) { |
| this.fCaretAddress = fRendering.getSelection().getEnd(); |
| this.fSubCellCaretPosition = 0; |
| } |
| |
| updateCaret(); |
| |
| new CopyDefaultAction(fRendering, DND.SELECTION_CLIPBOARD).run(); |
| } catch (DebugException e) { |
| fRendering.logError(TraditionalRenderingMessages.getString("TraditionalRendering.FAILURE_APPEND_SELECTION"), //$NON-NLS-1$ |
| e); |
| } |
| } |
| |
| protected void paint(PaintEvent pe) { |
| fRowCount = getBounds().height / getCellHeight(); |
| |
| if (fRendering.isDirty()) { |
| fRendering.setDirty(false); |
| fRendering.refresh(); |
| } |
| } |
| |
| abstract protected BigInteger getViewportAddress(int col, int row) throws DebugException; |
| |
| protected Point getCellLocation(BigInteger address) { |
| return null; |
| } |
| |
| protected String getCellText(MemoryByte bytes[]) { |
| return null; |
| } |
| |
| abstract protected int getCellWidth(); |
| |
| abstract protected int getCellCharacterCount(); |
| |
| @Override |
| public void setFont(Font font) { |
| super.setFont(font); |
| fCharacterWidth = -1; |
| fTextHeight = -1; |
| } |
| |
| protected int getCellHeight() { |
| // If additional information is to be inserted between lines |
| // double the height |
| int multiplier = fRendering.hasVisibleRangeInfo() ? 2 : 1; |
| int cellHeight = getCellTextHeight() * multiplier + (fRendering.getCellPadding() * 2); |
| |
| return cellHeight; |
| } |
| |
| private int fCharacterWidth = -1; // called often, cache |
| |
| protected int getCellCharacterWidth() { |
| if (fCharacterWidth == -1) { |
| GC gc = new GC(this); |
| gc.setFont(fRendering.getFont()); |
| fCharacterWidth = gc.getAdvanceWidth('F'); |
| gc.dispose(); |
| } |
| |
| return fCharacterWidth; |
| } |
| |
| private int fTextHeight = -1; // called often, cache |
| |
| protected int getCellTextHeight() { |
| if (fTextHeight == -1) { |
| GC gc = new GC(this); |
| gc.setFont(fRendering.getFont()); |
| FontMetrics fontMetrics = gc.getFontMetrics(); |
| fTextHeight = fontMetrics.getHeight(); |
| gc.dispose(); |
| } |
| return fTextHeight; |
| } |
| |
| protected boolean shouldDrawBox(TraditionalMemoryByte bytes[], int col) { |
| TraditionalRendering ren = fRendering.getTraditionalRendering(); |
| if (ren.getBoxEdit()) { |
| for (TraditionalMemoryByte tmb : bytes) { |
| if (tmb.isEdited()) { |
| return true; |
| } |
| } |
| } |
| |
| if (ren.getBoxChanged()) { |
| for (int i = 0; i < fRendering.getHistoryDepth(); i++) { |
| for (TraditionalMemoryByte tmb : bytes) { |
| if (tmb.isChanged(i)) { |
| return true; |
| } |
| } |
| } |
| } |
| |
| return false; |
| } |
| } |