/******************************************************************************* * 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.BigInteger; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.model.MemoryByte; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.osgi.util.NLS; 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.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.widgets.Canvas; import org.eclipse.swt.widgets.Caret; public abstract class FPAbstractPane 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; // Mouse listener class class AbstractPaneMouseListener implements MouseListener { @Override public void mouseUp(MouseEvent me) { // Move the caret positionCaret(me.x, me.y); fCaret.setVisible(true); if (fSelectionInProgress && me.button == 1) endSelection(me.x, me.y); fSelectionInProgress = fSelectionStarted = false; } // Mouse down click @Override public void mouseDown(MouseEvent me) { // Any click, whether inside this cell or elsewhere, terminates the edit and acts the same as a carriage return would. handleCarriageReturn(); // Switch focus and check for selection FPAbstractPane.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 (FPAbstractPane.this.fSelectionStartAddress == null) FPAbstractPane.this.fSelectionStartAddress = fRendering.getSelection().getStart(); FPAbstractPane.this.fSelectionStarted = true; FPAbstractPane.this.appendSelection(me.x, me.y); } else { // Start a new selection FPAbstractPane.this.startSelection(me.x, me.y); } } } // Double click @Override public void mouseDoubleClick(MouseEvent me) { handleMouseDoubleClick(me); } } // Mouse move listener class class AbstractPaneMouseMoveListener implements MouseMoveListener { @Override public void mouseMove(MouseEvent me) { if (fSelectionStarted) { fSelectionInProgress = true; appendSelection(me.x, me.y); } } } // Focus listener class class AbstractPaneFocusListener implements FocusListener { @Override public void focusLost(FocusEvent fe) { IPreferenceStore store = FPRenderingPlugin.getDefault().getPreferenceStore(); if (FPRenderingPreferenceConstants.MEM_EDIT_BUFFER_SAVE_ON_ENTER_ONLY.equals(store.getString(FPRenderingPreferenceConstants.MEM_EDIT_BUFFER_SAVE))) fRendering.getViewportCache().clearEditBuffer(); else fRendering.getViewportCache().writeEditBuffer(); // clear the pane local selection start FPAbstractPane.this.fSelectionStartAddress = null; } @Override public void focusGained(FocusEvent fe) { // Set the floating point edit mode indicator if the user clicked in the Data Pane; otherwise clear it if (FPAbstractPane.this instanceof FPDataPane) fRendering.displayEditModeIndicator(true); else fRendering.displayEditModeIndicator(false); } } // Key listener class class AbstractPaneKeyListener implements KeyListener { @Override public void keyPressed(KeyEvent ke) { fOldSubCellCaretPosition = fSubCellCaretPosition; // Shift 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; } } } // Arrow, Page, Insert, Escape and standard characters 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.INSERT) { handleInsertKey(); } else if (ke.keyCode == SWT.ESC) { fRendering.getViewportCache().clearEditBuffer(); handleCTRLZ(); } else if (ke.character == '\r') { fRendering.getViewportCache().writeEditBuffer(); handleCarriageReturn(); } else if (FPutilities.validEditCharacter(ke.character)) { // Check for selection if (fRendering.getSelection().hasSelection()) { setCaretAddress(fRendering.getSelection().getLow()); fSubCellCaretPosition = 0; } // Add the chatacter to the cell editCell(fCaretAddress, fSubCellCaretPosition, ke.character); } // Control if ((ke.stateMask & SWT.CTRL) != 0) { // CTRL/Z if (ke.keyCode == 'z' || ke.keyCode == 'Z') handleCTRLZ(); } // Alt if ((ke.stateMask & SWT.ALT) != 0) { // Future use } // Shift 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) { // If it's a SHIFT key, keep the selection since we may add to it fRendering.getSelection().clear(); } } @Override public void keyReleased(KeyEvent ke) { // do nothing } } class AbstractPanePaintListener implements PaintListener { @Override public void paintControl(PaintEvent pe) { FPAbstractPane.this.paint(pe); } } public FPAbstractPane(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.addKeyListener(createKeyListener()); this.addFocusListener(createFocusListener()); } // Listener methods protected MouseListener createMouseListener() { return new AbstractPaneMouseListener(); } protected MouseMoveListener createMouseMoveListener() { return new AbstractPaneMouseMoveListener(); } protected FocusListener createFocusListener() { return new AbstractPaneFocusListener(); } protected KeyListener createKeyListener() { return new AbstractPaneKeyListener(); } protected PaintListener createPaintListener() { return new AbstractPanePaintListener(); } // Right arrow protected void handleRightArrowKey() { fSubCellCaretPosition++; if (fSubCellCaretPosition >= getCellCharacterCount()) { // We've moved beyond the end of the cell: End the edit to the previous cell. handleCarriageReturn(); // Move to the next cell; ensure that caret is within the addressable range fSubCellCaretPosition = 0; BigInteger newCaretAddress = fCaretAddress.add(BigInteger.valueOf(fRendering.getFPDataType().getByteLength())); if (newCaretAddress.compareTo(fRendering.getMemoryBlockEndAddress()) > 0) fSubCellCaretPosition = getCellCharacterCount(); else setCaretAddress(newCaretAddress); } updateTheCaret(); ensureCaretWithinViewport(); } // Left arrow protected void handleLeftArrowKey() { fSubCellCaretPosition--; if (fSubCellCaretPosition < 0) { // We've moved beyond the beginning of the cell: This action ends the edit to the previous cell. handleCarriageReturn(); // Move to the previous cell; ensure that caret is within the addressable range fSubCellCaretPosition = getCellCharacterCount() - 1; BigInteger newCaretAddress = fCaretAddress.subtract(BigInteger.valueOf(fRendering.getFPDataType().getByteLength())); if (newCaretAddress.compareTo(fRendering.getMemoryBlockStartAddress()) < 0) fSubCellCaretPosition = 0; else setCaretAddress(newCaretAddress); } updateTheCaret(); ensureCaretWithinViewport(); } // Down arrow protected void handleDownArrowKey() { // We've moved beyond the beginning of the cell: This action ends the edit to the previous cell. handleCarriageReturn(); // Ensure that caret is within the addressable range BigInteger newCaretAddress = fCaretAddress.add(BigInteger.valueOf(fRendering.getFPDataType().getByteLength() * fRendering.getColumnCount())); setCaretAddress(newCaretAddress); updateTheCaret(); ensureCaretWithinViewport(); } // Up arrow protected void handleUpArrowKey() { // We've moved beyond the beginning of the cell: This action ends the edit to the previous cell. handleCarriageReturn(); // Ensure that caret is within the addressable range BigInteger newCaretAddress = fCaretAddress.subtract(BigInteger.valueOf(fRendering.getFPDataType().getByteLength() * fRendering.getColumnCount())); setCaretAddress(newCaretAddress); updateTheCaret(); ensureCaretWithinViewport(); } // Page down protected void handlePageDownKey() { // We've moved beyond the beginning of the cell: This action ends the edit to the previous cell. handleCarriageReturn(); // Ensure that caret is within the addressable range BigInteger newCaretAddress = fCaretAddress.add(BigInteger.valueOf(fRendering.getAddressableCellsPerRow() * (fRendering.getRowCount() - 1))); setCaretAddress(newCaretAddress); updateTheCaret(); ensureCaretWithinViewport(); } // Page up protected void handlePageUpKey() { // We've moved beyond the beginning of the cell: This action ends the edit to the previous cell. handleCarriageReturn(); // Ensure that caret is within the addressable range BigInteger newCaretAddress = fCaretAddress.subtract(BigInteger.valueOf(fRendering.getAddressableCellsPerRow() * (fRendering.getRowCount() - 1))); setCaretAddress(newCaretAddress); updateTheCaret(); ensureCaretWithinViewport(); } // Insert key protected void handleInsertKey() { // If focus is in the Data Pane, toggle Insert/Overwrite mode and make sure the cell edit // status line indicator is displayed. Otherwise, make clear the status line indicator. if (FPAbstractPane.this instanceof FPDataPane) { if (!fRendering.isEditingCell()) fRendering.setInsertMode(!fRendering.insertMode()); fRendering.displayEditModeIndicator(true); } else fRendering.displayEditModeIndicator(false); } // Double-click 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 } } // Carriage return protected void handleCarriageReturn() { // If we're not editing a cell or there is no string buffer to use, nothing to do: Exit edit mode and return. if (!fRendering.isEditingCell() || fRendering.getEditBuffer() == null) { fRendering.endCellEditing(); return; } // Remove all whitespace from the string buffer. fRendering.setEditBuffer(new StringBuffer(fRendering.getEditBuffer().toString().trim().replaceAll(" ", ""))); //$NON-NLS-1$ //$NON-NLS-2$ // Check the string to make sure it's in valid, acceptable form. if (FPutilities.isValidFormat(fRendering.getEditBuffer().toString())) { // Valid string: Convert it to a byte array and write the buffer back to memory; // a subsequent re-draw/paint converts it to normalized scientific notation. fRendering.convertAndUpdateCell(fRendering.getCellEditAddress(), fRendering.getEditBuffer().toString()); } else { // Invalid string: Create the error text and restore the previous value String errorText = NLS.bind(FPRenderingMessages.getString("FPRendering.ERROR_FPENTRY_POPUP_TEXT"), fRendering.getEditBuffer().toString()); //$NON-NLS-1$ try { fRendering.setEditBuffer(new StringBuffer(fRendering.fDataPane.bytesToSciNotation(fRendering.getBytes(fCaretAddress, fRendering.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$ } // Exit cell-edit mode fRendering.endCellEditing(); } // CTRL/Z handling protected void handleCTRLZ() { // CTRL/Z: Replace the cell contents with the original value and exit "number edit mode" try { fRendering.setEditBuffer(new StringBuffer(fRendering.fDataPane.bytesToSciNotation(fRendering.getBytes(fCaretAddress, fRendering.getFPDataType().getByteLength())))); } catch (DebugException e) { e.printStackTrace(); } fRendering.endCellEditing(); } // Other getter/setters protected boolean isPaneVisible() { return fPaneVisible; } protected void setPaneVisible(boolean visible) { fPaneVisible = visible; this.setVisible(visible); } protected int getNumberOfBytesRepresentedByColumn() { return fRendering.getCharsPerColumn(); } protected void editCell(BigInteger cellAddress, int subCellPosition, char character) { // Do nothing; overridden in subclass FPDataPane } // 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; } protected void updateTheCaret() { try { if (fCaretAddress != null) { Point cellPosition = getCellLocation(fCaretAddress); if (cellPosition != null) fCaret.setLocation(cellPosition.x + fSubCellCaretPosition * getCellCharacterWidth(), cellPosition.y); } } catch (Exception e) { fRendering.logError(FPRenderingMessages.getString("FPRendering.FAILURE_POSITION_CURSOR"), e); //$NON-NLS-1$ } } // This method scrolls the viewport to insure that the caret is within the viewable area protected void ensureCaretWithinViewport() // TODO getAddressableSize() > 1 ? { // If the caret is before the viewport start if so, scroll viewport up by several rows BigInteger rowCount = BigInteger.valueOf(getRowCount()); BigInteger rowMemBytes = BigInteger.valueOf(fRendering.getFPDataType().getByteLength() * fRendering.getColumnCount()); BigInteger viewableBytes = rowCount.multiply(rowMemBytes); BigInteger viewableEnd = fRendering.getViewportStartAddress().add(viewableBytes); if (fCaretAddress.compareTo(fRendering.getViewportStartAddress()) < 0) { fRendering.setViewportStartAddress(fRendering.getViewportStartAddress().subtract(rowMemBytes)); fRendering.ensureViewportAddressDisplayable(); fRendering.gotoAddress(fRendering.getViewportStartAddress()); } // If the caret is after the viewport end if so, scroll viewport down by appropriate rows else if (fCaretAddress.compareTo(viewableEnd) >= 0) { fRendering.setViewportStartAddress(fRendering.getViewportStartAddress().add(rowMemBytes)); fRendering.ensureViewportAddressDisplayable(); fRendering.gotoAddress(fRendering.getViewportStartAddress()); } 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; } // Start selection 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.getFPDataType().getByteLength())), address); fSelectionStarted = true; new CopyAction(fRendering, DND.SELECTION_CLIPBOARD).run(); } } catch (DebugException e) { fRendering.logError(FPRenderingMessages.getString("FPRendering.FAILURE_START_SELECTION"), e); //$NON-NLS-1$ } } // End selection 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) { // 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.getFPDataType().getByteLength()))), address); } else { fRendering.getSelection().setEnd(null, null); } } else { fRendering.getSelection().setEnd(address.add(BigInteger.valueOf(fRendering.getFPDataType().getByteLength())), address); } if (fRendering.getSelection().getEnd() != null) { this.fCaretAddress = fRendering.getSelection().getEnd(); this.fSubCellCaretPosition = 0; } updateTheCaret(); new CopyAction(fRendering, DND.SELECTION_CLIPBOARD).run(); } catch (Exception e) { fRendering.logError(FPRenderingMessages.getString("FPRendering.FAILURE_APPEND_SELECTION"), e); //$NON-NLS-1$ } } 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; fCellHeight = -1; fTextHeight = -1; } private int fCellHeight = -1; // called often, cache protected int getCellHeight() { if (fCellHeight == -1) { fCellHeight = getCellTextHeight() + (fRendering.getCellPadding() * 2); } return fCellHeight; } 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; } }