GT-2705 - File Import - fixed drag-n-drop onto Project Tree dialogs

This commit is contained in:
dragonmacher 2019-03-28 18:24:34 -04:00
parent 097cf2e758
commit 84c16c2b27
18 changed files with 240 additions and 560 deletions

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,20 +15,28 @@
*/
package foundation;
import ghidra.app.factory.*;
import ghidra.app.util.*;
import ghidra.framework.*;
import ghidra.framework.data.*;
import ghidra.framework.main.datatree.*;
import ghidra.app.factory.GhidraToolStateFactory;
import ghidra.app.util.GhidraFileOpenDataFlavorHandlerService;
import ghidra.framework.ModuleInitializer;
import ghidra.framework.PluggableServiceRegistry;
import ghidra.framework.data.ToolStateFactory;
import ghidra.framework.main.datatree.GhidraDataFlavorHandlerService;
import ghidra.program.database.*;
public class FoundationInitializer implements ModuleInitializer {
@Override
public void run() {
PluggableServiceRegistry.registerPluggableService( ToolStateFactory.class, new GhidraToolStateFactory() );
PluggableServiceRegistry.registerPluggableService( DataFlavorHandlerService.class, new GhidraDataFlavorHandlerService() );
PluggableServiceRegistry.registerPluggableService( FileOpenDataFlavorHandlerService.class, new GhidraFileOpenDataFlavorHandlerService() );
PluggableServiceRegistry.registerPluggableService( DataTypeArchiveMergeManagerFactory.class, new GhidraDataTypeArchiveMergeManagerFactory() );
PluggableServiceRegistry.registerPluggableService( ProgramMultiUserMergeManagerFactory.class, new GhidraProgramMultiUserMergeManagerFactory() );
PluggableServiceRegistry.registerPluggableService(ToolStateFactory.class,
new GhidraToolStateFactory());
PluggableServiceRegistry.registerPluggableService(GhidraDataFlavorHandlerService.class,
new GhidraDataFlavorHandlerService());
PluggableServiceRegistry.registerPluggableService(
GhidraFileOpenDataFlavorHandlerService.class,
new GhidraFileOpenDataFlavorHandlerService());
PluggableServiceRegistry.registerPluggableService(DataTypeArchiveMergeManagerFactory.class,
new GhidraDataTypeArchiveMergeManagerFactory());
PluggableServiceRegistry.registerPluggableService(ProgramMultiUserMergeManagerFactory.class,
new GhidraProgramMultiUserMergeManagerFactory());
}
@Override

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,14 +15,13 @@
*/
package ghidra.app.util;
import ghidra.framework.main.datatree.*;
import java.awt.datatransfer.DataFlavor;
public class GhidraFileOpenDataFlavorHandlerService extends FileOpenDataFlavorHandlerService {
import ghidra.framework.main.datatree.*;
@Override
protected void doRegisterDataFlavorHandlers() {
public class GhidraFileOpenDataFlavorHandlerService {
public GhidraFileOpenDataFlavorHandlerService() {
try {
DataFlavor linuxFileUrlFlavor =
@ -34,15 +32,15 @@ public class GhidraFileOpenDataFlavorHandlerService extends FileOpenDataFlavorHa
// should never happen as it is using java.lang.String
}
LocalTreeNodeFlavorHandler localHandler = new LocalTreeNodeFlavorHandler();
LocalTreeNodeHandler localHandler = new LocalTreeNodeHandler();
FileOpenDropHandler.addDataFlavorHandler(DataTreeDragNDropHandler.localDomainFileFlavor,
localHandler);
FileOpenDropHandler.addDataFlavorHandler(VersionInfoTransferable.localVersionInfoFlavor,
FileOpenDropHandler.addDataFlavorHandler(DataTreeDragNDropHandler.localDomainFileTreeFlavor,
localHandler);
FileOpenDropHandler.addDataFlavorHandler(DataFlavor.javaFileListFlavor,
new JavaFileListFlavorHandler());
FileOpenDropHandler.addDataFlavorHandler(
DataTreeDragNDropHandler.localDomainFileTreeFlavor, localHandler);
FileOpenDropHandler.addDataFlavorHandler(VersionInfoTransferable.localVersionInfoFlavor,
new LocalVersionInfoHandler());
FileOpenDropHandler.addDataFlavorHandler(DataFlavor.javaFileListFlavor,
new JavaFileListHandler());
}
}

View File

@ -1,48 +0,0 @@
/* ###
* IP: GHIDRA
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util;
import java.awt.datatransfer.DataFlavor;
import java.awt.dnd.DropTargetDropEvent;
import java.io.File;
import java.util.List;
import ghidra.app.services.FileImporterService;
import ghidra.framework.model.DomainFolder;
import ghidra.framework.plugintool.PluginTool;
import util.CollectionUtils;
final class JavaFileListFlavorHandler implements FileOpenDataFlavorHandler {
@Override
public void handle(PluginTool tool, Object obj, DropTargetDropEvent e, DataFlavor f) {
List<File> files = CollectionUtils.asList((List<?>) obj, File.class);
FileImporterService im = tool.getService(FileImporterService.class);
if (im == null) {
tool.setStatusInfo("ERROR: Could not get importer service.");
return;
}
DomainFolder rootFolder = tool.getProject().getProjectData().getRootFolder();
if (files.size() == 1 && files.get(0).isFile()) {
im.importFile(rootFolder, files.get(0));
}
else {
im.importFiles(rootFolder, files);
}
}
}

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,9 +17,10 @@ package ghidra.framework.main.datatree;
import java.awt.datatransfer.DataFlavor;
public class GhidraDataFlavorHandlerService extends DataFlavorHandlerService {
@Override
protected void doRegisterDataFlavorHandlers() {
public class GhidraDataFlavorHandlerService {
public GhidraDataFlavorHandlerService() {
try {
DataFlavor linuxFileUrlFlavor =
new DataFlavor("application/x-java-serialized-object;class=java.lang.String");
@ -31,15 +31,12 @@ public class GhidraDataFlavorHandlerService extends DataFlavorHandlerService {
// should never happen as it is using java.lang.String
}
final LocalTreeNodeHandler localTreeNodeHandler = new LocalTreeNodeHandler();
LocalTreeNodeHandler localNodeHandler = new LocalTreeNodeHandler();
DataTreeDragNDropHandler.addActiveDataFlavorHandler(
DataTreeDragNDropHandler.localDomainFileTreeFlavor, localTreeNodeHandler);
DataTreeDragNDropHandler.localDomainFileTreeFlavor, localNodeHandler);
DataTreeDragNDropHandler.addActiveDataFlavorHandler(DataFlavor.javaFileListFlavor,
new JavaFileListHandler());
DataTreeDragNDropHandler.addActiveDataFlavorHandler(
VersionInfoTransferable.localVersionInfoFlavor, new LocalVersionInfoHandler());
DataTreeDragNDropHandler.addInactiveDataFlavorHandler(
DataTreeDragNDropHandler.localDomainFileTreeFlavor, localTreeNodeHandler);
}
}

View File

@ -19,13 +19,15 @@
package ghidra.framework.main.datatree;
import java.awt.datatransfer.DataFlavor;
import java.awt.dnd.DropTargetDropEvent;
import java.io.File;
import java.util.List;
import docking.widgets.tree.GTreeNode;
import ghidra.app.services.FileImporterService;
import ghidra.framework.main.FrontEndTool;
import ghidra.app.util.FileOpenDataFlavorHandler;
import ghidra.framework.model.DomainFolder;
import ghidra.framework.plugintool.PluginTool;
import ghidra.util.Msg;
import util.CollectionUtils;
@ -33,24 +35,43 @@ import util.CollectionUtils;
* A drag-and-drop handler for trees that is specific to List&ltFile&gt. (see
* {@link DataFlavor#javaFileListFlavor}).
*/
final class JavaFileListHandler implements DataFlavorHandler {
@Override
public void handle(FrontEndTool tool, DataTree dataTree, GTreeNode destinationNode,
Object transferData, int dropAction) {
DomainFolder folder = getDomainFolder(destinationNode);
public final class JavaFileListHandler implements DataTreeFlavorHandler, FileOpenDataFlavorHandler {
FileImporterService im = tool.getService(FileImporterService.class);
if (im == null) {
Msg.showError(this, dataTree, "Could Not Import", "Could not find importer service");
@Override
public void handle(PluginTool tool, Object transferData, DropTargetDropEvent e, DataFlavor f) {
FileImporterService importer = tool.getService(FileImporterService.class);
if (importer == null) {
Msg.showError(this, null, "Could Not Import", "Could not find Importer Service");
return;
}
List<File> fileList = CollectionUtils.asList((List<?>) transferData, File.class);
DomainFolder folder = tool.getProject().getProjectData().getRootFolder();
doImport(importer, folder, transferData);
}
@Override
public void handle(PluginTool tool, DataTree dataTree, GTreeNode destinationNode,
Object transferData, int dropAction) {
FileImporterService importer = tool.getService(FileImporterService.class);
if (importer == null) {
Msg.showError(this, dataTree, "Could Not Import", "Could not find Importer Service");
return;
}
DomainFolder folder = getDomainFolder(destinationNode);
doImport(importer, folder, transferData);
}
private void doImport(FileImporterService importer, DomainFolder folder, Object files) {
List<File> fileList = CollectionUtils.asList((List<?>) files, File.class);
if (fileList.size() == 1 && fileList.get(0).isFile()) {
im.importFile(folder, fileList.get(0));
importer.importFile(folder, fileList.get(0));
}
else {
im.importFiles(folder, fileList);
importer.importFiles(folder, fileList);
}
}

View File

@ -27,7 +27,6 @@ import java.util.function.Function;
import docking.widgets.tree.GTreeNode;
import ghidra.app.services.FileImporterService;
import ghidra.app.util.FileOpenDataFlavorHandler;
import ghidra.framework.main.FrontEndTool;
import ghidra.framework.model.DomainFolder;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.ServiceProvider;
@ -38,11 +37,11 @@ import ghidra.util.Msg;
* duty in that it opens files for DataTrees and for Tools (signaled via the interfaces it
* implements).
*/
public final class LinuxFileUrlHandler implements DataFlavorHandler, FileOpenDataFlavorHandler {
public final class LinuxFileUrlHandler implements DataTreeFlavorHandler, FileOpenDataFlavorHandler {
@Override
// This is for the DataFlavorHandler interface for handling node drops in DataTrees
public void handle(FrontEndTool tool, DataTree dataTree, GTreeNode destinationNode,
public void handle(PluginTool tool, DataTree dataTree, GTreeNode destinationNode,
Object transferData, int dropAction) {
DomainFolder folder = getDomainFolder(destinationNode);

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,11 +15,14 @@
*/
package ghidra.app.util;
import ghidra.framework.plugintool.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.datatransfer.*;
import java.awt.dnd.*;
import ghidra.framework.plugintool.PluginTool;
/**
* Interface for classes that will handle drop actions for files dropped onto the tool
*/
public interface FileOpenDataFlavorHandler {
public void handle(PluginTool tool, Object obj, DropTargetDropEvent e, DataFlavor f);
}

View File

@ -1,37 +0,0 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util;
import ghidra.framework.PluggableServiceRegistry;
import ghidra.framework.main.datatree.DataTreeDragNDropHandler;
import ghidra.framework.main.datatree.VersionInfoTransferable;
public class FileOpenDataFlavorHandlerService {
static {
PluggableServiceRegistry.registerPluggableService(FileOpenDataFlavorHandlerService.class, new FileOpenDataFlavorHandlerService());
}
public static void registerDataFlavorHandlers() {
FileOpenDataFlavorHandlerService factory = PluggableServiceRegistry.getPluggableService(FileOpenDataFlavorHandlerService.class);
factory.doRegisterDataFlavorHandlers();
}
protected void doRegisterDataFlavorHandlers() {
FileOpenDropHandler.addDataFlavorHandler(DataTreeDragNDropHandler.localDomainFileFlavor, new LocalTreeNodeFlavorHandler());
FileOpenDropHandler.addDataFlavorHandler(VersionInfoTransferable.localVersionInfoFlavor, new LocalTreeNodeFlavorHandler());
}
}

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,9 +15,6 @@
*/
package ghidra.app.util;
import ghidra.framework.plugintool.PluginTool;
import ghidra.util.CascadedDropTarget;
import java.awt.Component;
import java.awt.Container;
import java.awt.datatransfer.DataFlavor;
@ -33,6 +29,8 @@ import javax.swing.CellRendererPane;
import docking.DropTargetHandler;
import docking.dnd.DropTgtAdapter;
import docking.dnd.Droppable;
import ghidra.framework.plugintool.PluginTool;
import ghidra.util.CascadedDropTarget;
/**
* Handles drag/drop events on a given component such that a file
@ -44,9 +42,6 @@ import docking.dnd.Droppable;
public class FileOpenDropHandler implements DropTargetHandler, Droppable, ContainerListener {
private static HashMap<DataFlavor, FileOpenDataFlavorHandler> handlers =
new HashMap<DataFlavor, FileOpenDataFlavorHandler>();
static {
FileOpenDataFlavorHandlerService.registerDataFlavorHandlers();
}
private DropTgtAdapter dropTargetAdapter;
private DropTarget globalDropTarget;
@ -75,11 +70,13 @@ public class FileOpenDropHandler implements DropTargetHandler, Droppable, Contai
/**
* Dispose this drop handler.
*/
@Override
public void dispose() {
deinitializeComponents(component);
globalDropTarget.removeDropTargetListener(dropTargetAdapter);
}
@Override
public boolean isDropOk(DropTargetDragEvent e) {
Set<DataFlavor> flavors = handlers.keySet();
for (DataFlavor dataFlavor : flavors) {
@ -90,6 +87,7 @@ public class FileOpenDropHandler implements DropTargetHandler, Droppable, Contai
return false;
}
@Override
public void add(Object obj, DropTargetDropEvent e, DataFlavor f) {
FileOpenDataFlavorHandler handler = handlers.get(f);
if (handler != null) {
@ -97,25 +95,29 @@ public class FileOpenDropHandler implements DropTargetHandler, Droppable, Contai
}
}
@Override
public void dragUnderFeedback(boolean ok, DropTargetDragEvent e) {
// nothing to display or do
}
@Override
public void undoDragUnderFeedback() {
// nothing to display or do
}
private void initializeComponents(Component comp) {
if (comp instanceof CellRendererPane)
if (comp instanceof CellRendererPane) {
return;
}
if (comp instanceof Container) {
Container c = (Container) comp;
c.addContainerListener(this);
Component comps[] = c.getComponents();
for (Component element : comps)
for (Component element : comps) {
initializeComponents(element);
}
}
DropTarget primaryDropTarget = comp.getDropTarget();
if (primaryDropTarget != null) {
new CascadedDropTarget(comp, primaryDropTarget, globalDropTarget);
@ -123,16 +125,18 @@ public class FileOpenDropHandler implements DropTargetHandler, Droppable, Contai
}
private void deinitializeComponents(Component comp) {
if (comp instanceof CellRendererPane)
if (comp instanceof CellRendererPane) {
return;
}
if (comp instanceof Container) {
Container c = (Container) comp;
c.removeContainerListener(this);
Component comps[] = c.getComponents();
for (Component element : comps)
for (Component element : comps) {
deinitializeComponents(element);
}
}
DropTarget dt = comp.getDropTarget();
if (dt instanceof CascadedDropTarget) {
CascadedDropTarget target = (CascadedDropTarget) dt;
@ -141,15 +145,18 @@ public class FileOpenDropHandler implements DropTargetHandler, Droppable, Contai
}
}
@Override
public void componentAdded(ContainerEvent e) {
initializeComponents(e.getChild());
}
@Override
public void componentRemoved(ContainerEvent e) {
deinitializeComponents(e.getChild());
}
public static void addDataFlavorHandler(DataFlavor dataFlavor, FileOpenDataFlavorHandler handler) {
public static void addDataFlavorHandler(DataFlavor dataFlavor,
FileOpenDataFlavorHandler handler) {
handlers.put(dataFlavor, handler);
}

View File

@ -1,72 +0,0 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
*
*/
package ghidra.app.util;
import ghidra.framework.main.GetVersionedObjectTask;
import ghidra.framework.main.datatree.*;
import ghidra.framework.model.*;
import ghidra.framework.plugintool.PluginTool;
import java.awt.datatransfer.DataFlavor;
import java.awt.dnd.DropTargetDropEvent;
import java.util.List;
final class LocalTreeNodeFlavorHandler implements FileOpenDataFlavorHandler {
public void handle(PluginTool tool, Object obj, DropTargetDropEvent e, DataFlavor f) {
if (f.equals(DataTreeDragNDropHandler.localDomainFileFlavor)) {
List<?> files = (List<?>) obj;
DomainFile[] domainFiles = new DomainFile[files.size()];
for (int i = 0; i < files.size(); i++) {
domainFiles[i] = (DomainFile) files.get(i);
}
tool.acceptDomainFiles(domainFiles);
}
else if (f.equals(DataTreeDragNDropHandler.localDomainFileTreeFlavor)) {
List<?> files = (List<?>) obj;
DomainFile[] domainFiles = new DomainFile[files.size()];
for (int i = 0; i < files.size(); i++) {
DomainFileNode node = (DomainFileNode) files.get(i);
domainFiles[i] = node.getDomainFile();
}
tool.acceptDomainFiles(domainFiles);
}
else if (f.equals(VersionInfoTransferable.localVersionInfoFlavor)) {
VersionInfo info = (VersionInfo) obj;
Project project = tool.getProject();
ProjectData projectData = project.getProjectData();
DomainFile file = projectData.getFile(info.getDomainFilePath());
DomainObject versionedObj = getVersionedObject(tool, file, info.getVersionNumber());
if (versionedObj != null) {
DomainFile domainFile = versionedObj.getDomainFile();
if (domainFile != null) {
tool.acceptDomainFiles(new DomainFile[] { domainFile });
}
versionedObj.release(this);
}
}
}
private DomainObject getVersionedObject(PluginTool tool, DomainFile file, int versionNumber) {
GetVersionedObjectTask task = new GetVersionedObjectTask(this, file, versionNumber);
tool.execute(task, 250);
return task.getVersionedObject();
}
}

View File

@ -1,47 +0,0 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
*
*/
package ghidra.app.util;
import ghidra.framework.main.*;
import ghidra.framework.main.datatree.*;
import ghidra.framework.model.*;
import ghidra.framework.plugintool.*;
import java.awt.datatransfer.*;
import java.awt.dnd.*;
final class LocalVersionInfoFlavorHandler implements
FileOpenDataFlavorHandler {
public void handle(PluginTool tool, Object obj, DropTargetDropEvent e, DataFlavor f) {
VersionInfo info = (VersionInfo) obj;
DomainFile file = tool.getProject().getProjectData().getFile(info.getDomainFilePath());
GetVersionedObjectTask task = new GetVersionedObjectTask(this, file,
info.getVersionNumber());
tool.execute(task, 250);
DomainObject versionedObj = task.getVersionedObject();
if (versionedObj != null) {
DomainFile vfile = versionedObj.getDomainFile();
tool.acceptDomainFiles(new DomainFile[] {vfile});
versionedObj.release(this);
}
}
}

View File

@ -1,38 +0,0 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.framework.main.datatree;
import ghidra.framework.PluggableServiceRegistry;
public class DataFlavorHandlerService {
static {
PluggableServiceRegistry.registerPluggableService(DataFlavorHandlerService.class, new DataFlavorHandlerService());
}
public static void registerDataFlavorHandlers() {
DataFlavorHandlerService factory = PluggableServiceRegistry.getPluggableService(DataFlavorHandlerService.class);
factory.doRegisterDataFlavorHandlers();
}
protected void doRegisterDataFlavorHandlers() {
final LocalTreeNodeHandler localTreeNodeHandler = new LocalTreeNodeHandler();
DataTreeDragNDropHandler.addActiveDataFlavorHandler(DataTreeDragNDropHandler.localDomainFileTreeFlavor, localTreeNodeHandler);
DataTreeDragNDropHandler.addActiveDataFlavorHandler(VersionInfoTransferable.localVersionInfoFlavor, new LocalVersionInfoHandler());
DataTreeDragNDropHandler.addInactiveDataFlavorHandler(DataTreeDragNDropHandler.localDomainFileTreeFlavor, localTreeNodeHandler);
}
}

View File

@ -16,8 +16,7 @@
package ghidra.framework.main.datatree;
import java.awt.Component;
import java.awt.event.*;
import java.util.List;
import java.awt.event.KeyEvent;
import javax.swing.JTree;
import javax.swing.KeyStroke;
@ -30,20 +29,13 @@ import docking.widgets.tree.support.GTreeRenderer;
import ghidra.framework.main.FrontEndTool;
/**
* Tree that shows the folders and domain files in a Project.
* Tree that shows the folders and domain files in a Project
*/
public class DataTree extends GTree {
static {
DataFlavorHandlerService.registerDataFlavorHandlers();
}
private boolean isActive;
private DataTreeDragNDropHandler dragNDropHandler;
/**
* Constructor
* @param folder root domain folder for the project.
*/
DataTree(FrontEndTool tool, GTreeRootNode root) {
super(root);
@ -53,30 +45,11 @@ public class DataTree extends GTree {
docking.ToolTipManager.sharedInstance().registerComponent(this);
//When the user right clicks, change selection to what the mouse was under
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent evt) {
if (evt.getButton() == MouseEvent.BUTTON3) {
//Find the would-be newly selected path
TreePath newPath = getPathForLocation(evt.getX(), evt.getY());
if (newPath == null) {
return;
}
//Determine if the path is already selected--If so, do not change the selection
TreePath[] paths = getSelectionPaths();
if (paths != null) {
for (TreePath element : paths) {
if (element.equals(newPath)) {
return;
}
}
}
}
}
});
if (tool != null) {
dragNDropHandler = new DataTreeDragNDropHandler(tool, this, isActive);
setDragNDropHandler(dragNDropHandler);
}
initializeKeyEvents();
}
@ -92,74 +65,10 @@ public class DataTree extends GTree {
KeyStroke.getKeyStroke(KeyEvent.VK_X, DockingUtils.CONTROL_KEY_MODIFIER_MASK));
}
void setProjectActive(boolean b) {
dragNDropHandler.setProjectActive(b);
void setProjectActive(boolean isActive) {
if (dragNDropHandler != null) {
dragNDropHandler.setProjectActive(isActive);
}
/**
* Return true if this path has all of its subpaths expanded.
*/
public boolean allPathsExpanded(TreePath path) {
GTreeNode node = (GTreeNode) path.getLastPathComponent();
if (node.isLeaf()) {
return true;
}
if (isCollapsed(path)) {
return false;
}
boolean allLeaves = true;
List<GTreeNode> children = node.getChildren();
for (GTreeNode child : children) {
if (child.isLeaf()) {
continue;
}
allLeaves = false;
if (!isExpanded(child.getTreePath())) {
return false;
}
if (!allPathsExpanded(child.getTreePath())) {
return false;
}
}
if (allLeaves) {
return isExpanded(path);
}
return true;
}
/**
* Return true if this path has all of its subpaths collapsed.
*/
public boolean allPathsCollapsed(TreePath path) {
GTreeNode node = (GTreeNode) path.getLastPathComponent();
if (isExpanded(path)) {
return false;
}
boolean allLeaves = true; // variable for knowing whether all children are leaves
node.getChildren();
for (GTreeNode child : node) {
if (child.isLeaf()) {
continue;
}
allLeaves = false;
if (!isCollapsed(child.getTreePath())) {
return false;
}
if (!allPathsCollapsed(child.getTreePath())) {
return false;
}
}
if (allLeaves) {
return isCollapsed(path);
}
return true;
}
public void clearSelection() {
@ -183,20 +92,7 @@ public class DataTree extends GTree {
getJTree().stopEditing();
}
//////////////////////////////////////////////////////////////////////
// *** private methods
//////////////////////////////////////////////////////////////////////
/**
* Tree cell renderer to use the appropriate icons for the
* DataTreeNodes.
*/
private class DataTreeCellRenderer extends GTreeRenderer {
/**
* Configures the renderer based on the passed in components.
* The icon is set according to value, expanded, and leaf
* parameters.
*/
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel,
boolean expanded, boolean leaf, int row, boolean doesHaveFocus) {
@ -209,7 +105,5 @@ public class DataTree extends GTree {
}
return this;
}
}
}

View File

@ -30,9 +30,7 @@ import ghidra.util.Msg;
import ghidra.util.exception.AssertException;
public class DataTreeDragNDropHandler implements GTreeDragNDropHandler {
private static Map<DataFlavor, DataFlavorHandler> activeProjectDropFlavorHandlerMap =
new HashMap<>();
private static Map<DataFlavor, DataFlavorHandler> inactiveProjectDropFlavorHandlerMap =
private static Map<DataFlavor, DataTreeFlavorHandler> activeProjectDropFlavorHandlerMap =
new HashMap<>();
public static DataFlavor localDomainFileTreeFlavor = createLocalTreeNodeFlavor();
public static DataFlavor localDomainFileFlavor = createLocalTreeFlavor();
@ -79,29 +77,31 @@ public class DataTreeDragNDropHandler implements GTreeDragNDropHandler {
public void drop(GTreeNode destination, Transferable transferable, int dropAction) {
DataFlavor[] transferDataFlavors = transferable.getTransferDataFlavors();
for (DataFlavor dataFlavor : transferDataFlavors) {
DataFlavorHandler flavorHandler = getFlavorHandler(dataFlavor);
DataTreeFlavorHandler flavorHandler = getFlavorHandler(dataFlavor);
if (flavorHandler != null) {
try {
Object transferData = transferable.getTransferData(dataFlavor);
flavorHandler.handle(tool, tree, destination, transferData, dropAction);
}
catch (UnsupportedFlavorException e) {
throw new AssertException(
"Got unsupported flavor from using a supported flavor");
}
catch (IOException e) {
Msg.showError(this, null, "IO Error", "Error during drop", e);
}
handleDrop(destination, transferable, dropAction, dataFlavor, flavorHandler);
return;
}
}
}
private DataFlavorHandler getFlavorHandler(DataFlavor flavor) {
if (isActiveProject) {
return activeProjectDropFlavorHandlerMap.get(flavor);
private void handleDrop(GTreeNode destination, Transferable transferable, int dropAction,
DataFlavor dataFlavor, DataTreeFlavorHandler flavorHandler) {
try {
Object transferData = transferable.getTransferData(dataFlavor);
flavorHandler.handle(tool, tree, destination, transferData, dropAction);
}
return inactiveProjectDropFlavorHandlerMap.get(flavor);
catch (UnsupportedFlavorException e) {
throw new AssertException("Got unsupported flavor from using a supported flavor");
}
catch (IOException e) {
Msg.showError(this, null, "IO Error", "Error during drop", e);
}
}
private DataTreeFlavorHandler getFlavorHandler(DataFlavor flavor) {
return activeProjectDropFlavorHandlerMap.get(flavor);
}
@Override
@ -134,13 +134,6 @@ public class DataTreeDragNDropHandler implements GTreeDragNDropHandler {
@Override
public DataFlavor[] getSupportedDataFlavors(List<GTreeNode> transferNodes) {
return allSupportedFlavors;
// Set<DataFlavor> keySet = null;
// if (isActiveProject) {
// keySet = activeProjectDropFlavorHandlerMap.keySet();
// } else {
// keySet = inactiveProjectDropFlavorHandlerMap.keySet();
// }
// return keySet.toArray(new DataFlavor[keySet.size()]);
}
@Override
@ -196,24 +189,15 @@ public class DataTreeDragNDropHandler implements GTreeDragNDropHandler {
return false;
}
public static void addActiveDataFlavorHandler(DataFlavor flavor, DataFlavorHandler handler) {
public static void addActiveDataFlavorHandler(DataFlavor flavor, DataTreeFlavorHandler handler) {
activeProjectDropFlavorHandlerMap.put(flavor, handler);
}
public static void addInactiveDataFlavorHandler(DataFlavor flavor, DataFlavorHandler handler) {
inactiveProjectDropFlavorHandlerMap.put(flavor, handler);
}
public static DataFlavorHandler removeActiveDataFlavorHandler(DataFlavor flavor) {
public static DataTreeFlavorHandler removeActiveDataFlavorHandler(DataFlavor flavor) {
return activeProjectDropFlavorHandlerMap.remove(flavor);
}
public static DataFlavorHandler removeInctiveDataFlavorHandler(DataFlavor flavor) {
return inactiveProjectDropFlavorHandlerMap.remove(flavor);
}
public void setProjectActive(boolean b) {
isActiveProject = b;
}
}

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,10 +15,13 @@
*/
package ghidra.framework.main.datatree;
import ghidra.framework.main.FrontEndTool;
import docking.widgets.tree.GTreeNode;
import ghidra.framework.plugintool.PluginTool;
public interface DataFlavorHandler {
public void handle(FrontEndTool tool, DataTree dataTree, GTreeNode destinationNode,
/**
* Interface for classes that will handle drop actions for {@link DataTree}s.
*/
public interface DataTreeFlavorHandler {
public void handle(PluginTool tool, DataTree dataTree, GTreeNode destinationNode,
Object transferData, int dropAction);
}

View File

@ -15,29 +15,55 @@
*/
package ghidra.framework.main.datatree;
import java.awt.datatransfer.DataFlavor;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTargetDropEvent;
import java.io.IOException;
import java.util.List;
import javax.swing.SwingUtilities;
import docking.widgets.tree.GTreeNode;
import docking.widgets.tree.GTreeState;
import ghidra.framework.main.FrontEndTool;
import ghidra.app.util.FileOpenDataFlavorHandler;
import ghidra.framework.model.DomainFile;
import ghidra.framework.model.DomainFolder;
import ghidra.framework.plugintool.PluginTool;
import ghidra.util.Msg;
import ghidra.util.SystemUtilities;
import ghidra.util.exception.DuplicateFileException;
import ghidra.util.exception.FileInUseException;
import ghidra.util.task.*;
final class LocalTreeNodeHandler implements DataFlavorHandler {
public final class LocalTreeNodeHandler
implements DataTreeFlavorHandler, FileOpenDataFlavorHandler {
private DataTree dataTree;
private GTreeState treeState;
@Override
public void handle(PluginTool tool, Object obj, DropTargetDropEvent e, DataFlavor f) {
if (f.equals(DataTreeDragNDropHandler.localDomainFileFlavor)) {
List<?> files = (List<?>) obj;
DomainFile[] domainFiles = new DomainFile[files.size()];
for (int i = 0; i < files.size(); i++) {
domainFiles[i] = (DomainFile) files.get(i);
}
tool.acceptDomainFiles(domainFiles);
}
else if (f.equals(DataTreeDragNDropHandler.localDomainFileTreeFlavor)) {
List<?> files = (List<?>) obj;
DomainFile[] domainFiles = new DomainFile[files.size()];
for (int i = 0; i < files.size(); i++) {
DomainFileNode node = (DomainFileNode) files.get(i);
domainFiles[i] = node.getDomainFile();
}
tool.acceptDomainFiles(domainFiles);
}
}
@Override
@SuppressWarnings("unchecked")
public void handle(FrontEndTool tool, DataTree tree, GTreeNode destinationNode,
public void handle(PluginTool tool, DataTree tree, GTreeNode destinationNode,
Object transferData, int dropAction) {
this.dataTree = tree;
@ -52,12 +78,9 @@ final class LocalTreeNodeHandler implements DataFlavorHandler {
new TaskLauncher(task, dataTree, 1000);
if (treeState != null) { // is set to null if drag results in a task
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
SystemUtilities.runSwingLater(() -> {
treeState.updateStateForMovedNodes();
dataTree.restoreTreeState(treeState);
}
});
}
}

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -14,23 +13,42 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
*
*/
package ghidra.framework.main.datatree;
import ghidra.framework.client.*;
import ghidra.framework.main.FrontEndTool;
import ghidra.framework.model.DomainFile;
import ghidra.framework.model.DomainFolder;
import ghidra.util.task.TaskLauncher;
import java.awt.datatransfer.DataFlavor;
import java.awt.dnd.DropTargetDropEvent;
import java.io.IOException;
import docking.widgets.tree.GTreeNode;
import ghidra.app.util.FileOpenDataFlavorHandler;
import ghidra.framework.client.*;
import ghidra.framework.main.GetVersionedObjectTask;
import ghidra.framework.model.*;
import ghidra.framework.plugintool.PluginTool;
import ghidra.util.task.TaskLauncher;
final class LocalVersionInfoHandler implements DataFlavorHandler {
public void handle(FrontEndTool tool, DataTree dataTree, GTreeNode destinationNode,
public final class LocalVersionInfoHandler
implements DataTreeFlavorHandler, FileOpenDataFlavorHandler {
@Override
public void handle(PluginTool tool, Object obj, DropTargetDropEvent e, DataFlavor f) {
VersionInfo info = (VersionInfo) obj;
DomainFile file = tool.getProject().getProjectData().getFile(info.getDomainFilePath());
GetVersionedObjectTask task =
new GetVersionedObjectTask(this, file, info.getVersionNumber());
tool.execute(task, 250);
DomainObject versionedObj = task.getVersionedObject();
if (versionedObj != null) {
DomainFile vfile = versionedObj.getDomainFile();
tool.acceptDomainFiles(new DomainFile[] { vfile });
versionedObj.release(this);
}
}
@Override
public void handle(PluginTool tool, DataTree dataTree, GTreeNode destinationNode,
Object transferData, int dropAction) {
DomainFolder folder = getDomainFolder(destinationNode);
@ -42,10 +60,13 @@ final class LocalVersionInfoHandler implements DataFlavorHandler {
}
DomainFile file = tool.getProject().getProjectData().getFile(info.getDomainFilePath());
if (file != null) {
new TaskLauncher(new CopyFileVersionTask(file, info.getVersionNumber(), folder), dataTree, 500);
new TaskLauncher(new CopyFileVersionTask(file, info.getVersionNumber(), folder),
dataTree, 500);
}
}
catch (NotConnectedException exc) {}
catch (NotConnectedException exc) {
// not sure why we squash this?
}
catch (IOException exc) {
ClientUtil.handleException(rep, exc, "Repository Connection", tool.getToolFrame());
}

View File

@ -55,33 +55,26 @@ public class ProjectDataTreePanel extends JPanel {
private ChangeManager changeMgr;
private boolean isActiveProject;
private FrontEndTool tool; // may be null if the panel is inside of the
// these may be null if the panel is inside of a dialog
private FrontEndTool tool;
private FrontEndPlugin plugin;
// data tree dialog
/**
* Construct an empty panel that is going to be used as the active
* panel.
* @param tool front end tool
* Construct an empty panel that is going to be used as the active panel
* @param plugin front end plugin
*/
public ProjectDataTreePanel(FrontEndPlugin plugin) {
this(null, true, plugin, null);
}
/**
* Construct a new DataTreePanel.
* Constructor
*
* @param projectName name of project
* @param projectData object that provides access to all the user data
* folders in a project
* @param isActiveProject true if the project is active, and the
* data tree may be modified
* @param tool front end tool; will be null if the panel is used in a dialog
* @param actionMgr class to handle enablement of actions; an ActionManager
* is passed in when several data tree panels all need to use the
* same ActionManager, e.g., the viewed projects in the front end tool;
* actionMgr will be null if the panel is used in a dialog
* @param plugin front end plugin; will be null if the panel is used in a dialog
* @param filter optional filter that is used to hide programs from view
*/
public ProjectDataTreePanel(String projectName, boolean isActiveProject, FrontEndPlugin plugin,
DomainFileFilter filter) {
@ -211,9 +204,6 @@ public class ProjectDataTreePanel extends JPanel {
}
}
/**
* Set the help location for the data tree.
*/
public void setHelpLocation(HelpLocation helpLocation) {
HelpService help = Help.getHelpService();
help.registerHelp(tree, helpLocation);
@ -228,8 +218,9 @@ public class ProjectDataTreePanel extends JPanel {
}
/**
* Get the number of selected items in the tree.
* These could be either DomainFile's or DomainFolder's.
* Get the number of selected items in the tree. These could be either files or folders.
*
* @return the number of selected items in the tree.
*/
public int getSelectedItemCount() {
return tree.getSelectionCount();
@ -279,47 +270,31 @@ public class ProjectDataTreePanel extends JPanel {
tree.removeGTreeSelectionListener(l);
}
/**
* Add a mouse listener to the data tree.
*/
public void addTreeMouseListener(MouseListener l) {
tree.addMouseListener(l);
}
/**
* Remove the mouse listener from the data tree.
*/
public void removeTreeMouseListener(MouseListener l) {
tree.removeMouseListener(l);
}
/**
* Set the preferred size of the scroll pane that contains the
* data tree.
*/
public void setPreferredTreePanelSize(Dimension d) {
tree.setPreferredSize(d);
}
/**
* Get the project data that this data tree panel is operating on.
*/
public ProjectData getProjectData() {
return projectData;
}
/**
* Notification that the project was renamed; update the root node name
* and reload the node.
* and reload the node
* @param newName the new project name
*/
public void projectRenamed(String newName) {
updateProjectName(newName);
}
/**
* Notification that panel is being disposed.
*
*/
public void dispose() {
if (projectData != null) {
projectData.removeDomainFolderChangeListener(changeMgr);
@ -328,10 +303,12 @@ public class ProjectDataTreePanel extends JPanel {
}
/**
* Get the data tree node that is selected.
* @param e mouse event for the popup; may be null if this is being
* called as a result of the key binding pressed
* @return null if there is no selection
* Get the data tree node that is selected
*
* @param provider the provider with which to construct the new context
* @param e mouse event for the popup; may be null if this is being called as a result of
* the key binding pressed
* @return the new context; null if there is no selection
*/
public ActionContext getActionContext(ComponentProvider provider, MouseEvent e) {
if (root instanceof NoProjectNode) {
@ -364,9 +341,6 @@ public class ProjectDataTreePanel extends JPanel {
domainFolderList, domainFileList, tree, isActiveProject);
}
/**
* get the data tree for this data tree panel.
*/
public DataTree getDataTree() {
return tree;
}
@ -380,17 +354,10 @@ public class ProjectDataTreePanel extends JPanel {
tree.setFilterVisible(enabled);
}
/**
* Return true if this data tree panel is listening to domain folder
* changes.
*/
boolean domainFolderListenerAdded() {
return changeMgr != null;
}
/**
* Get the folder change listener.
*/
DomainFolderChangeListener getFolderChangeListener() {
return changeMgr;
}