diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/Annotation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/Annotation.java index e513f417b2..cd0bcd5581 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/Annotation.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/Annotation.java @@ -27,7 +27,7 @@ import ghidra.util.classfinder.ClassSearcher; public class Annotation { /** - * A pattern to match text between two quote characters and to capture that text. This + * A pattern to match text between two quote characters and to capture that text. This * pattern does not match quote characters that are escaped with a '\' character. */ private static final Pattern QUOTATION_PATTERN = @@ -47,7 +47,7 @@ public class Annotation { return ANNOTATED_STRING_MAP; } - // locates AnnotatedStringHandler implementations to handle annotations + // locates AnnotatedStringHandler implementations to handle annotations private static Map createAnnotatedStringHandlerMap() { Map map = new HashMap<>(); @@ -70,8 +70,9 @@ public class Annotation { * Note: This constructor assumes that the string starts with "{
@
" and ends with '}' * * @param annotationText The complete annotation text. - * @param prototypeString An AttributedString that provides the attributes for the display + * @param prototypeString An AttributedString that provides the attributes for the display * text this Annotation can create + * @param program the program */ public Annotation(String annotationText, AttributedString prototypeString, Program program) { @@ -126,7 +127,7 @@ public class Annotation { * Called when a mouse click occurs on a FieldElement containing this Annotation. * * @param sourceNavigatable The source navigatable associated with the mouse click. - * @param serviceProvider The service provider to be used when creating + * @param serviceProvider The service provider to be used when creating * {@link AnnotatedStringHandler} instances. * @return true if the handler desires to handle the mouse click. */ @@ -143,13 +144,13 @@ public class Annotation { buffer.delete(0, 2); // remove '{' and '@' buffer.deleteCharAt(buffer.length() - 1); - // first split out the tokens on '"' so that annotations can have groupings with + // first split out the tokens on '"' so that annotations can have groupings with // whitespace int unqouotedOffset = 0; List tokens = new ArrayList<>(); Matcher matcher = QUOTATION_PATTERN.matcher(buffer.toString()); while (matcher.find()) { - // put all text in the buffer, + // put all text in the buffer, int quoteStart = matcher.start(); String contentBeforeQuote = buffer.substring(unqouotedOffset, quoteStart); grabTokens(tokens, contentBeforeQuote); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/bean/field/AnnotatedTextFieldElement.java b/Ghidra/Features/Base/src/main/java/ghidra/util/bean/field/AnnotatedTextFieldElement.java index de6f2fd326..ce702452e2 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/util/bean/field/AnnotatedTextFieldElement.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/util/bean/field/AnnotatedTextFieldElement.java @@ -23,9 +23,9 @@ import ghidra.framework.plugintool.ServiceProvider; import ghidra.program.util.ProgramLocation; /** - * A subclass of {@link FieldElement} that allows for mouse handling callbacks via the + * A subclass of {@link FieldElement} that allows for mouse handling callbacks via the * {@link #handleMouseClicked(Navigatable, ServiceProvider)} method. This class - * is based upon {@link Annotation} objects, which are elements that perform actions when the + * is based upon {@link Annotation} objects, which are elements that perform actions when the * use clicks an instance of this class in the display. */ final public class AnnotatedTextFieldElement extends AbstractTextFieldElement { @@ -56,13 +56,23 @@ final public class AnnotatedTextFieldElement extends AbstractTextFieldElement { * Returns the original annotation text in the data model, which will differ from the display * text. * @return the original annotation text in the data model. + * @see #getDisplayString() */ public String getRawText() { return annotation.getAnnotationText(); } /** - * This method is designed to be called when a mouse click has occurred for a given + * Returns the display string of annotation + * @return the display string + * @see #getRawText() + */ + public String getDisplayString() { + return annotation.getDisplayString().getText(); + } + + /** + * This method is designed to be called when a mouse click has occurred for a given * {@link ProgramLocation}. * * @param sourceNavigatable The source Navigatable diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/ClangHighlightController.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/ClangHighlightController.java index ad67cffac0..bae9a14109 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/ClangHighlightController.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/ClangHighlightController.java @@ -35,7 +35,7 @@ import util.CollectionUtils; /** * Class to handle highlights for a decompiled function. * - *

This class does not painting directly. Rather, this class tracks the currently highlighted + *

This class does not paint directly. Rather, this class tracks the currently highlighted * tokens and then sets the highlight color on the token when it is highlighted and clears the * highlight color when the highlight is removed. * @@ -206,7 +206,7 @@ public abstract class ClangHighlightController { * Return the current highlighted token (if exists and unique) * @return token or null */ - private ClangToken getHighlightedToken() { + public ClangToken getHighlightedToken() { if (primaryHighlightTokens.size() == 1) { HighlightToken hlToken = CollectionUtils.any(primaryHighlightTokens); return hlToken.getToken(); diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerPanel.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerPanel.java index 74b8d6eeb6..bf839db6d7 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerPanel.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerPanel.java @@ -259,10 +259,6 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field highlightController.addPrimaryHighlights(root, ops, hlColor); } - public String getHighlightedText() { - return highlightController.getPrimaryHighlightedText(); - } - public void setHighlightController(ClangHighlightController highlightController) { if (this.highlightController != null) { this.highlightController.removeListener(this); @@ -698,6 +694,11 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field highlightersById.clear(); } + public FontMetrics getFontMetrics() { + Font font = options.getDefaultFont(); + return super.getFontMetrics(font); + } + private FontMetrics getFontMetrics(DecompileOptions decompileOptions) { Font font = decompileOptions.getDefaultFont(); return getFontMetrics(font); @@ -756,7 +757,7 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field tryGoToVarnode((ClangVariableToken) token, newWindow); } else if (token instanceof ClangCommentToken) { - tryGoToComment(location, event, textField, token, newWindow); + tryGoToComment(location, event, textField, newWindow); } else if (token instanceof ClangSyntaxToken) { tryGoToSyntaxToken((ClangSyntaxToken) token); @@ -764,10 +765,9 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field } private void tryGoToComment(FieldLocation location, MouseEvent event, ClangTextField textField, - ClangToken token, boolean newWindow) { + boolean newWindow) { - // special cases - // -comments: these no longer use tokens for each item, but are one composite field + // comments may use annotations; tell the annotation it was clicked FieldElement clickedElement = textField.getClickedObject(location); if (clickedElement instanceof AnnotatedTextFieldElement) { AnnotatedTextFieldElement annotation = (AnnotatedTextFieldElement) clickedElement; @@ -775,7 +775,7 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field return; } - String text = clickedElement.getText(); + String text = textField.getText(); String word = StringUtilities.findWord(text, location.col); tryGoToScalar(word, newWindow); } @@ -879,7 +879,7 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field Address addr = space.getAddress(NumericUtilities.parseHexLong(offsetStr), true); controller.goToAddress(addr, newWindow); } - catch (Exception e) { + catch (AddressOutOfBoundsException e) { // give-up } return; @@ -888,7 +888,7 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field long value = NumericUtilities.parseHexLong(text); controller.goToScalar(value, newWindow); } - catch (Exception e) { + catch (NumberFormatException e) { return; // give up } } @@ -1017,6 +1017,49 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field return SPECIAL_COLOR_DEF; } + public String getHighlightedText() { + ClangToken token = highlightController.getHighlightedToken(); + if (token == null) { + return null; + } + if (token instanceof ClangCommentToken) { + return null; // comments are not single words that get highlighted + } + return token.getText(); + } + + public String getTextUnderCursor() { + + FieldLocation location = fieldPanel.getCursorLocation(); + ClangTextField textField = (ClangTextField) fieldPanel.getCurrentField(); + if (textField == null) { + return null; + } + + ClangToken token = textField.getToken(location); + if (!(token instanceof ClangCommentToken)) { + return token.getText(); // non-comment tokens are not multi-word; use the token's text + } + + FieldElement clickedElement = textField.getClickedObject(location); + if (clickedElement instanceof AnnotatedTextFieldElement) { + AnnotatedTextFieldElement annotation = (AnnotatedTextFieldElement) clickedElement; + return annotation.getDisplayString(); + } + + String text = textField.getText(); + return StringUtilities.findWord(text, location.col); + } + + public String getSelectedText() { + FieldSelection selection = fieldPanel.getSelection(); + if (selection.isEmpty()) { + return null; + } + + return FieldSelectionHelper.getFieldSelectionText(selection, fieldPanel); + } + public FieldLocation getCursorPosition() { return fieldPanel.getCursorLocation(); } @@ -1047,15 +1090,6 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field return null; } - public String getTextSelection() { - FieldSelection selection = fieldPanel.getSelection(); - if (selection.isEmpty()) { - return null; - } - - return FieldSelectionHelper.getFieldSelectionText(selection, fieldPanel); - } - public ClangToken getTokenAtCursor() { FieldLocation cursorPosition = fieldPanel.getCursorLocation(); Field field = fieldPanel.getCurrentField(); diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/LocationClangHighlightController.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/LocationClangHighlightController.java index c4d14cfe6e..761e586c86 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/LocationClangHighlightController.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/LocationClangHighlightController.java @@ -15,6 +15,8 @@ */ package ghidra.app.decompiler.component; +import org.apache.commons.lang3.StringUtils; + import docking.widgets.EventTrigger; import docking.widgets.fieldpanel.field.Field; import docking.widgets.fieldpanel.support.FieldLocation; @@ -40,6 +42,11 @@ public class LocationClangHighlightController extends ClangHighlightController { return; } + String text = tok.getText(); + if (StringUtils.isBlank(text)) { + return; // do not highlight whitespace + } + addPrimaryHighlight(tok, defaultHighlightColor); if (tok instanceof ClangSyntaxToken) { addPrimaryHighlightToTokensForParenthesis((ClangSyntaxToken) tok, defaultParenColor); diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/DecompilerProvider.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/DecompilerProvider.java index 714a4ec756..a303c02426 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/DecompilerProvider.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/DecompilerProvider.java @@ -462,7 +462,7 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter @Override public String getTextSelection() { DecompilerPanel decompilerPanel = controller.getDecompilerPanel(); - return decompilerPanel.getTextSelection(); + return decompilerPanel.getSelectedText(); } boolean isBusy() { diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/FindAction.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/FindAction.java index a0ee3c155b..4fd62f0430 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/FindAction.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/FindAction.java @@ -19,6 +19,8 @@ import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.util.List; +import org.apache.commons.lang3.StringUtils; + import docking.action.KeyBindingData; import docking.action.MenuData; import docking.widgets.*; @@ -126,8 +128,15 @@ public class FindAction extends AbstractDecompilerAction { protected void decompilerActionPerformed(DecompilerActionContext context) { DecompilerPanel decompilerPanel = context.getDecompilerPanel(); FindDialog dialog = getFindDialog(decompilerPanel); - String text = decompilerPanel.getHighlightedText(); - if (text != null) { + String text = decompilerPanel.getSelectedText(); + if (text == null) { + text = decompilerPanel.getHighlightedText(); + + // note: if we decide to grab the text under the cursor, then use + // text = decompilerPanel.getTextUnderCursor(); + } + + if (!StringUtils.isBlank(text)) { dialog.setSearchText(text); } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/FieldPanel.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/FieldPanel.java index 2d7cd01ce6..280612ee87 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/FieldPanel.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/FieldPanel.java @@ -392,6 +392,8 @@ public class FieldPanel extends JPanel /** * Returns the default background color. + * @return the default background color. + * @see #getBackground() */ public Color getBackgroundColor() { return backgroundColorModel.getDefaultBackgroundColor(); @@ -431,6 +433,7 @@ public class FieldPanel extends JPanel /** * * Returns the foreground color. + * @return the foreground color. */ public Color getForegroundColor() { return paintContext.getForeground(); @@ -438,6 +441,7 @@ public class FieldPanel extends JPanel /** * Returns the color used as the background for selected items. + * @return the color used as the background for selected items. */ public Color getSelectionColor() { return paintContext.getSelectionColor(); @@ -445,18 +449,24 @@ public class FieldPanel extends JPanel /** * Returns the color color used as the background for highlighted items. + * @return the color color used as the background for highlighted items. */ public Color getHighlightColor() { return paintContext.getHighlightColor(); } /** - * Returns the current cursor color. + * Returns the cursor color when this field panel is focused. + * @return the cursor color when this field panel is focused. */ public Color getFocusedCursorColor() { return paintContext.getFocusedCursorColor(); } + /** + * Returns the cursor color when this field panel is not focused. + * @return the cursor color when this field panel is not focused. + */ public Color getNonFocusCursorColor() { return paintContext.getNotFocusedCursorColor(); } @@ -485,6 +495,7 @@ public class FieldPanel extends JPanel /** * Returns the point in pixels of where the cursor is located. + * @return the point in pixels of where the cursor is located. */ public Point getCursorPoint() { Rectangle bounds = getCursorBounds(); @@ -529,7 +540,7 @@ public class FieldPanel extends JPanel selectionListeners.remove(listener); } - /** + /** * Adds a selection listener that will be notified while the selection is being created * @param listener the listener to be notified */ @@ -537,7 +548,7 @@ public class FieldPanel extends JPanel liveSelectionListeners.add(listener); } - /** + /** * Removes the selection listener from being notified when the selection is being created * @param listener the listener to be removed from being notified */ @@ -680,6 +691,7 @@ public class FieldPanel extends JPanel /** * Returns the current selection. + * @return the current selection. */ public FieldSelection getSelection() { return new FieldSelection(selection); @@ -687,6 +699,7 @@ public class FieldPanel extends JPanel /** * Returns the current highlight (marked area). + * @return the current highlight (marked area). */ public FieldSelection getHighlight() { return new FieldSelection(highlight); @@ -730,7 +743,6 @@ public class FieldPanel extends JPanel return setCursorPosition(index, fieldNum, row, col, EventTrigger.API_CALL); } - // for subclasses to control the event trigger /** * Sets the cursorPosition to the given location with the given trigger. * @@ -762,6 +774,7 @@ public class FieldPanel extends JPanel /** * Returns the state of the cursor. True if on, false if off. + * @return the state of the cursor. True if on, false if off. */ public boolean isCursorOn() { return cursorHandler.isCursorOn(); @@ -858,6 +871,7 @@ public class FieldPanel extends JPanel * that layout. For example, if the layout is completely displayed, yPos will be 0. If part of * the layout is off the top off the screen, then yPos will have a negative value (indicating * that it begins above the displayable part of the screen. + * @return the position */ public ViewerPosition getViewerPosition() { if (layouts.size() > 0) { @@ -872,7 +886,8 @@ public class FieldPanel extends JPanel * <= 0, meaning the layout may be partially off the top of the screen. * * @param index the index of the layout to show at the top of the screen. - * @param yPos the position to show the layout. + * @param xPos the x position to set. + * @param yPos the y position to set. */ public void setViewerPosition(BigInteger index, int xPos, int yPos) { if (index.compareTo(BigInteger.ZERO) >= 0 && index.compareTo(model.getNumIndexes()) < 0) { @@ -909,7 +924,6 @@ public class FieldPanel extends JPanel } @Override - // BigLayoutModelListener public void dataChanged(BigInteger start, BigInteger end) { if (layouts.isEmpty()) { notifyScrollListenerDataChanged(start, end); @@ -961,7 +975,6 @@ public class FieldPanel extends JPanel } @Override - // BigLayoutModelListener public void modelSizeChanged(IndexMapper indexMapper) { BigInteger anchorIndex = layouts.isEmpty() ? BigInteger.ZERO : indexMapper.map(layouts.get(0).getIndex()); @@ -1044,6 +1057,7 @@ public class FieldPanel extends JPanel /** * Returns the offset of the cursor from the top of the screen + * @return the offset of the cursor from the top of the screen */ public int getCursorOffset() { return getOffset(cursorPosition); @@ -1238,7 +1252,9 @@ public class FieldPanel extends JPanel } /** - * Finds the layout containing the given point. + * Finds the layout containing the given y position. + * @param y the y position. + * @return the layout. */ AnchoredLayout findLayoutAt(int y) { for (AnchoredLayout layout : layouts) { @@ -1304,10 +1320,11 @@ public class FieldPanel extends JPanel } } -// ================================================================================================== +//================================================================================================== // Inner Classes -// ================================================================================================== - public class FieldPanelMouseAdapter extends MouseAdapter { +//================================================================================================== + + private class FieldPanelMouseAdapter extends MouseAdapter { @Override public void mousePressed(MouseEvent e) { @@ -1329,12 +1346,12 @@ public class FieldPanel extends JPanel hoverHandler.hoverExited(); } - public boolean isButton3(MouseEvent e) { + private boolean isButton3(MouseEvent e) { return e.getButton() == MouseEvent.BUTTON3; } } - public class FieldPanelMouseMotionAdapter extends MouseMotionAdapter { + private class FieldPanelMouseMotionAdapter extends MouseMotionAdapter { @Override public void mouseDragged(MouseEvent e) { hoverHandler.stopHover(); @@ -1347,74 +1364,74 @@ public class FieldPanel extends JPanel } } - interface KeyAction { + public interface KeyAction { public void handleKeyEvent(KeyEvent event); } - class UpKeyAction implements KeyAction { + private class UpKeyAction implements KeyAction { @Override public void handleKeyEvent(KeyEvent e) { keyHandler.vkUp(e); } } - class DownKeyAction implements KeyAction { + private class DownKeyAction implements KeyAction { @Override public void handleKeyEvent(KeyEvent e) { keyHandler.vkDown(e); } } - class LeftKeyAction implements KeyAction { + private class LeftKeyAction implements KeyAction { @Override public void handleKeyEvent(KeyEvent e) { keyHandler.vkLeft(e); } } - class RightKeyAction implements KeyAction { + private class RightKeyAction implements KeyAction { @Override public void handleKeyEvent(KeyEvent e) { keyHandler.vkRight(e); } } - class HomeKeyAction implements KeyAction { + private class HomeKeyAction implements KeyAction { @Override public void handleKeyEvent(KeyEvent e) { keyHandler.vkHome(e); } } - class EndKeyAction implements KeyAction { + private class EndKeyAction implements KeyAction { @Override public void handleKeyEvent(KeyEvent e) { keyHandler.vkEnd(e); } } - class PageUpKeyAction implements KeyAction { + private class PageUpKeyAction implements KeyAction { @Override public void handleKeyEvent(KeyEvent e) { keyHandler.vkPageUp(e); } } - class PageDownKeyAction implements KeyAction { + private class PageDownKeyAction implements KeyAction { @Override public void handleKeyEvent(KeyEvent e) { keyHandler.vkPageDown(e); } } - class EnterKeyAction implements KeyAction { + private class EnterKeyAction implements KeyAction { @Override public void handleKeyEvent(KeyEvent e) { keyHandler.vkEnter(e); } } - class FieldPanelKeyAdapter extends KeyAdapter { + private class FieldPanelKeyAdapter extends KeyAdapter { private Map actionMap; FieldPanelKeyAdapter() { @@ -1460,8 +1477,7 @@ public class FieldPanel extends JPanel // Shift is handled special, so mask it off in the event before getting the action. // If the shift is being held, the selection is extended while moving the cursor. int keyCode = e.getKeyCode(); - int modifiers = - e.getModifiers() & ~(InputEvent.SHIFT_DOWN_MASK | InputEvent.SHIFT_MASK); + int modifiers = e.getModifiersEx() & ~InputEvent.SHIFT_DOWN_MASK; KeyEvent maskedEvent = new KeyEvent(e.getComponent(), e.getID(), e.getWhen(), modifiers, keyCode, e.getKeyChar(), e.getKeyLocation()); @@ -1490,7 +1506,7 @@ public class FieldPanel extends JPanel } } - public class FieldPanelFocusListener implements FocusListener { + private class FieldPanelFocusListener implements FocusListener { @Override public void focusGained(FocusEvent e) { inFocus = true; @@ -1511,7 +1527,7 @@ public class FieldPanel extends JPanel } } - public class BigFieldPanelMouseWheelListener implements MouseWheelListener { + private class BigFieldPanelMouseWheelListener implements MouseWheelListener { @Override public void mouseWheelMoved(MouseWheelEvent e) { double wheelRotation = e.getPreciseWheelRotation(); @@ -1555,7 +1571,7 @@ public class FieldPanel extends JPanel } } - public class MouseHandler implements ActionListener { + private class MouseHandler implements ActionListener { private Timer scrollTimer; // used to generate auto scroll private int mouseDownX; private int mouseDownY; @@ -1563,11 +1579,11 @@ public class FieldPanel extends JPanel private int timerScrollAmount; private FieldLocation timerPoint; - public MouseHandler() { + MouseHandler() { scrollTimer = new Timer(100, this); } - public void dispose() { + void dispose() { scrollTimer.stop(); } @@ -1588,7 +1604,7 @@ public class FieldPanel extends JPanel } } - public void mousePressed(MouseEvent e) { + void mousePressed(MouseEvent e) { requestFocus(); didDrag = false; if (e.getButton() != MouseEvent.BUTTON1) { @@ -1624,11 +1640,11 @@ public class FieldPanel extends JPanel } } - public boolean didDrag() { + boolean didDrag() { return didDrag; } - public void mouseDragged(MouseEvent e) { + void mouseDragged(MouseEvent e) { if ((e.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK) == 0) { return; } @@ -1652,7 +1668,7 @@ public class FieldPanel extends JPanel } } - public void mouseReleased(MouseEvent e) { + void mouseReleased(MouseEvent e) { scrollTimer.stop(); if (e.getButton() != MouseEvent.BUTTON1) { return; @@ -1661,7 +1677,7 @@ public class FieldPanel extends JPanel cursorHandler.setCursorPos(e.getX(), e.getY(), EventTrigger.GUI_ACTION); if (didDrag) { // Send an event after the drag is finished. Event are suppressed while dragging, - // meaning that the above call to setCursorPos() will not have fired an event + // meaning that the above call to setCursorPos() will not have fired an event // because the internal cursor position did not change during the mouse release. cursorHandler.notifyCursorChanged(EventTrigger.GUI_ACTION); } @@ -1672,55 +1688,51 @@ public class FieldPanel extends JPanel } /** - * Basically checks if the the "shift" modifier is on and the "control" modifier is not. - * Note that "control" is operating system dependent. It is on windows, and - * on mac. + * Checks if the the "shift" modifier is on and the "control" modifier is not. */ private boolean isAddToContiguousSelectionActivator(MouseEvent e) { return (e.isShiftDown() && !DockingUtils.isControlModifier(e)); } /** - * Basically checks if the the "control" modifier is on and the shift modifier is not. Note - * that "control" is operating system dependent. It is on windows, and - * on mac. + * Checks if the the "control" modifier is on and the shift modifier is not. */ private boolean isAddRemoveDisjointSelectionActivator(MouseEvent e) { return DockingUtils.isControlModifier(e) && !e.isShiftDown(); } } - class KeyHandler { + private class KeyHandler { - public void shiftKeyPressed() { + void shiftKeyPressed() { selectionHandler.beginSelectionSequence(cursorPosition); } - public void shiftKeyReleased() { + void shiftKeyReleased() { selectionHandler.endSelectionSequence(); } - public void vkUp(KeyEvent e) { + void vkUp(KeyEvent e) { cursorHandler.doCursorUp(EventTrigger.GUI_ACTION); selectionHandler.updateSelectionSequence(cursorPosition); } - public void vkDown(KeyEvent e) { + void vkDown(KeyEvent e) { cursorHandler.doCursorDown(EventTrigger.GUI_ACTION); selectionHandler.updateSelectionSequence(cursorPosition); } - public void vkLeft(KeyEvent e) { + void vkLeft(KeyEvent e) { cursorHandler.doCursorLeft(EventTrigger.GUI_ACTION); selectionHandler.updateSelectionSequence(cursorPosition); } - public void vkRight(KeyEvent e) { + void vkRight(KeyEvent e) { cursorHandler.doCursorRight(EventTrigger.GUI_ACTION); selectionHandler.updateSelectionSequence(cursorPosition); } - public void vkEnd(KeyEvent e) { + void vkEnd(KeyEvent e) { if (DockingUtils.isControlModifier(e)) { doEndOfFile(EventTrigger.GUI_ACTION); } @@ -1730,7 +1742,7 @@ public class FieldPanel extends JPanel selectionHandler.updateSelectionSequence(cursorPosition); } - public void vkHome(KeyEvent e) { + void vkHome(KeyEvent e) { if (DockingUtils.isControlModifier(e)) { doTopOfFile(EventTrigger.GUI_ACTION); } @@ -1740,27 +1752,26 @@ public class FieldPanel extends JPanel selectionHandler.updateSelectionSequence(cursorPosition); } - public void vkPageUp(KeyEvent e) { + void vkPageUp(KeyEvent e) { doPageUp(EventTrigger.GUI_ACTION); selectionHandler.updateSelectionSequence(cursorPosition); } - public void vkPageDown(KeyEvent e) { + void vkPageDown(KeyEvent e) { doPageDown(EventTrigger.GUI_ACTION); selectionHandler.updateSelectionSequence(cursorPosition); } - public void vkEnter(KeyEvent e) { + void vkEnter(KeyEvent e) { Point pt = getCursorPoint(); if (pt != null) { notifyFieldMouseListeners(new MouseEvent(e.getComponent(), e.getID(), e.getWhen(), 0, pt.x, pt.y, 2, false, MouseEvent.BUTTON1)); } } - } - class SelectionHandler { + private class SelectionHandler { private boolean selectionOn = true; private boolean selectionChanged; private boolean removeFromSelection; @@ -1828,7 +1839,7 @@ public class FieldPanel extends JPanel selectionChanged = true; } - public void enableSelection(boolean b) { + void enableSelection(boolean b) { selectionOn = b; if (!selectionOn) { selection.clear(); @@ -1836,7 +1847,7 @@ public class FieldPanel extends JPanel } } - public class CursorHandler { + private class CursorHandler { private int lastX = 0; private boolean cursorOn = true; private Field currentField; @@ -1846,7 +1857,7 @@ public class FieldPanel extends JPanel cursorBlinker = new CursorBlinker(FieldPanel.this); } - public void setBlinkCursor(Boolean blinkCursor) { + void setBlinkCursor(Boolean blinkCursor) { if (blinkCursor && cursorBlinker == null) { cursorBlinker = new CursorBlinker(FieldPanel.this); } @@ -1856,28 +1867,28 @@ public class FieldPanel extends JPanel } } - public boolean isCursorOn() { + boolean isCursorOn() { return cursorOn; } - public void setCursorOn(boolean cursorOn) { + void setCursorOn(boolean cursorOn) { this.cursorOn = cursorOn; } - public void focusLost() { + void focusLost() { if (cursorBlinker != null) { cursorBlinker.stop(); } } - public void focusGained() { + void focusGained() { if (cursorBlinker != null) { cursorBlinker.restart(); } } - public Field getCurrentField() { + Field getCurrentField() { if (currentField == null) { AnchoredLayout layout = findLayoutOnScreen(cursorPosition.getIndex()); if (layout != null) { @@ -2135,7 +2146,7 @@ public class FieldPanel extends JPanel } - public void scrollToCursor() { + void scrollToCursor() { doScrollTo(cursorPosition); } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/support/FieldLocation.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/support/FieldLocation.java index b7c6e835dd..547f0ea651 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/support/FieldLocation.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/support/FieldLocation.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,8 +19,36 @@ import java.math.BigInteger; import org.jdom.Element; +import docking.widgets.fieldpanel.Layout; +import docking.widgets.fieldpanel.field.Field; + /** - * Class to represent locations within the FieldViewer. + * Class to represent {@link Field} locations within the field viewer. + *

+ * A field location represents a place within a Field. Fields live within a concept we call a + * layout. A layout represents an 'item', for example an address, along with a grouping of + * related information. Each layout will contain one or more Field objects. Further, each + * layout's fields may have varying shapes, such as single or multiple rows within the layout. + * Thusly, a layout could conceptually represent a single line of text or multiple groupings of + * text and images, similar to how a newspaper or web page is laid out. + *

+ * A layout lives in a larger collection of layouts, which are laid out vertically. The index of a + * layout is its position within that larger list. This class contains the index of the layout + * within which it lives. + *

+ * A {@link FieldSelection} may be within a single layout or may cross multiple layouts. To + * determine if a selection crosses multiple layouts, you can get the {@link FieldRange range} of + * the selection. You can then use the range's start and end locations to determine if the + * selection spans multiple layouts. If the start and end indexes of the range are the same, then + * the selection is within a single layout; otherwise, the selection spans multiple layouts. + *

+ * This location also contains row and column values. These values refer to the row and column of + * text within a single Field. Lastly, this class contains a field number, which represents the + * relative field number inside of the over layout, which may contain multiple fields. + * + * @see FieldSelection + * @see FieldRange + * @see Layout */ public class FieldLocation implements Comparable { public static final FieldLocation MAX = @@ -29,7 +56,7 @@ public class FieldLocation implements Comparable { Integer.MAX_VALUE); public int fieldNum; // the number of the field for this location - public int row; // the row position within the field . + public int row; // the row position within the field public int col; // the col position within the field private BigInteger index; @@ -94,12 +121,19 @@ public class FieldLocation implements Comparable { this(loc.index, loc.fieldNum, loc.row, loc.col); } + /** + * Returns the index for this location. The index corresponds to the layout that contains + * the field represented by this location. See the javadoc header for more details. + * @return the index for this location. + */ public BigInteger getIndex() { return index; } /** - * Returns the field index for this location. + * Returns the number of the field for this location. This is the number of the field within + * a given layout. See the javadoc header for more details. + * @return the number of the field for this location. */ public int getFieldNum() { return fieldNum; @@ -107,6 +141,7 @@ public class FieldLocation implements Comparable { /** * Returns the row within the Field for this location. + * @return the row within the Field for this location. */ public int getRow() { return row; @@ -114,15 +149,12 @@ public class FieldLocation implements Comparable { /** * Returns the column within the Field for this location. + * @return the column within the Field for this location. */ public int getCol() { return col; } - /** - * - * @see java.lang.Object#equals(java.lang.Object) - */ @Override public boolean equals(Object obj) { if (obj == null) { @@ -141,6 +173,7 @@ public class FieldLocation implements Comparable { return false; } + @Override public int compareTo(FieldLocation o) { int compareTo = index.compareTo(o.index); if (compareTo != 0) { @@ -167,19 +200,11 @@ public class FieldLocation implements Comparable { return 0; } - /** - * - * @see java.lang.Object#hashCode() - */ @Override public int hashCode() { return index.intValue() + fieldNum * 100 + row * 10 + col; } - /** - * - * @see java.lang.Object#toString() - */ @Override public String toString() { return index.toString() + ", " + fieldNum + ", " + row + ", " + col; @@ -200,7 +225,6 @@ public class FieldLocation implements Comparable { fieldNum = loc.fieldNum; row = loc.row; col = loc.col; - } public void setIndex(BigInteger index) { diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/support/FieldRange.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/support/FieldRange.java index d7124969ce..b677dd3dc8 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/support/FieldRange.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/support/FieldRange.java @@ -15,20 +15,24 @@ */ package docking.widgets.fieldpanel.support; -import ghidra.util.exception.AssertException; - import java.math.BigInteger; import org.jdom.Element; +import docking.widgets.fieldpanel.Layout; +import ghidra.util.exception.AssertException; + /** - * Class to a range consisting of a start position within a start row to an end position within an - * end row (exclusive). + * A range consists of a start position within a start row to an end position within an end row + * (exclusive). *

- * Conceptually, this class can be thought of as a range of rows (defined by - * startIndex and endindex) with sub-positions within those rows (defined by - * startField and endField). As an example, consider a text select that begins on - * some word in a row and ends on another word in a different row. + * Conceptually, this class can be thought of as a range of rows (defined by start and end + * indexes) with sub-positions within those rows. As an example, consider a text selection that + * begins on some word in a row and ends on another word in a different row. + * + * @see FieldSelection + * @see FieldLocation + * @see Layout */ public class FieldRange implements Comparable { FieldLocation start; @@ -71,9 +75,6 @@ public class FieldRange implements Comparable { return end; } - /** - * Return string representation for debugging purposes. - */ @Override public String toString() { return "FieldRange: (" + start + " :: " + end + ")"; @@ -108,6 +109,7 @@ public class FieldRange implements Comparable { return start.hashCode() << 16 + end.hashCode(); } + @Override public int compareTo(FieldRange o) { int result = start.compareTo(o.start); if (result == 0) { @@ -190,7 +192,8 @@ public class FieldRange implements Comparable { public boolean containsEntirely(int index) { if (start.getIndex().intValue() > index || - ((start.getIndex().intValue() == index) && (start.fieldNum != 0 || start.row != 0 || start.col != 0))) { + ((start.getIndex().intValue() == index) && + (start.fieldNum != 0 || start.row != 0 || start.col != 0))) { return false; } if (end.getIndex().intValue() <= index) { diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/support/FieldSelection.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/support/FieldSelection.java index db1b5d6eba..1897958e68 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/support/FieldSelection.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/support/FieldSelection.java @@ -15,17 +15,26 @@ */ package docking.widgets.fieldpanel.support; -import ghidra.framework.options.SaveState; -import ghidra.util.Msg; - import java.math.BigInteger; import java.util.*; import org.jdom.Element; +import docking.widgets.fieldpanel.Layout; +import ghidra.framework.options.SaveState; + /** - * Interface for reporting the FieldViewer selection. The selection consists of - * a sequence of ranges of indexes. + * This class represents a selection in a field viewer. + *

+ * A {@link FieldSelection} may be within a single layout or may cross multiple layouts. To + * determine if a selection crosses multiple layouts, you can get the {@link FieldRange range} of + * the selection. You can then use the range's start and end locations to determine if the + * selection spans multiple layouts. If the start and end indexes of the range are the same, then + * the selection is within a single layout; otherwise, the selection spans multiple layouts. + * + * @see FieldRange + * @see FieldLocation + * @see Layout */ public class FieldSelection implements Iterable { @@ -35,7 +44,7 @@ public class FieldSelection implements Iterable { * Construct a new empty FieldSelection. */ public FieldSelection() { - ranges = new ArrayList(4); + ranges = new ArrayList<>(4); } /** @@ -43,7 +52,7 @@ public class FieldSelection implements Iterable { * @param selection the FieldSelection to copy. */ public FieldSelection(FieldSelection selection) { - ranges = new ArrayList(selection.ranges.size()); + ranges = new ArrayList<>(selection.ranges.size()); for (FieldRange range : selection.ranges) { ranges.add(new FieldRange(range)); } @@ -53,7 +62,7 @@ public class FieldSelection implements Iterable { * Removes all indexes from the list. */ public void clear() { - ranges = new ArrayList(4); + ranges = new ArrayList<>(4); } /** @@ -69,6 +78,7 @@ public class FieldSelection implements Iterable { * Returns the range if the given Field at the given index is in the selection. * Otherwise returns null. * @param loc location to find the range for. + * @return the range */ public FieldRange getRangeContaining(FieldLocation loc) { int insertIndex = Collections.binarySearch(ranges, new FieldRange(loc, FieldLocation.MAX)); @@ -95,9 +105,11 @@ public class FieldSelection implements Iterable { /** * Returns true if the all the fields in the layout with the given index are - * included in this selection. + * included in this selection. * @param index index of the layout to test. - + * @return true if the all the fields in the layout with the given index are + * included in this selection. + */ public boolean containsEntirely(BigInteger index) { FieldLocation start = new FieldLocation(index, 0, 0, 0); @@ -229,12 +241,14 @@ public class FieldSelection implements Iterable { } } } + insertIndex++; while (insertIndex < ranges.size()) { FieldRange range = ranges.get(insertIndex); if (!deleteRange.intersects(range)) { return; } + FieldRange leftOver = range.subtract(deleteRange); if (range.isEmpty()) { ranges.remove(insertIndex); @@ -264,6 +278,7 @@ public class FieldSelection implements Iterable { /** * Returns the current number of ranges in the list. + * @return the current number of ranges in the list. */ public int getNumRanges() { return ranges.size(); @@ -272,6 +287,7 @@ public class FieldSelection implements Iterable { /** * Returns the i'th Field Range in the selection. * @param rangeNum the index of the range to retrieve. + * @return the range */ public FieldRange getFieldRange(int rangeNum) { return ranges.get(rangeNum); @@ -279,8 +295,9 @@ public class FieldSelection implements Iterable { /** * Compute the intersection of this field selection and another one. - * The intersection of two field selections is all fields existing in + * The intersection of two field selections is all fields existing in * both selections. + * *

Note: This field selection becomes the intersection. * * @param selection field selection to intersect. @@ -304,6 +321,7 @@ public class FieldSelection implements Iterable { /** * Computes the intersection of this field selection and the given field selection. * @param selection the selection to intersect with. + * @return the selection */ public final FieldSelection findIntersection(FieldSelection selection) { if (selection == null || this.ranges.size() == 0 || selection.ranges.size() == 0) { @@ -323,16 +341,14 @@ public class FieldSelection implements Iterable { /** * Delete all fields in the ranges in the given field selection from this one. - * @param selection the field selection fields to remove from this - * field selection. + * @param selection the field selection fields to remove from this field selection. */ public final void delete(FieldSelection selection) { if (selection == null || this.ranges.size() == 0 || selection.ranges.size() == 0) { return; } - // process all ranges in the selection, delete each one's - // associated fields from this set. + // process all ranges in the selection, delete each associated fields from this set for (FieldRange range : selection.ranges) { removeRange(range.start, range.end); } @@ -340,63 +356,50 @@ public class FieldSelection implements Iterable { /** * Insert all fields in the ranges in the given field selection from this one. - * @param selection the field selection fields to add to this - * field selection. + * @param selection the field selection fields to add to this field selection. */ public final void insert(FieldSelection selection) { if (selection == null || selection.getNumRanges() == 0) { return; } - // process all ranges in the selection, add each one's - // associated fields from this set. + // process all ranges in the selection, add each associated fields from this set for (FieldRange range : selection.ranges) { addRange(range.start, range.end); } } - /** - * Prints out the ranges for debugging. - */ - public void printRanges() { - Msg.debug(this, "*********"); - for (FieldRange range : ranges) { - Msg.debug(this, range); - } - Msg.debug(this, "**********"); - } - @Override public String toString() { - StringBuffer buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); for (FieldRange range : ranges) { buf.append(range.toString()); } return buf.toString(); } - /** - * - * @see java.lang.Object#equals(java.lang.Object) - */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((ranges == null) ? 0 : ranges.hashCode()); + return result; + } + @Override public boolean equals(Object obj) { - if (!(obj instanceof FieldSelection)) { + if (this == obj) { + return true; + } + if (obj == null) { return false; } + if (getClass() != obj.getClass()) { + return false; + } + FieldSelection other = (FieldSelection) obj; - if (ranges.size() != other.ranges.size()) { - return false; - } - int n = ranges.size(); - for (int i = 0; i < n; i++) { - FieldRange thisRange = ranges.get(i); - FieldRange otherRange = other.ranges.get(i); - if (!thisRange.equals(otherRange)) { - return false; - } - } - return true; + return Objects.equals(ranges, other.ranges); } public void save(SaveState saveState) { @@ -424,7 +427,7 @@ public class FieldSelection implements Iterable { } public boolean isEmpty() { - return ranges.size() == 0; + return ranges.isEmpty(); } public FieldSelection intersect(int index) { @@ -448,15 +451,17 @@ public class FieldSelection implements Iterable { int insertIndex = Collections.binarySearch(ranges, range); - // if exact match, return it; + // if exact match, return it if (insertIndex >= 0) { intersection.addRange(range); return intersection; } + insertIndex = -insertIndex - 2; if (insertIndex < 0) { insertIndex++; } + while (insertIndex < ranges.size()) { FieldRange searchRange = ranges.get(insertIndex); if (searchRange.start.compareTo(range.end) >= 0) {