diff --git a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerPanel.java b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerPanel.java index 0867d952b2..e968ce2fd6 100644 --- a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerPanel.java +++ b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerPanel.java @@ -250,7 +250,7 @@ public class ByteViewerPanel extends JPanel startField.setText(start); ByteBlock lastBlock = blocks[blocks.length - 1]; endField.setText(lastBlock - .getLocationRepresentation(lastBlock.getLength().subtract(BigInteger.ONE))); + .getLocationRepresentation(lastBlock.getLength().subtract(BigInteger.ONE))); indexPanelWidth = getIndexPanelWidth(blocks); int center = indexPanelWidth / 2; @@ -1172,6 +1172,11 @@ class CompositePanel extends JPanel implements IndexedScrollable, IndexScrollLis // handled by indexPanel } + @Override + public void mouseWheelMoved(double preciseWheelRotation, boolean isHorizontal) { + indexPanel.mouseWheelMoved(preciseWheelRotation, isHorizontal); + } + @Override public void indexRangeChanged(BigInteger startIndex, BigInteger endIndex, int yStart, int yEnd) { 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 61e9d52271..7eaf9bf905 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 @@ -110,7 +110,17 @@ public class FieldPanel extends JPanel addKeyListener(new FieldPanelKeyAdapter()); addMouseListener(new FieldPanelMouseAdapter()); addMouseMotionListener(new FieldPanelMouseMotionAdapter()); - addMouseWheelListener(new BigFieldPanelMouseWheelListener()); + + // This the default scroll wheel listener. Note: to work around a bug in the scroll pane, + // this component will not expand to entirely fill the parent scroll pane (See the + // IndexedScrollPane). This listener will handle scroll wheel events that happen over + // field panel. There is a similar listener to handle events that happen over the + // IndexScrollPane + addMouseWheelListener(e -> { + mouseWheelMoved(e.getPreciseWheelRotation(), e.isShiftDown()); + e.consume(); + }); + addFocusListener(new FieldPanelFocusListener()); setDoubleBuffered(false); @@ -1387,6 +1397,46 @@ public class FieldPanel extends JPanel return accessibleFieldPanel; } + public void mouseWheelMoved(double preciseWheelRotation, boolean horizontal) { + + Layout firstLayout = model.getLayout(BigInteger.ZERO); + if (firstLayout == null) { + return; // nothing to scroll + } + + int scrollUnit = firstLayout.getScrollableUnitIncrement(0, 1); + int scrollAmount = (int) (preciseWheelRotation * scrollUnit * MOUSEWHEEL_LINES_TO_SCROLL); + + if (hoverHandler.isHoverShowing()) { + hoverHandler.scroll(scrollAmount); + } + else { + hoverHandler.stopHover(); + + if (horizontal && horizontalScrollingEnabled) { + scrollViewHorizontally(scrollAmount); + } + else { + scrollView(scrollAmount); + } + } + } + + private void scrollViewHorizontally(int scrollAmount) { + + JViewport vp = getViewport(); + if (vp == null) { + // this will happen for Field Panels not placed inside of scroll panes + return; + } + + Point pos = vp.getViewPosition(); + + // don't allow new x position to go negative or else you can scroll left past the beginning + int x = Math.max(0, pos.x + scrollAmount); + vp.setViewPosition(new Point(x, pos.y)); + } + //================================================================================================== // Inner Classes //================================================================================================== @@ -1753,50 +1803,6 @@ public class FieldPanel extends JPanel } } - private class BigFieldPanelMouseWheelListener implements MouseWheelListener { - @Override - public void mouseWheelMoved(MouseWheelEvent e) { - double wheelRotation = e.getPreciseWheelRotation(); - - Layout firstLayout = model.getLayout(BigInteger.ZERO); - int layoutScrollHt = firstLayout != null // - ? firstLayout.getScrollableUnitIncrement(0, 1) - : 0; - int scrollAmount = (int) (wheelRotation * layoutScrollHt * MOUSEWHEEL_LINES_TO_SCROLL); - if (scrollAmount == 0) { - return; - } - - if (hoverHandler.isHoverShowing()) { - hoverHandler.scroll(scrollAmount); - } - else { - hoverHandler.stopHover(); - - if (e.isShiftDown() && horizontalScrollingEnabled) { - scrollViewHorizontally(scrollAmount); - } - else { - scrollView(scrollAmount); - } - } - e.consume(); - } - - private void scrollViewHorizontally(int scrollAmount) { - - JViewport vp = getViewport(); - if (vp == null) { - // this will happen for Field Panels not placed inside of scroll panes - return; - } - - // horizontal scroll (only move viewport) - Point pos = vp.getViewPosition(); - vp.setViewPosition(new Point(Math.max(0, pos.x + scrollAmount), pos.y)); - } - } - private class MouseHandler implements ActionListener { private Timer scrollTimer; // used to generate auto scroll private int mouseDownX; diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/indexedscrollpane/IndexedScrollPane.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/indexedscrollpane/IndexedScrollPane.java index e185fd5b73..69dc3d3bc8 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/indexedscrollpane/IndexedScrollPane.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/indexedscrollpane/IndexedScrollPane.java @@ -50,6 +50,14 @@ public class IndexedScrollPane extends JPanel implements IndexScrollListener { add(scrollPane); viewport = scrollPane.getViewport(); + + // This scroll pane does not have the view component track the width. This is to + // prevent a text clipping issue caused by the scroll pane not compensating for the + // scroll bars. Since the component may not occupy the full width of this scroll pane, + // we need to process any scroll wheel events that happen outside of that component. + viewport.addMouseWheelListener(e -> { + scrollable.mouseWheelMoved(e.getPreciseWheelRotation(), e.isShiftDown()); + }); viewport.setBackground(comp.getBackground()); viewport.addChangeListener(e -> viewportStateChanged()); viewport.setScrollMode(JViewport.SIMPLE_SCROLL_MODE); @@ -191,6 +199,7 @@ public class IndexedScrollPane extends JPanel implements IndexScrollListener { ScrollView(JComponent component) { setLayout(new ScrollViewLayout()); add(component); + } @Override diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/indexedscrollpane/IndexedScrollable.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/indexedscrollpane/IndexedScrollable.java index 76055ecabf..94157daddf 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/indexedscrollpane/IndexedScrollable.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/indexedscrollpane/IndexedScrollable.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. @@ -18,29 +17,90 @@ package docking.widgets.indexedscrollpane; import java.math.BigInteger; +/** + * Interface for scrolling a FieldPanel or container of a group of FieldPanels which displays + * a list of displayable items (layouts) + */ public interface IndexedScrollable { - BigInteger getIndexCount(); + /** + * Returns the number individually addressable items displayed. + * @return the number individually addressable items displayed + */ + public BigInteger getIndexCount(); - boolean isUniformIndex(); + /** + * Returns true if all the items are the same vertical size. + * @return true if all the items are the same vertical size + */ + public boolean isUniformIndex(); - int getHeight(BigInteger index); + /** + * Returns the height of the n'th item. + * @param index the index of the time to get height for + * @return the height of the n'th item. + */ + public int getHeight(BigInteger index); - void showIndex(BigInteger index, int verticalOffset); + /** + * Makes the item at the given index be visible on the screen at the given vertical offset + * @param index the index of the item to show + * @param verticalOffset the number of pixels from the top of the screen to show the item + */ + public void showIndex(BigInteger index, int verticalOffset); - BigInteger getIndexAfter(BigInteger index); + /** + * Returns the index of the next non-null item. Not all indexes have items. Some items span + * multiple indexes + * @param index the index to start searching for the next non-null item + * @return the index of the next non-null item, or -1 if there is none + */ + public BigInteger getIndexAfter(BigInteger index); - BigInteger getIndexBefore(BigInteger index); + /** + * Returns the index of the previous non-null item. Not all indexes have items. Some items span + * multiple indexes + * @param index the index to start searching backwards for the previous non-null item + * @return the index of the previous non-null item, or -1 if there is none + */ + public BigInteger getIndexBefore(BigInteger index); - void scrollLineUp(); + /** + * Scrolls the displayed items up by the height of one line of text + */ + public void scrollLineUp(); - void scrollLineDown(); + /** + * Scrolls the displayed items down by the height of one line of text + */ + public void scrollLineDown(); - void scrollPageUp(); + /** + * Scrolls the displayed items up by the height of one screen of text + */ + public void scrollPageUp(); - void scrollPageDown(); + /** + * Scrolls the displayed items down by the height of one screen of text + */ + public void scrollPageDown(); - void addIndexScrollListener(IndexScrollListener listener); + /** + * Adds a listener to be notified when the view is scrolled in any way. + * @param listener the listener to be notified when the visible items change + */ + public void addIndexScrollListener(IndexScrollListener listener); - void removeIndexScrollListener(IndexScrollListener listener); + /** + * Removes the given listener from those to be notified when the view changes. + * @param listener the listener to remove + */ + public void removeIndexScrollListener(IndexScrollListener listener); + + /** + * Notify the scrollable that the mouse wheel was moved. + * @param preciseWheelRotation the amount of rotation of the wheel + * @param isHorizontal true if the rotation was horizontal, false for vertical + */ + public void mouseWheelMoved(double preciseWheelRotation, boolean isHorizontal); }