mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-23 12:42:30 +00:00
Merge remote-tracking branch 'origin/GT-3432-dragonmacher-drag-n-drop'
(fixes #1396)
This commit is contained in:
commit
068a2f455e
@ -19,18 +19,18 @@ import java.awt.datatransfer.DataFlavor;
|
||||
|
||||
import ghidra.framework.main.datatree.*;
|
||||
|
||||
/**
|
||||
* A class used to initialize the handling of files that are dropped onto the tool
|
||||
*/
|
||||
public class GhidraFileOpenDataFlavorHandlerService {
|
||||
|
||||
public GhidraFileOpenDataFlavorHandlerService() {
|
||||
|
||||
try {
|
||||
DataFlavor linuxFileUrlFlavor =
|
||||
new DataFlavor("application/x-java-serialized-object;class=java.lang.String");
|
||||
FileOpenDropHandler.addDataFlavorHandler(linuxFileUrlFlavor, new LinuxFileUrlHandler());
|
||||
}
|
||||
catch (ClassNotFoundException cnfe) {
|
||||
// should never happen as it is using java.lang.String
|
||||
}
|
||||
//
|
||||
// Note: the order of the file drop flavors/handlers is intentional. We wish to process
|
||||
// objects first which we know to be transfered from within the current JVM. After
|
||||
// that, then process objects given to us from the OS or another JVM.
|
||||
//
|
||||
|
||||
LocalTreeNodeHandler localHandler = new LocalTreeNodeHandler();
|
||||
FileOpenDropHandler.addDataFlavorHandler(DataTreeDragNDropHandler.localDomainFileFlavor,
|
||||
@ -40,7 +40,13 @@ public class GhidraFileOpenDataFlavorHandlerService {
|
||||
|
||||
FileOpenDropHandler.addDataFlavorHandler(VersionInfoTransferable.localVersionInfoFlavor,
|
||||
new LocalVersionInfoHandler());
|
||||
|
||||
FileOpenDropHandler.addDataFlavorHandler(DataFlavor.javaFileListFlavor,
|
||||
new JavaFileListHandler());
|
||||
|
||||
DataFlavor linuxFileUrlFlavor =
|
||||
new DataFlavor("application/x-java-serialized-object;class=java.lang.String",
|
||||
"String file URL");
|
||||
FileOpenDropHandler.addDataFlavorHandler(linuxFileUrlFlavor, new LinuxFileUrlHandler());
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import java.awt.Component;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.dnd.DropTargetDropEvent;
|
||||
import java.io.File;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -84,8 +85,14 @@ public final class LinuxFileUrlHandler implements DataTreeFlavorHandler, FileOpe
|
||||
try {
|
||||
return new File(new URL(s).toURI());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
Msg.error(this, "Unable to open dropped URL: '" + s + "'", ex);
|
||||
catch (MalformedURLException e) {
|
||||
// this could be the case that this handler is attempting to process an arbitrary
|
||||
// String that is not actually a URL
|
||||
Msg.trace(this, "Not a URL: '" + s + "'", e);
|
||||
return null;
|
||||
}
|
||||
catch (Exception e) {
|
||||
Msg.error(this, "Unable to open dropped URL: '" + s + "'", e);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
@ -15,246 +15,202 @@
|
||||
*/
|
||||
package docking.dnd;
|
||||
|
||||
import ghidra.util.Msg;
|
||||
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
import java.awt.dnd.*;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
* Class to handle notifications of drag and drop operations that occur
|
||||
* on the DropTarget object. The DropTarget is the component that accepts
|
||||
* drops during a drag and drop operation. The <code>drop</code>
|
||||
* method actually transfers the data.
|
||||
* Class to handle notifications of drag and drop operations that occur on the DropTarget
|
||||
* object. The DropTarget is the component that accepts drops during a drag and drop operation.
|
||||
* The <code>drop</code> method actually transfers the data.
|
||||
*/
|
||||
public class DropTgtAdapter implements DropTargetListener {
|
||||
|
||||
private Droppable dropComponent;
|
||||
private int dropActions; // actions that the drop target
|
||||
// can accept
|
||||
private DataFlavor []dropFlavors; //drop flavors that the
|
||||
// drop target can accept
|
||||
private Droppable dropComponent;
|
||||
private int dropActions; // actions that the drop target can accept
|
||||
private DataFlavor[] dropFlavors; //drop flavors that the drop target can accept
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param dropComponent the drop target
|
||||
* @param acceptableDropActions a DnDConstants variable that defines
|
||||
* dnd actions
|
||||
* @param acceptableDropFlavors acceptable data formats that the drop
|
||||
* target can handle
|
||||
*/
|
||||
public DropTgtAdapter(Droppable dropComponent,
|
||||
int acceptableDropActions, DataFlavor []acceptableDropFlavors) {
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param dropComponent the drop target
|
||||
* @param acceptableDropActions a DnDConstants variable that defines dnd actions
|
||||
* @param acceptableDropFlavors acceptable data formats that the drop target can handle
|
||||
*/
|
||||
public DropTgtAdapter(Droppable dropComponent,
|
||||
int acceptableDropActions, DataFlavor[] acceptableDropFlavors) {
|
||||
|
||||
this.dropComponent = dropComponent;
|
||||
dropActions = acceptableDropActions;
|
||||
dropFlavors = acceptableDropFlavors;
|
||||
}
|
||||
/**
|
||||
* Set the data flavors acceptable to the associated drop target.
|
||||
* @param dropFlavors
|
||||
*/
|
||||
public void setAcceptableDropFlavors(DataFlavor []dropFlavors) {
|
||||
this.dropFlavors = dropFlavors;
|
||||
}
|
||||
/**
|
||||
* DropTargetListener method called when the drag operation encounters
|
||||
* the drop target.
|
||||
* @param e event that has current state of drag and drop operation
|
||||
*/
|
||||
public void dragEnter(DropTargetDragEvent e) {
|
||||
this.dropComponent = dropComponent;
|
||||
dropActions = acceptableDropActions;
|
||||
dropFlavors = acceptableDropFlavors;
|
||||
}
|
||||
|
||||
if (isDropOk(e)) {
|
||||
e.acceptDrag(e.getDropAction());
|
||||
}
|
||||
else {
|
||||
dropComponent.dragUnderFeedback(false,e);
|
||||
e.rejectDrag();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* DropTargetListener method called when the drag operation is over
|
||||
* the drop target.
|
||||
* @param e event that has current state of drag and drop operation
|
||||
*/
|
||||
public void dragOver(DropTargetDragEvent e) {
|
||||
/**
|
||||
* Set the data flavors acceptable to the associated drop target
|
||||
* @param dropFlavors the flavors
|
||||
*/
|
||||
public void setAcceptableDropFlavors(DataFlavor[] dropFlavors) {
|
||||
this.dropFlavors = dropFlavors;
|
||||
}
|
||||
|
||||
if (isDropOk(e)) {
|
||||
dropComponent.dragUnderFeedback(true, e);
|
||||
e.acceptDrag(e.getDropAction());
|
||||
}
|
||||
else {
|
||||
dropComponent.dragUnderFeedback(false,e);
|
||||
e.rejectDrag();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* DropTargetListener method called when the
|
||||
* drag operation exits the drop target without dropping. However,
|
||||
* this method is also called even when the drop completes.
|
||||
* @param e event that has current state of drag and drop operation
|
||||
*/
|
||||
public void dragExit(DropTargetEvent e) {
|
||||
dropComponent.undoDragUnderFeedback();
|
||||
// Note: at this point, there is no way to tell whether the
|
||||
// drop actually occurred; so, there is no notification
|
||||
// for a "drop canceled"
|
||||
@Override
|
||||
public void dragEnter(DropTargetDragEvent e) {
|
||||
|
||||
}
|
||||
/**
|
||||
* DropTargetListener method called when the user modifies the
|
||||
* drag action.
|
||||
* @param e event that has current state of drag and drop operation
|
||||
*/
|
||||
public void dropActionChanged(DropTargetDragEvent e){
|
||||
dragOver(e);
|
||||
}
|
||||
/**
|
||||
* DropTargetListener method called when the drag operation terminates and
|
||||
* drops onto the drop target.
|
||||
* @param e event that has current state of drag and drop operation
|
||||
*/
|
||||
public void drop(DropTargetDropEvent e) {
|
||||
if (isDropOk(e)) {
|
||||
e.acceptDrag(e.getDropAction());
|
||||
}
|
||||
else {
|
||||
dropComponent.dragUnderFeedback(false, e);
|
||||
e.rejectDrag();
|
||||
}
|
||||
}
|
||||
|
||||
// only handle local transfers (within same JVM) for now...
|
||||
// if (!e.isLocalTransfer()) {
|
||||
// e.rejectDrop();
|
||||
// dropComponent.undoDragUnderFeedback();
|
||||
// return;
|
||||
// }
|
||||
@Override
|
||||
public void dragOver(DropTargetDragEvent e) {
|
||||
|
||||
Transferable t = e.getTransferable();
|
||||
int flavorIndex=-1;
|
||||
for (int i=0; i<dropFlavors.length; i++) {
|
||||
if (t.isDataFlavorSupported(dropFlavors[i])) {
|
||||
flavorIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (flavorIndex < 0) {
|
||||
e.rejectDrop();
|
||||
dropComponent.undoDragUnderFeedback();
|
||||
return;
|
||||
}
|
||||
if (isDropOk(e)) {
|
||||
dropComponent.dragUnderFeedback(true, e);
|
||||
e.acceptDrag(e.getDropAction());
|
||||
}
|
||||
else {
|
||||
dropComponent.dragUnderFeedback(false, e);
|
||||
e.rejectDrag();
|
||||
}
|
||||
}
|
||||
|
||||
int dropAction = e.getDropAction();
|
||||
int sourceActions = e.getSourceActions();
|
||||
@Override
|
||||
public void dragExit(DropTargetEvent e) {
|
||||
dropComponent.undoDragUnderFeedback();
|
||||
|
||||
if ( (dropAction & sourceActions) == 0) {
|
||||
e.rejectDrop();
|
||||
dropComponent.undoDragUnderFeedback();
|
||||
return;
|
||||
}
|
||||
// Note: at this point, there is no way to tell whether the drop actually occurred;
|
||||
// so, there is no notification for a "drop canceled"
|
||||
}
|
||||
|
||||
// the source listener receives this action in dragDropEnd().
|
||||
// if the action is DnDConstants.ACTION_COPY_OR_MOVE, then
|
||||
// the source receives the MOVE.
|
||||
e.acceptDrop(e.getDropAction());
|
||||
Object data =null;
|
||||
boolean error=false;
|
||||
Throwable th=null;
|
||||
@Override
|
||||
public void dropActionChanged(DropTargetDragEvent e) {
|
||||
dragOver(e);
|
||||
}
|
||||
|
||||
// now get the drop flavor that matches up with that in
|
||||
// the transferable object
|
||||
@Override
|
||||
public void drop(DropTargetDropEvent e) {
|
||||
|
||||
Transferable t = e.getTransferable();
|
||||
int flavorIndex = -1;
|
||||
for (int i = 0; i < dropFlavors.length; i++) {
|
||||
if (t.isDataFlavorSupported(dropFlavors[i])) {
|
||||
flavorIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (flavorIndex < 0) {
|
||||
e.rejectDrop();
|
||||
dropComponent.undoDragUnderFeedback();
|
||||
return;
|
||||
}
|
||||
|
||||
int dropAction = e.getDropAction();
|
||||
int sourceActions = e.getSourceActions();
|
||||
|
||||
if ((dropAction & sourceActions) == 0) {
|
||||
e.rejectDrop();
|
||||
dropComponent.undoDragUnderFeedback();
|
||||
return;
|
||||
}
|
||||
|
||||
// The source listener receives this action in dragDropEnd().
|
||||
// If the action is DnDConstants.ACTION_COPY_OR_MOVE, then the source receives the MOVE.
|
||||
e.acceptDrop(e.getDropAction());
|
||||
Object data = null;
|
||||
|
||||
try {
|
||||
data = t.getTransferData(dropFlavors[flavorIndex]);
|
||||
} catch (Throwable thr) {
|
||||
error=true;
|
||||
th = thr;
|
||||
}
|
||||
catch (Throwable throwable) {
|
||||
e.dropComplete(false);
|
||||
dropComponent.undoDragUnderFeedback();
|
||||
Msg.showError(this, null, "Drop Failed", "Could not get transfer data.", throwable);
|
||||
return;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
e.dropComplete(false);
|
||||
dropComponent.undoDragUnderFeedback();
|
||||
Msg.showError(this,null, "Drop Failed", "Could not get transfer data.", th);
|
||||
}
|
||||
else {
|
||||
// this is the copy
|
||||
DataFlavor flavor=dropFlavors[flavorIndex];
|
||||
try {
|
||||
dropComponent.add(data, e, flavor);
|
||||
// notify drag source that the drop is complete...
|
||||
e.dropComplete(true);
|
||||
dropComponent.undoDragUnderFeedback();
|
||||
} catch (Throwable thr) {
|
||||
e.dropComplete(false);
|
||||
dropComponent.undoDragUnderFeedback();
|
||||
String message = thr.getMessage();
|
||||
if ( message == null ) {
|
||||
message = "";
|
||||
}
|
||||
Msg.showError(this, null, "Unexpected Drag and Drop Exception", message, thr);
|
||||
}
|
||||
}
|
||||
// this is the copy
|
||||
DataFlavor flavor = dropFlavors[flavorIndex];
|
||||
try {
|
||||
dropComponent.add(data, e, flavor);
|
||||
e.dropComplete(true);
|
||||
dropComponent.undoDragUnderFeedback();
|
||||
}
|
||||
catch (Throwable throwable) {
|
||||
e.dropComplete(false);
|
||||
dropComponent.undoDragUnderFeedback();
|
||||
String message = throwable.getMessage();
|
||||
Msg.showError(this, null, "Unexpected Drag and Drop Exception", message, throwable);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the drop operation is OK. A drop is deemed to be okay if
|
||||
* <OL>
|
||||
* <LI>the drop target accepts one of the data flavors that the event's transferable provides
|
||||
* </LI>
|
||||
* <LI>the drop action (i.e. COPY, MOVE, etc.) is accepted by the target
|
||||
* </LI>
|
||||
* <LI>the drop is accepted by the Droppable component
|
||||
* </LI>
|
||||
* </OL>
|
||||
*
|
||||
* @param e event that has current state of drag and drop operation
|
||||
* @return true if the drop operation is OK
|
||||
*/
|
||||
protected boolean isDropOk(DropTargetDragEvent e) {
|
||||
|
||||
/**
|
||||
* Returns true if the drop operation is OK. A drop is deemed to be okay if
|
||||
* <br> 1. the drop target accepts one of the data flavors that the event's transferrable provides.
|
||||
* <br> 2. the drop action (i.e. COPY, MOVE, etc.) is accepted by the target.
|
||||
* <br> 3. the drop is accepted by the Droppable component.
|
||||
* @param e event that has current state of drag and drop operation
|
||||
*/
|
||||
protected boolean isDropOk(DropTargetDragEvent e) {
|
||||
// Does this target accept the drop action type being dropped on it?
|
||||
int da = e.getDropAction();
|
||||
if ((da & dropActions) == 0) {
|
||||
return false;
|
||||
}
|
||||
// Does the event's transferable have a flavor that this drop target accepts?
|
||||
int da = e.getDropAction();
|
||||
if ((da & dropActions) == 0) {
|
||||
return false;
|
||||
}
|
||||
// Does the event's transferable have a flavor that this drop target accepts?
|
||||
if (!isDragFlavorSupported(e)) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// Does the target component allow the drop.
|
||||
if (!dropComponent.isDropOk(e)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Returns true if the drop target can accept the data
|
||||
* flavor that is to be dropped.
|
||||
*/
|
||||
protected boolean isDragFlavorSupported(DropTargetDragEvent e) {
|
||||
if (dropFlavors == null) {
|
||||
return false; // This drop target doesn't accept any flavors.
|
||||
}
|
||||
// Check each flavor to see that this accepts at least one flavor the event can drop.
|
||||
for (int i=0; i<dropFlavors.length; i++) {
|
||||
if (e.isDataFlavorSupported(dropFlavors[i])){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!dropComponent.isDropOk(e)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static DataFlavor getFirstMatchingFlavor(DropTargetDragEvent e, DataFlavor[] acceptableFlavors) {
|
||||
/**
|
||||
* Returns true if the drop target can accept the data flavor that is to be dropped
|
||||
* @param e event that has current state of drag and drop operation
|
||||
* @return true if the drop target can accept the data flavor that is to be dropped
|
||||
*/
|
||||
protected boolean isDragFlavorSupported(DropTargetDragEvent e) {
|
||||
if (dropFlavors == null) {
|
||||
return false; // This drop target doesn't accept any flavors.
|
||||
}
|
||||
// Check each flavor to see that this accepts at least one flavor the event can drop.
|
||||
for (DataFlavor dropFlavor : dropFlavors) {
|
||||
if (e.isDataFlavorSupported(dropFlavor)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static DataFlavor getFirstMatchingFlavor(DropTargetDragEvent e,
|
||||
DataFlavor[] acceptableFlavors) {
|
||||
DataFlavor[] transferFlavors = e.getCurrentDataFlavors();
|
||||
for (DataFlavor acceptableFlavor : acceptableFlavors) {
|
||||
for (DataFlavor transferFlavor : transferFlavors) {
|
||||
if (acceptableFlavor.equals(transferFlavor)) {
|
||||
return transferFlavor;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static DataFlavor[] getAllMatchingFlavors(DropTargetDragEvent e, DataFlavor[] acceptableFlavors) {
|
||||
ArrayList<DataFlavor> list = new ArrayList<DataFlavor>();
|
||||
DataFlavor[] transferFlavors = e.getCurrentDataFlavors();
|
||||
for (DataFlavor acceptableFlavor : acceptableFlavors) {
|
||||
for (DataFlavor transferFlavor : transferFlavors) {
|
||||
if (acceptableFlavor.equals(transferFlavor)) {
|
||||
list.add(transferFlavor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return list.toArray(new DataFlavor[list.size()]);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -33,8 +33,8 @@ public class DataTreeDragNDropHandler implements GTreeDragNDropHandler {
|
||||
private static Map<DataFlavor, DataTreeFlavorHandler> activeProjectDropFlavorHandlerMap =
|
||||
new HashMap<>();
|
||||
public static DataFlavor localDomainFileTreeFlavor = createLocalTreeNodeFlavor();
|
||||
public static DataFlavor localDomainFileFlavor = createLocalTreeFlavor();
|
||||
|
||||
public static DataFlavor localDomainFileFlavor = createLocalTreeFlavor();
|
||||
public static DataFlavor[] allSupportedFlavors =
|
||||
{ localDomainFileTreeFlavor, localDomainFileFlavor, DataFlavor.stringFlavor };
|
||||
|
||||
@ -152,7 +152,7 @@ public class DataTreeDragNDropHandler implements GTreeDragNDropHandler {
|
||||
.map(node -> ((DomainFileNode) node).getDomainFile())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
else if (flavor == DataFlavor.stringFlavor) {
|
||||
else if (flavor.equals(DataFlavor.stringFlavor)) {
|
||||
// allow users to copy the names of nodes
|
||||
return transferNodes.stream()
|
||||
.map(node -> node.getName())
|
||||
|
Loading…
Reference in New Issue
Block a user