GP-6207 - Byte Viewer - Fixed the option to toggle highlighting the current line; Fixed dragging behavior

This commit is contained in:
dragonmacher
2026-01-08 16:56:19 -05:00
parent 45252a5b15
commit b087cb7ec6
6 changed files with 95 additions and 51 deletions

View File

@@ -17,32 +17,40 @@ package ghidra.app.plugin.core.byteviewer;
import java.awt.Color;
import java.math.BigInteger;
import java.util.function.Supplier;
import docking.widgets.fieldpanel.support.BackgroundColorModel;
import docking.widgets.fieldpanel.support.FieldLocation;
/**
* {@link BackgroundColorModel} that changes the color for the currently focused byteviewer row
* {@link BackgroundColorModel} that changes the color for the currently focused byte viewer row
*/
public class ByteViewerBGColorModel implements BackgroundColorModel {
private Color bgColor = ByteViewerComponentProvider.BG_COLOR;
private Supplier<BigInteger> cursorLocSupplier;
private ByteViewerPanel panel;
/**
* Creates new model.
* Creates a new model.
*
* @param cursorLocSupplier provides the index of the byteviewer-global cursor
* @param panel the byte viewer used to synchronize the current line across components
*/
public ByteViewerBGColorModel(Supplier<BigInteger> cursorLocSupplier) {
this.cursorLocSupplier = cursorLocSupplier;
public ByteViewerBGColorModel(ByteViewerPanel panel) {
this.panel = panel;
}
@Override
public Color getBackgroundColor(BigInteger index) {
BigInteger cursorIndex = cursorLocSupplier.get();
return cursorIndex.equals(index)
? ByteViewerComponentProvider.CURRENT_LINE_COLOR
: bgColor;
if (!panel.isHighlightCurrentLine()) {
return bgColor;
}
ByteViewerComponent c = panel.getCurrentComponent();
FieldLocation loc = c.getCursorLocation();
BigInteger cursorIndex = loc.getIndex();
if (index.equals(cursorIndex)) {
return ByteViewerComponentProvider.CURRENT_LINE_COLOR;
}
return bgColor;
}
@Override

View File

@@ -60,6 +60,7 @@ public class ByteViewerComponent extends FieldPanel
private int charWidth;
private IndexMap indexMap;
private ProgramByteBlockSet blockSet;
private ByteViewerLayoutModel layoutModel;
private boolean doingRefresh;
private boolean doingEdit;
@@ -71,24 +72,24 @@ public class ByteViewerComponent extends FieldPanel
private FieldSelectionListener liveSelectionListener = (selection, trigger) -> {
ByteBlockSelection sel = processFieldSelection(selection);
panel.updateLiveSelection(ByteViewerComponent.this, sel);
panel.updateLiveSelection(this, sel);
};
private ByteViewerHoverProvider byteViewerHoverProvider;
/**
* Constructor
*
* @param vpanel the byte viewer panel that this component lives in
* @param panel the byte viewer panel that this component lives in
* @param layoutModel the layout model for this component
* @param model data format model that knows how the data should be displayed
* @param bytesPerLine number of bytes displayed in a row
*/
protected ByteViewerComponent(ByteViewerPanel vpanel, ByteViewerLayoutModel layoutModel,
protected ByteViewerComponent(ByteViewerPanel panel, ByteViewerLayoutModel layoutModel,
DataFormatModel model, int bytesPerLine) {
super(layoutModel, "Byte Viewer");
setFieldDescriptionProvider((l, f) -> getFieldDescription(l, f));
this.panel = vpanel;
this.panel = panel;
this.model = model;
this.bytesPerLine = bytesPerLine;
this.layoutModel = layoutModel;
@@ -693,8 +694,9 @@ public class ByteViewerComponent extends FieldPanel
});
setBackgroundColor(ByteViewerComponentProvider.BG_COLOR);
// specialized line coloring
setBackgroundColorModel(new ByteViewerBGColorModel(() -> getCursorLocation().getIndex()));
setBackgroundColorModel(new ByteViewerBGColorModel(panel));
Gui.registerFont(this, ByteViewerComponentProvider.DEFAULT_FONT_ID);
@@ -707,6 +709,11 @@ public class ByteViewerComponent extends FieldPanel
setHoverProvider(byteViewerHoverProvider);
}
@Override
public boolean isDragging() { // open access
return super.isDragging();
}
/**
* Enable help for this component; used the model name as part of the help ID.
*/
@@ -950,7 +957,7 @@ public class ByteViewerComponent extends FieldPanel
// WARNING: unusual situation. This method signature is the same between both
// AbstractHoverProvider and the HoverService interface.
// AbstractHoverProvider calls the scroll() on the service, but when
// both calls end up at the same method, you will get a stackoverflow.
// both calls end up at the same method, you will get a stack overflow.
// We implement a do-nothing here that prevents that.
}
}

View File

@@ -93,8 +93,6 @@ public abstract class ByteViewerComponentProvider extends ComponentProviderAdapt
static final String OPTION_FONT = "Font";
private static final String DEFAULT_VIEW = "Hex";
private static final String CURRENT_LINE_COLOR_OPTION_NAME =
GhidraOptions.HIGHLIGHT_CURSOR_LINE_COLOR_OPTION_NAME;
private static final String OPTION_HIGHLIGHT_CURSOR_LINE =
GhidraOptions.HIGHLIGHT_CURSOR_LINE_OPTION_NAME;
private static final String OPTION_HIGHLIGHT_MIDDLE_MOUSE_NAME = "Middle Mouse Color";
@@ -215,10 +213,10 @@ public abstract class ByteViewerComponentProvider extends ComponentProviderAdapt
}
@Override
public List<DockingActionIf> getPopupActions(Tool tool, ActionContext context) {
public List<DockingActionIf> getPopupActions(Tool t, ActionContext context) {
if (context instanceof ByteViewerActionContext bvContext &&
bvContext.getComponentProvider() == this) {
return bvContext.getActiveColumn().getPopupActions(tool, bvContext);
return bvContext.getActiveColumn().getPopupActions(t, bvContext);
}
return null;
}
@@ -248,12 +246,19 @@ public abstract class ByteViewerComponentProvider extends ComponentProviderAdapt
@Override
public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
Object newValue) {
if (options.getName().equals(CATEGORY_BROWSER_FIELDS)) {
if (optionName.equals(CURSOR_HIGHLIGHT_BUTTON_NAME)) {
CURSOR_MOUSE_BUTTON_NAMES mouseButton = (CURSOR_MOUSE_BUTTON_NAMES) newValue;
panel.setHighlightButton(mouseButton.getMouseEventID());
}
}
else if (options.getName().equals("ByteViewer")) {
if (optionName.equals(OPTION_HIGHLIGHT_CURSOR_LINE)) {
panel.setHighlightCurrentLineEnabled((Boolean) newValue);
}
}
}
// Options.getStringEnum() is deprecated
@@ -285,16 +290,15 @@ public abstract class ByteViewerComponentProvider extends ComponentProviderAdapt
CURSOR_COLOR_UNFOCUSED_EDIT.getId(), help,
"Color of the cursor in the unfocused view when editing.");
opt.registerThemeColorBinding(CURRENT_LINE_COLOR_OPTION_NAME,
GhidraOptions.DEFAULT_CURSOR_LINE_COLOR.getId(), help,
"Color of the line containing the cursor.");
opt.registerThemeColorBinding(OPTION_HIGHLIGHT_MIDDLE_MOUSE_NAME,
HIGHLIGHT_MIDDLE_MOUSE_COLOR.getId(), help, "The middle-mouse highlight color.");
opt.registerThemeFontBinding(OPTION_FONT, DEFAULT_FONT_ID, help, "Font used in the views.");
opt.registerOption(OPTION_HIGHLIGHT_CURSOR_LINE, true, help,
boolean highlightCurrentLine = true;
opt.registerOption(OPTION_HIGHLIGHT_CURSOR_LINE, highlightCurrentLine, help,
"Toggles highlighting background color of line containing the cursor.");
panel.setHighlightCurrentLineEnabled(highlightCurrentLine);
opt.addOptionsChangeListener(this);
@@ -332,14 +336,6 @@ public abstract class ByteViewerComponentProvider extends ComponentProviderAdapt
return panel.getCursorLocation();
}
ByteBlockSelection getBlockSelection() {
return panel.getViewerSelection();
}
void setBlockSelection(ByteBlockSelection selection) {
panel.setViewerSelection(selection);
}
ByteBlockSet getByteBlockSet() {
return blockSet;
}
@@ -375,7 +371,6 @@ public abstract class ByteViewerComponentProvider extends ComponentProviderAdapt
panel.repaint();
}
changed |= addNewViews(selectedViews);
if (changed) {
@@ -513,8 +508,7 @@ public abstract class ByteViewerComponentProvider extends ComponentProviderAdapt
model.setByteViewerConfigOptions(configOptions);
String viewName = model.getName();
ByteViewerComponent bvc =
panel.addView(viewName, model, editModeAction.isSelected(), updateViewPosition);
ByteViewerComponent bvc = panel.addView(viewName, model, updateViewPosition);
viewMap.put(viewName, bvc);
if (configChanged) {
tool.setConfigChanged(true);
@@ -528,6 +522,7 @@ public abstract class ByteViewerComponentProvider extends ComponentProviderAdapt
if (bvc == null) {
return;
}
panel.removeView(bvc);
if (configChanged) {
@@ -541,7 +536,8 @@ public abstract class ByteViewerComponentProvider extends ComponentProviderAdapt
protected abstract void updateSelection(ByteBlockSelection selection);
protected abstract void updateLiveSelection(ByteBlockSelection selection);
protected abstract void updateLiveSelection(ByteViewerComponent bvc,
ByteBlockSelection selection);
void dispose() {
tool.removePopupActionProvider(this);

View File

@@ -65,6 +65,9 @@ public class ByteViewerPanel extends JPanel implements LayoutModel, LayoutListen
private IndexMap indexMap; // maps indexes to the correct block and offset
private int blockOffset;
private ByteViewerComponent currentView;
private boolean highlightCurrentLine;
private int highlightButton;
private ListenerSet<LayoutModelListener> layoutListeners =
new ListenerSet<>(LayoutModelListener.class, false);
@@ -117,6 +120,15 @@ public class ByteViewerPanel extends JPanel implements LayoutModel, LayoutListen
return highlightButton;
}
void setHighlightCurrentLineEnabled(boolean b) {
highlightCurrentLine = b;
repaint();
}
boolean isHighlightCurrentLine() {
return highlightCurrentLine;
}
void setHighlightButton(int highlightButton) {
this.highlightButton = highlightButton;
}
@@ -269,11 +281,10 @@ public class ByteViewerPanel extends JPanel implements LayoutModel, LayoutListen
*
* @param viewName name of the format, e.g., Hex, Ascii, etc.
* @param model model that understands the format
* @param editMode true if edit mode is on
* @param updateViewPosition true if the view position should be set
* @return the new component
*/
ByteViewerComponent addView(String viewName, DataFormatModel model, boolean editMode,
ByteViewerComponent addView(String viewName, DataFormatModel model,
boolean updateViewPosition) {
if (viewList.size() != 0) {
@@ -447,8 +458,17 @@ public class ByteViewerPanel extends JPanel implements LayoutModel, LayoutListen
}
}
void updateLiveSelection(ByteViewerComponent source, ByteBlockSelection selection) {
provider.updateLiveSelection(selection);
void updateLiveSelection(ByteViewerComponent bvc, ByteBlockSelection selection) {
provider.updateLiveSelection(bvc, selection);
for (ByteViewerComponent c : viewList) {
if (c == bvc) {
continue;
}
c.setViewerSelection(selection);
}
updateIndexColumnCurrentLine();
}
@@ -514,7 +534,7 @@ public class ByteViewerPanel extends JPanel implements LayoutModel, LayoutListen
setLayout(new BorderLayout(10, 0));
setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
setFont(ByteViewerComponentProvider.DEFAULT_FONT); // side-effect sets fontMetrics
// for the index/address column
@@ -526,8 +546,7 @@ public class ByteViewerPanel extends JPanel implements LayoutModel, LayoutListen
indexPanel.setFocusable(false);
indexPanel.addLayoutListener(this);
indexPanel.setBackgroundColor(ByteViewerComponentProvider.BG_COLOR);
indexPanel.setBackgroundColorModel(
new ByteViewerBGColorModel(() -> getCurrentComponent().getCursorLocation().getIndex()));
indexPanel.setBackgroundColorModel(new ByteViewerBGColorModel(this));
indexedView = new ByteViewerIndexedView(indexPanel);
IndexedScrollPane indexedScrollPane = new IndexedScrollPane(indexedView);

View File

@@ -618,13 +618,17 @@ public class ProgramByteViewerComponentProvider extends ByteViewerComponentProvi
}
@Override
protected void updateLiveSelection(ByteBlockSelection selection) {
if (blockSet != null) {
AbstractSelectionPluginEvent event =
blockSet.getPluginEvent(plugin.getName(), selection);
liveSelection = event.getSelection();
updateTitle();
protected void updateLiveSelection(ByteViewerComponent sourceComponent,
ByteBlockSelection blockSelection) {
if (blockSet == null) {
return;
}
AbstractSelectionPluginEvent event =
blockSet.getPluginEvent(plugin.getName(), blockSelection);
liveSelection = event.getSelection();
updateTitle();
}
@Override