From 4cda642e9c5920d154c372eda6896efbe5109c60 Mon Sep 17 00:00:00 2001 From: dragonmacher <48328597+dragonmacher@users.noreply.github.com> Date: Mon, 12 Aug 2024 12:04:38 -0400 Subject: [PATCH] GP-1359 - Updated the Listing and Byte Viewer to show selection size as the user drags --- .../core/codebrowser/CodeViewerProvider.java | 18 +++- .../listingpanel/ListingModelAdapter.java | 8 +- .../viewer/listingpanel/ListingPanel.java | 27 +++++- .../ghidra/program/util/ProgramSelection.java | 92 ++++++++++--------- .../core/byteviewer/ByteViewerComponent.java | 15 +-- .../ByteViewerComponentProvider.java | 6 +- .../core/byteviewer/ByteViewerPanel.java | 77 ++++++---------- .../ProgramByteViewerComponentProvider.java | 38 +++++++- .../program/model/address/AddressSet.java | 11 ++- 9 files changed, 175 insertions(+), 117 deletions(-) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeViewerProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeViewerProvider.java index 4732ac66ad..87c4a63e4a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeViewerProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeViewerProvider.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -105,7 +105,10 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter private CoordinatedListingPanelListener coordinatedListingPanelListener; private FormatManager formatMgr; private FieldPanelCoordinator coordinator; - + private ProgramSelectionListener liveProgramSelectionListener = (selection, trigger) -> { + liveSelection = selection; + updateSubTitle(); + }; private FocusingMouseListener focusingMouseListener; private CodeBrowserClipboardProvider codeViewerClipboardProvider; @@ -116,6 +119,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter private CloneCodeViewerAction cloneCodeViewerAction; private ProgramSelection currentSelection; + private ProgramSelection liveSelection; private ProgramSelection currentHighlight; private String currentStringSelection; @@ -163,6 +167,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter createActions(); listingPanel.setProgramLocationListener(this); listingPanel.setProgramSelectionListener(this); + listingPanel.setLiveProgramSelectionListener(liveProgramSelectionListener); listingPanel.setStringSelectionListener(this); listingPanel.addIndexMapChangeListener(this); @@ -542,11 +547,18 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter private void doSetSelection(ProgramSelection selection) { + liveSelection = null; currentSelection = selection; codeViewerClipboardProvider.setSelection(currentSelection); listingPanel.setSelection(currentSelection); plugin.selectionChanged(this, currentSelection); contextChanged(); + updateSubTitle(); + } + + private void updateSubTitle() { + + ProgramSelection selection = liveSelection != null ? liveSelection : currentSelection; String selectionInfo = null; if (!selection.isEmpty()) { long n = selection.getNumAddresses(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingModelAdapter.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingModelAdapter.java index f33399bb02..5ac78bc222 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingModelAdapter.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingModelAdapter.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -345,10 +345,8 @@ public class ListingModelAdapter implements LayoutModel, ListingModelListener { return ps; } } - Program program = model.getProgram(); addrSet = model.adjustAddressSetToCodeUnitBoundaries(addrSet); - AddressFactory factory = program == null ? null : program.getAddressFactory(); - return new ProgramSelection(factory, addrSet); + return new ProgramSelection(null, addrSet); } // this methods does NOT work for structures inside of unions, but handles structures diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingPanel.java index 9f96decfc6..d51c0eca46 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingPanel.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -63,7 +63,19 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc private ProgramLocationListener programLocationListener; private ProgramSelectionListener programSelectionListener; + private ProgramSelectionListener liveProgramSelectionListener; private StringSelectionListener stringSelectionListener; + private FieldSelectionListener fieldPanelLiveSelectionListener = (selection, trigger) -> { + + if (liveProgramSelectionListener == null) { + return; + } + + ProgramSelection ps = layoutModel.getProgramSelection(selection); + if (ps != null) { + liveProgramSelectionListener.programSelectionChanged(ps, trigger); + } + }; private ListingModel listingModel; private FieldHeader headerPanel; @@ -106,6 +118,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc fieldPanel.addFieldMouseListener(this); fieldPanel.addFieldLocationListener(this); fieldPanel.addFieldSelectionListener(this); + fieldPanel.addLiveFieldSelectionListener(fieldPanelLiveSelectionListener); fieldPanel.addLayoutListener(this); propertyBasedColorModel = new PropertyBasedBackgroundColorModel(); fieldPanel.setBackgroundColorModel(propertyBasedColorModel); @@ -207,6 +220,16 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc programSelectionListener = listener; } + /** + * Sets the ProgramSelectionListener for selection changes while dragging. Only one listener is + * supported + * + * @param listener the ProgramSelectionListener to use. + */ + public void setLiveProgramSelectionListener(ProgramSelectionListener listener) { + liveProgramSelectionListener = listener; + } + public void setStringSelectionListener(StringSelectionListener listener) { stringSelectionListener = listener; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramSelection.java b/Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramSelection.java index e7a5e76221..28e39e0978 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramSelection.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramSelection.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -32,15 +32,6 @@ public class ProgramSelection implements AddressSetView { * Construct a new empty ProgramSelection. */ public ProgramSelection() { - this((AddressFactory) null); - } - - /** - * Construct a new empty ProgramSelection. - * @param addressFactory the address factory for the address set - * associated with this program selection. - */ - public ProgramSelection(AddressFactory addressFactory) { addressSet = new AddressSet(); } @@ -50,18 +41,6 @@ public class ProgramSelection implements AddressSetView { * @param to the end of the selection */ public ProgramSelection(Address from, Address to) { - this(null, from, to); - } - - /** - * Constructor. - * @param addressFactory the address factory for the address set - * associated with this program selection. - * @param from the start of the selection - * @param to the end of the selection - */ - public ProgramSelection(AddressFactory addressFactory, Address from, Address to) { - this(addressFactory); if (to.compareTo(from) < 0) { Address temp = to; to = from; @@ -75,36 +54,59 @@ public class ProgramSelection implements AddressSetView { * @param setView address set for the selection */ public ProgramSelection(AddressSetView setView) { - this(null, setView); - } - - /** - * Construct a new ProgramSelection - * @param addressFactory the address factory for the address set - * associated with this program selection. - * @param setView address set for the selection - */ - public ProgramSelection(AddressFactory addressFactory, AddressSetView setView) { addressSet = new AddressSet(setView); } - /** - * Construct a new ProgramSelection from the indicated interior selection. - * @param addressFactory the address factory for the address set - * associated with this program selection. - * @param sel the interior selection - */ - public ProgramSelection(AddressFactory addressFactory, InteriorSelection sel) { - this(addressFactory, sel.getStartAddress(), sel.getEndAddress()); - interiorSelection = sel; - } - /** * Construct a new ProgramSelection from the indicated interior selection. * @param sel the interior selection */ public ProgramSelection(InteriorSelection sel) { - this(null, sel); + this(sel.getStartAddress(), sel.getEndAddress()); + interiorSelection = sel; + } + + /** + * Construct a new empty ProgramSelection. + * @param addressFactory NOT USED + * @deprecated use {@link #ProgramSelection()} + */ + @Deprecated(since = "11.2", forRemoval = true) + public ProgramSelection(AddressFactory addressFactory) { + this(); + } + + /** + * Constructor. + * @param addressFactory NOT USED + * @param from the start of the selection + * @param to the end of the selection + */ + @Deprecated(since = "11.2", forRemoval = true) + public ProgramSelection(AddressFactory addressFactory, Address from, Address to) { + this(from, to); + } + + /** + * Construct a new ProgramSelection + * @param addressFactory NOT USED + * @param setView address set for the selection + * @deprecated use {@link #ProgramSelection(AddressSetView)} + */ + @Deprecated(since = "11.2", forRemoval = true) + public ProgramSelection(AddressFactory addressFactory, AddressSetView setView) { + this(setView); + } + + /** + * Construct a new ProgramSelection from the indicated interior selection. + * @param addressFactory NOT USED + * @param sel the interior selection + * @deprecated use {@link #ProgramSelection(InteriorSelection)}s + */ + @Deprecated(since = "11.2", forRemoval = true) + public ProgramSelection(AddressFactory addressFactory, InteriorSelection sel) { + this(sel); } /** diff --git a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerComponent.java b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerComponent.java index 335300d923..797ebaeea9 100644 --- a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerComponent.java +++ b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerComponent.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -68,6 +68,11 @@ public class ByteViewerComponent extends FieldPanel implements FieldMouseListene private ByteViewerHighlighter highlightProvider = new ByteViewerHighlighter(); private int highlightButton = MouseEvent.BUTTON2; + private FieldSelectionListener liveSelectionListener = (selection, trigger) -> { + ByteBlockSelection sel = processFieldSelection(selection); + panel.updateLiveSelection(ByteViewerComponent.this, sel); + }; + /** * Constructor * @@ -206,12 +211,12 @@ public class ByteViewerComponent extends FieldPanel implements FieldMouseListene if (blockSet == null || doingRefresh) { return; } + ByteBlockSelection sel = processFieldSelection(selection); // notify panel to update other components panel.updateSelection(this, sel); setViewerSelection(sel); - } /** @@ -339,12 +344,10 @@ public class ByteViewerComponent extends FieldPanel implements FieldMouseListene return null; } - /** - * Add listeners. - */ void addListeners() { addFieldLocationListener(this); addFieldSelectionListener(this); + addLiveFieldSelectionListener(liveSelectionListener); addFieldInputListener(this); addFieldMouseListener(this); } diff --git a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerComponentProvider.java b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerComponentProvider.java index c7aff4649b..fc672cb1f2 100644 --- a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerComponentProvider.java +++ b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerComponentProvider.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -429,6 +429,8 @@ public abstract class ByteViewerComponentProvider extends ComponentProviderAdapt protected abstract void updateSelection(ByteBlockSelection selection); + protected abstract void updateLiveSelection(ByteBlockSelection selection); + void dispose() { updateManager.dispose(); updateManager = null; 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 da7d22fc92..48ee08b02e 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 @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -101,9 +101,8 @@ public class ByteViewerPanel extends JPanel implements LayoutModel, LayoutListen height += dim.height; } boolean addHeight = true; - for (int i = 0; i < viewList.size(); i++) { + for (ByteViewerComponent c : viewList) { - ByteViewerComponent c = viewList.get(i); Dimension d = c.getPreferredSize(); width += d.width; width += 2; // for separator @@ -133,54 +132,47 @@ public class ByteViewerPanel extends JPanel implements LayoutModel, LayoutListen ////////////////////////////////////////////////////////////////////////// void setCurrentCursorColor(Color c) { currentCursorColor = c; - for (int i = 0; i < viewList.size(); i++) { - ByteViewerComponent comp = viewList.get(i); + for (ByteViewerComponent comp : viewList) { comp.setCurrentCursorColor(c); } } void setCurrentCursorLineColor(Color c) { currentCursorLineColor = c; - for (int i = 0; i < viewList.size(); i++) { - ByteViewerComponent comp = viewList.get(i); + for (ByteViewerComponent comp : viewList) { comp.setCurrentCursorLineColor(c); } } void setHighlightButton(int highlightButton) { this.highlightButton = highlightButton; - for (int i = 0; i < viewList.size(); i++) { - ByteViewerComponent comp = viewList.get(i); + for (ByteViewerComponent comp : viewList) { comp.setHighlightButton(highlightButton); } } void setMouseButtonHighlightColor(Color color) { this.highlightColor = color; - for (int i = 0; i < viewList.size(); i++) { - ByteViewerComponent comp = viewList.get(i); + for (ByteViewerComponent comp : viewList) { comp.setMouseButtonHighlightColor(color); } } void setCursorColor(Color c) { - for (int i = 0; i < viewList.size(); i++) { - ByteViewerComponent comp = viewList.get(i); + for (ByteViewerComponent comp : viewList) { comp.setNonFocusCursorColor(c); } } void setSeparatorColor(Color c) { indexFactory.setMissingValueColor(c); - for (int i = 0; i < viewList.size(); i++) { - ByteViewerComponent comp = viewList.get(i); + for (ByteViewerComponent comp : viewList) { comp.setSeparatorColor(c); } } void setNonFocusCursorColor(Color c) { - for (int i = 0; i < viewList.size(); i++) { - ByteViewerComponent comp = viewList.get(i); + for (ByteViewerComponent comp : viewList) { comp.setNonFocusCursorColor(c); } } @@ -217,13 +209,11 @@ public class ByteViewerPanel extends JPanel implements LayoutModel, LayoutListen // Do the following loop twice - once with update off and then with update on. // need to do this because all the byte view components must have their models // updated before any one of them tells their dependents about the change. - for (int i = 0; i < viewList.size(); i++) { - ByteViewerComponent c = viewList.get(i); + for (ByteViewerComponent c : viewList) { c.enableIndexUpdate(false); c.setIndexMap(indexMap); } - for (int i = 0; i < viewList.size(); i++) { - ByteViewerComponent c = viewList.get(i); + for (ByteViewerComponent c : viewList) { c.enableIndexUpdate(true); c.setIndexMap(indexMap); } @@ -235,8 +225,7 @@ public class ByteViewerPanel extends JPanel implements LayoutModel, LayoutListen } void setViewerSelection(ByteBlockSelection selection) { - for (int i = 0; i < viewList.size(); i++) { - ByteViewerComponent c = viewList.get(i); + for (ByteViewerComponent c : viewList) { c.setViewerSelection(selection); } } @@ -249,8 +238,7 @@ public class ByteViewerPanel extends JPanel implements LayoutModel, LayoutListen } void setViewerHighlight(ByteBlockSelection highlight) { - for (int i = 0; i < viewList.size(); i++) { - ByteViewerComponent c = viewList.get(i); + for (ByteViewerComponent c : viewList) { c.setViewerHighlight(highlight); } } @@ -279,8 +267,7 @@ public class ByteViewerPanel extends JPanel implements LayoutModel, LayoutListen void setCursorLocation(ByteBlock block, BigInteger index, int column) { int modelIndex = -1; - for (int i = 0; i < viewList.size(); i++) { - ByteViewerComponent c = viewList.get(i); + for (ByteViewerComponent c : viewList) { modelIndex = c.setViewerCursorLocation(block, index, column); } if (modelIndex >= 0) { @@ -411,8 +398,7 @@ public class ByteViewerPanel extends JPanel implements LayoutModel, LayoutListen } void setEditMode(boolean editMode) { - for (int i = 0; i < viewList.size(); i++) { - ByteViewerComponent c = viewList.get(i); + for (ByteViewerComponent c : viewList) { c.setEditMode(editMode); } } @@ -428,8 +414,7 @@ public class ByteViewerPanel extends JPanel implements LayoutModel, LayoutListen * Force the current view to be refreshed. */ void refreshView() { - for (int i = 0; i < viewList.size(); i++) { - ByteViewerComponent c = viewList.get(i); + for (ByteViewerComponent c : viewList) { c.refreshView(); } } @@ -469,8 +454,7 @@ public class ByteViewerPanel extends JPanel implements LayoutModel, LayoutListen * @throws InvalidInputException if a model cannot support the bytesPerLine value */ void checkBytesPerLine(int numBytesPerLine) throws InvalidInputException { - for (int i = 0; i < viewList.size(); i++) { - ByteViewerComponent c = viewList.get(i); + for (ByteViewerComponent c : viewList) { DataFormatModel model = c.getDataModel(); int groupSize = model.getGroupSize(); if (groupSize > 0) { @@ -531,8 +515,7 @@ public class ByteViewerPanel extends JPanel implements LayoutModel, LayoutListen } insertionField.setText(locRep); } - for (int i = 0; i < viewList.size(); i++) { - ByteViewerComponent c = viewList.get(i); + for (ByteViewerComponent c : viewList) { if (source == c) { continue; } @@ -550,8 +533,7 @@ public class ByteViewerPanel extends JPanel implements LayoutModel, LayoutListen void updateSelection(ByteViewerComponent source, ByteBlockSelection selection) { provider.updateSelection(selection); - for (int i = 0; i < viewList.size(); i++) { - ByteViewerComponent c = viewList.get(i); + for (ByteViewerComponent c : viewList) { if (source == c) { continue; } @@ -559,6 +541,10 @@ public class ByteViewerPanel extends JPanel implements LayoutModel, LayoutListen } } + void updateLiveSelection(ByteViewerComponent source, ByteBlockSelection selection) { + provider.updateLiveSelection(selection); + } + FontMetrics getCurrentFontMetrics() { return fontMetrics; } @@ -589,8 +575,7 @@ public class ByteViewerPanel extends JPanel implements LayoutModel, LayoutListen BigInteger offset = vp.getOffset(); ViewerPosition vpos = vp.getViewerPosition(); - for (int i = 0; i < viewList.size(); i++) { - ByteViewerComponent c = viewList.get(i); + for (ByteViewerComponent c : viewList) { c.returnToView(block, offset, vpos); } indexPanel.setViewerPosition(vpos.getIndex(), vpos.getXOffset(), vpos.getYOffset()); @@ -625,8 +610,7 @@ public class ByteViewerPanel extends JPanel implements LayoutModel, LayoutListen void setFontMetrics(FontMetrics fm) { this.fontMetrics = fm; - for (int i = 0; i < viewList.size(); i++) { - ByteViewerComponent c = viewList.get(i); + for (ByteViewerComponent c : viewList) { c.setFontMetrics(fm); } indexFactory = new IndexFieldFactory(fm); @@ -636,8 +620,7 @@ public class ByteViewerPanel extends JPanel implements LayoutModel, LayoutListen void setEditColor(Color editColor) { this.editColor = editColor; - for (int i = 0; i < viewList.size(); i++) { - ByteViewerComponent c = viewList.get(i); + for (ByteViewerComponent c : viewList) { c.setEditColor(editColor); } } @@ -761,8 +744,7 @@ public class ByteViewerPanel extends JPanel implements LayoutModel, LayoutListen block = info.getBlock(); offset = info.getOffset(); } - for (int i = 0; i < viewList.size(); i++) { - ByteViewerComponent c = viewList.get(i); + for (ByteViewerComponent c : viewList) { c.setIndexMap(indexMap); if (info != null) { c.setViewerCursorLocation(block, offset, info.getColumn()); @@ -775,8 +757,7 @@ public class ByteViewerPanel extends JPanel implements LayoutModel, LayoutListen * Clear the selection. */ private void clearSelection() { - for (int i = 0; i < viewList.size(); i++) { - ByteViewerComponent c = viewList.get(i); + for (ByteViewerComponent c : viewList) { c.clearViewerSelection(); } } diff --git a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ProgramByteViewerComponentProvider.java b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ProgramByteViewerComponentProvider.java index 62ed9e83f4..2c9346f0ba 100644 --- a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ProgramByteViewerComponentProvider.java +++ b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ProgramByteViewerComponentProvider.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -62,6 +62,7 @@ public class ProgramByteViewerComponentProvider extends ByteViewerComponentProvi protected Program program; protected ProgramSelection currentSelection; + protected ProgramSelection liveSelection; protected ProgramSelection currentHighlight; protected ProgramLocation currentLocation; @@ -179,7 +180,9 @@ public class ProgramByteViewerComponentProvider extends ByteViewerComponentProvi } protected void setSelection(ProgramSelection selection, boolean notify) { + liveSelection = null; currentSelection = selection; + updateTitle(); if (selection == null) { return; } @@ -258,8 +261,26 @@ public class ProgramByteViewerComponentProvider extends ByteViewerComponentProvi if (!isConnected()) { title = "[" + title + "]"; } - setTitle(title); + updateSubTitle(); + } + + private void updateSubTitle() { + // Note: the Listing has similar code + ProgramSelection selection = liveSelection != null ? liveSelection : currentSelection; + String selectionInfo = null; + if (selection != null && !selection.isEmpty()) { + long n = selection.getNumAddresses(); + String nString = Long.toString(n); + if (n == 1) { + selectionInfo = "(1 byte selected)"; + } + else { + selectionInfo = '(' + nString + " bytes selected)"; + } + } + + setSubTitle(selectionInfo); } //================================================================================================== @@ -366,7 +387,7 @@ public class ProgramByteViewerComponentProvider extends ByteViewerComponentProvi public boolean isVisible() { return tool.isVisible(this); } - + //================================================================================================== // End Navigatable interface methods */ //================================================================================================== @@ -583,12 +604,21 @@ public class ProgramByteViewerComponentProvider extends ByteViewerComponentProvi @Override protected void updateSelection(ByteBlockSelection selection) { ProgramSelectionPluginEvent event = blockSet.getPluginEvent(plugin.getName(), selection); + liveSelection = null; currentSelection = event.getSelection(); plugin.updateSelection(this, event, program); clipboardProvider.setSelection(currentSelection); + updateTitle(); contextChanged(); } + @Override + protected void updateLiveSelection(ByteBlockSelection selection) { + ProgramSelectionPluginEvent event = blockSet.getPluginEvent(plugin.getName(), selection); + liveSelection = event.getSelection(); + updateTitle(); + } + @Override protected void updateLocation(ByteBlock block, BigInteger blockOffset, int column, boolean export) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressSet.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressSet.java index 3218af2ab0..cd2381a1b8 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressSet.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressSet.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -346,6 +346,13 @@ public class AddressSet implements AddressSetView { buffy.append(range); buffy.append(" "); } + + // remove the last space maintain symmetry + char lastChar = buffy.charAt(buffy.length() - 1); + if (lastChar == ' ') { + buffy.deleteCharAt(buffy.length() - 1); + } + buffy.append("]"); return buffy.toString(); }