diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/clipboard/CodeBrowserClipboardProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/clipboard/CodeBrowserClipboardProvider.java index 316339e361..a0ba1c466f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/clipboard/CodeBrowserClipboardProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/clipboard/CodeBrowserClipboardProvider.java @@ -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 = diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/ByteCopier.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/ByteCopier.java index ee77db5759..a8d28f1df5 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/ByteCopier.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/ByteCopier.java @@ -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)); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/OptionDialogBuilder.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/OptionDialogBuilder.java index a0ec07b2f0..59a01ffdb3 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/OptionDialogBuilder.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/OptionDialogBuilder.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,11 +63,12 @@ import ghidra.util.Swing; * called. Each of these methods called will overwrite the previously called method. * *
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;