Merge remote-tracking branch

'origin/GP-6267-dragonmacher-copy-large-selection--SQUASHED'
(Closes #8820)
This commit is contained in:
Ryan Kurtz
2025-12-23 18:49:09 -05:00
3 changed files with 46 additions and 8 deletions

View File

@@ -64,7 +64,6 @@ public class CodeBrowserClipboardProvider extends ByteCopier
implements ClipboardContentProviderService, OptionsChangeListener {
protected static final PaintContext PAINT_CONTEXT = new PaintContext();
private static int[] COMMENT_TYPESx = CommentTypes.getTypes();
public static final ClipboardType ADDRESS_TEXT_TYPE =
new ClipboardType(DataFlavor.stringFlavor, "Address");
@@ -493,6 +492,7 @@ public class CodeBrowserClipboardProvider extends ByteCopier
protected Transferable copyCode(TaskMonitor monitor) {
AddressSetView addressSet = getSelectedAddresses();
ListingModel listingModel = getListingModel();
TextLayoutGraphics g = new TextLayoutGraphics();
LayoutBackgroundColorManager colorMap =

View File

@@ -24,6 +24,7 @@ import java.util.regex.Pattern;
import docking.dnd.GenericDataFlavor;
import docking.dnd.StringTransferable;
import docking.widgets.OptionDialog;
import docking.widgets.OptionDialogBuilder;
import ghidra.framework.cmd.Command;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*;
@@ -42,6 +43,12 @@ import ghidra.util.task.TaskMonitor;
*/
public abstract class ByteCopier {
/**
* Arbitrary number of selected bytes before we prompt the user for confirmation. This can be
* changed as needed.s
*/
private static final long LARGE_SELECTION_COUNT = 1_000_000;
public static DataFlavor BYTE_STRING_FLAVOR = createByteStringLocalDataTypeFlavor();
public static DataFlavor BYTE_STRING_NO_SPACES_FLAVOR =
createByteStringNoSpacesLocalDataTypeFlavor();
@@ -156,6 +163,8 @@ public abstract class ByteCopier {
protected ProgramSelection currentSelection;
protected ProgramLocation currentLocation;
private OptionDialog confirmLargeSelectionDialog;
protected ByteCopier() {
// limit construction
}
@@ -165,9 +174,37 @@ public abstract class ByteCopier {
if (addressSet == null || addressSet.isEmpty()) {
return new AddressSet(currentLocation.getAddress());
}
long size = addressSet.getNumAddresses();
if (skipLargeSelection(size)) {
return new AddressSet();
}
return currentSelection;
}
private boolean skipLargeSelection(long size) {
if (size < LARGE_SELECTION_COUNT) {
return false;
}
if (confirmLargeSelectionDialog == null) {
String message = """
You have copied %,d bytes.
Are you sure you wish to copy this many bytes?
""".formatted(size);
confirmLargeSelectionDialog = new OptionDialogBuilder("Copy Large Selection?", message)
.addOption("Continue")
.addCancel()
.addDontShowAgainOption()
.build();
}
int choice = confirmLargeSelectionDialog.show();
return choice == OptionDialog.CANCEL_OPTION;
}
protected Transferable copyBytes(AddressSetView addresses, boolean includeSpaces,
TaskMonitor monitor) {
return createStringTransferable(copyBytesAsString(addresses, includeSpaces, monitor));

View File

@@ -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,11 +63,12 @@ import ghidra.util.Swing;
* called. Each of these methods called will overwrite the previously called method.
*
* <P>If the user selects the checkBox, then the dialog result will be remembered.
* In future calls to display that dialog (or any dialog sharing
* the same DialogRememberChoice object), the dialog will first check if has a
* DialogRememberChoice object and that it has a remembered result, and if so, will just return
* the remembered result instead of showing the dialog.
*
* In future calls to display that dialog , the dialog will first check if has a
* {@link DialogRememberOption} object and that it has a remembered result, and if so, will just
* return the remembered result instead of showing the dialog. The remember options will be used
* as long as the dialog is being used by the client. The client must hold onto the dialog so that
* repeated calls will work as expected with the remember option chosen. The client could instead
* hold onto the builder and use the {@link #show()} method to get the same behavior.
*/
public class OptionDialogBuilder {
private String title;