GP-3123 - Fixed broken tool button animation

This commit is contained in:
dragonmacher
2023-02-23 10:14:03 -05:00
parent ebde3f7250
commit 8734960b1b
7 changed files with 140 additions and 149 deletions

View File

@@ -485,11 +485,11 @@ public class FrontEndPlugin extends Plugin
* general project utility that brings up a file chooser for
* the user to specify a directory and filename that are used
* for the Project location and name
*
*
* @param fileChooser the chooser used to pick the project
* @param mode read-only or not
* @param preferenceName the preference property name used to save the last opened project
* @return the project locator for the opened project
* @return the project locator for the opened project
*/
ProjectLocator chooseProject(GhidraFileChooser fileChooser, String mode,
String preferenceName) {
@@ -703,9 +703,9 @@ public class FrontEndPlugin extends Plugin
connectionButton.setContentAreaFilled(false);
connectionButton.setSelected(isConnected);
connectionButton.setBorder(
isConnected ? BorderFactory.createBevelBorder(BevelBorder.LOWERED)
: BorderFactory.createBevelBorder(BevelBorder.RAISED));
connectionButton
.setBorder(isConnected ? BorderFactory.createBevelBorder(BevelBorder.LOWERED)
: BorderFactory.createBevelBorder(BevelBorder.RAISED));
connectionIconPanel.add(connectionButton);
if (isConnected) {
@@ -716,8 +716,8 @@ public class FrontEndPlugin extends Plugin
Msg.debug(this, "Unexpected exception retrieving user from repository", e);
}
}
repositoryLabel.setText(
"Project Repository: " + repository.getName() + getAccessString(user));
repositoryLabel
.setText("Project Repository: " + repository.getName() + getAccessString(user));
String serverName = repository.getServerInfo().getServerName();
connectionButton.setToolTipText(
@@ -988,8 +988,8 @@ public class FrontEndPlugin extends Plugin
}
};
renameToolAction.setPopupMenuData(new MenuData(new String[] { "Rename..." }, "tool"));
renameToolAction.setHelpLocation(
new HelpLocation(ToolConstants.TOOL_HELP_TOPIC, "Rename Tool"));
renameToolAction
.setHelpLocation(new HelpLocation(ToolConstants.TOOL_HELP_TOPIC, "Rename Tool"));
propertiesAction = new ToolButtonAction(PROPERTIES_ACTION_NAME) {
@Override
@@ -1012,8 +1012,8 @@ public class FrontEndPlugin extends Plugin
propertiesAction.setPopupMenuData(
new MenuData(new String[] { "Configure Plugins..." }, "zproperties"));
propertiesAction.setHelpLocation(
new HelpLocation(ToolConstants.TOOL_HELP_TOPIC, "Configure_Tool"));
propertiesAction
.setHelpLocation(new HelpLocation(ToolConstants.TOOL_HELP_TOPIC, "Configure_Tool"));
tool.addLocalAction(frontEndProvider, exportToolAction);
tool.addLocalAction(frontEndProvider, renameToolAction);
@@ -1103,6 +1103,15 @@ public class FrontEndPlugin extends Plugin
Project project = tool.getProject();
ToolServices toolServices = project.getToolServices();
ToolTemplate defaultToolTemplate = toolServices.getDefaultToolTemplate(domainFile);
if (defaultToolTemplate != null) {
ToolButton button = toolBar.getToolButtonForToolConfig(defaultToolTemplate);
if (button != null) {
button.launchTool(domainFile);
return;
}
}
if (toolServices.launchDefaultTool(domainFile) != null) {
return;
}
@@ -1132,7 +1141,7 @@ public class FrontEndPlugin extends Plugin
DomainFolder domainFolder = linkedFolder.getLinkedFolder();
if (domainFolder != null) {
// delayed to ensure tree is displayd
// delayed to ensure tree is displayed
Swing.runLater(() -> dtp.selectDomainFolder(domainFolder));
}
}

View File

@@ -41,6 +41,7 @@ import ghidra.util.bean.GGlassPane;
import ghidra.util.exception.AssertException;
import help.Help;
import help.HelpService;
import util.CollectionUtils;
import utility.function.Dummy;
/**
@@ -53,8 +54,6 @@ import utility.function.Dummy;
*/
class ToolButton extends EmptyBorderButton implements Draggable, Droppable {
private static final Runnable DUMMY_RUNNABLE = Dummy.runnable();
private DropTarget dropTarget;
private DropTgtAdapter dropTargetAdapter;
private DataFlavor[] acceptableFlavors; // data flavors that this component can support
@@ -145,57 +144,24 @@ class ToolButton extends EmptyBorderButton implements Draggable, Droppable {
}
public void launchTool(DomainFile domainFile) {
doLaunchTool(new DomainFile[] { domainFile });
openFilesAndOpenToolAsNecessary(List.of(domainFile), null);
}
//==================================================================================================
// Droppable interface
//==================================================================================================
/**
* Set drag feedback according to the OK parameter.
* @param ok true means the drop action is OK
* @param e event that has current state of drag and drop operation
*/
@Override
public void dragUnderFeedback(boolean ok, DropTargetDragEvent e) {
// nothing to do
}
/**
* Return true if is OK to drop the transferable at the location
* specified the event.
* @param e event that has current state of drag and drop operation
*/
@Override
@SuppressWarnings("unchecked")
// our data; cast is OK
public boolean isDropOk(DropTargetDragEvent e) {
DataFlavor[] flavors = e.getCurrentDataFlavors();
Transferable transferable = e.getTransferable();
try {
for (DataFlavor element : flavors) {
if (element.equals(DataTreeDragNDropHandler.localDomainFileFlavor)) {
Object draggedData = e.getTransferable()
.getTransferData(DataTreeDragNDropHandler.localDomainFileFlavor);
return containsSupportedDataTypes((List<DomainFile>) draggedData);
}
else if (element.equals(ToolButtonTransferable.localToolButtonFlavor)) {
Object draggedData = e.getTransferable()
.getTransferData(ToolButtonTransferable.localToolButtonFlavor);
ToolButton draggedButton = (ToolButton) draggedData;
if (draggedButton != null) {
if (draggedButton.associatedRunningTool == associatedRunningTool) {
// tool chest -> tool chest is not allowed (both runningTools are null).
// runningTool -> same runningTool is not allowed.
return false;
}
return true;
}
}
else if (element.equals(VersionInfoTransferable.localVersionInfoFlavor)) {
return true;
}
}
return checkForDrop(flavors, transferable);
}
catch (UnsupportedFlavorException e1) {
// don't care; return false
@@ -206,6 +172,36 @@ class ToolButton extends EmptyBorderButton implements Draggable, Droppable {
return false;
}
@SuppressWarnings("unchecked") // our data; cast is OK
private boolean checkForDrop(DataFlavor[] flavors, Transferable transferable)
throws UnsupportedFlavorException, IOException {
for (DataFlavor flavor : flavors) {
if (flavor.equals(DataTreeDragNDropHandler.localDomainFileFlavor)) {
Object draggedData =
transferable.getTransferData(DataTreeDragNDropHandler.localDomainFileFlavor);
return containsSupportedDataTypes((List<DomainFile>) draggedData);
}
else if (flavor.equals(ToolButtonTransferable.localToolButtonFlavor)) {
Object draggedData =
transferable.getTransferData(ToolButtonTransferable.localToolButtonFlavor);
ToolButton draggedButton = (ToolButton) draggedData;
if (draggedButton != null) {
if (draggedButton.associatedRunningTool == associatedRunningTool) {
// tool chest -> tool chest is not allowed (both runningTools are null).
// runningTool -> same runningTool is not allowed.
return false;
}
return true;
}
}
else if (flavor.equals(VersionInfoTransferable.localVersionInfoFlavor)) {
return true;
}
}
return false;
}
/**
* The given list must contain only valid domain files (i.e., no folders or null items)
* @param fileList The list of file objects to validate
@@ -223,23 +219,11 @@ class ToolButton extends EmptyBorderButton implements Draggable, Droppable {
return fileList.size() > 0;
}
/**
* Revert back to normal if any drag feedback was set.
*/
@Override
public void undoDragUnderFeedback() {
// nothing to do
}
/**
* Add the object to the droppable component. The DropTgtAdapter
* calls this method from its drop() method.
*
* @param obj Transferable object that is to be dropped.
* @param event has current state of drop operation
* @param f represents the opaque concept of a data format as
* would appear on a clipboard, during drag and drop.
*/
@Override
public void add(Object obj, DropTargetDropEvent event, DataFlavor f) {
@@ -249,8 +233,7 @@ class ToolButton extends EmptyBorderButton implements Draggable, Droppable {
@SuppressWarnings("unchecked")
// we put the data in
List<DomainFile> list = (List<DomainFile>) obj;
DomainFile[] domainFiles = list.toArray(new DomainFile[list.size()]);
openFilesAndOpenToolAsNecessary(domainFiles);
openFilesAndOpenToolAsNecessary(list, null);
}
else if (f.equals(VersionInfoTransferable.localVersionInfoFlavor)) {
VersionInfo info = (VersionInfo) obj;
@@ -264,7 +247,7 @@ class ToolButton extends EmptyBorderButton implements Draggable, Droppable {
DomainFile domainFile = versionedObj.getDomainFile();
if (isSupportedDataType(domainFile)) {
resetButtonAfterDrag(this);
openFilesAndOpenToolAsNecessary(new DomainFile[] { domainFile },
openFilesAndOpenToolAsNecessary(List.of(domainFile),
() -> versionedObj.release(ToolButton.this));
}
else {
@@ -280,23 +263,12 @@ class ToolButton extends EmptyBorderButton implements Draggable, Droppable {
}
}
private void showFilesNotAcceptedMessage(DomainFile[] domainFiles) {
StringBuffer buffer = new StringBuffer("Tool did not accept files: ");
for (int i = 0; i < domainFiles.length; i++) {
buffer.append(domainFiles[i].getName());
if (i != domainFiles.length - 1) {
buffer.append(", ");
}
}
Msg.showError(this, null, "Error", buffer.toString());
}
private void addFromToolButton(ToolButton toolButton) {
plugin.setToolButtonTransferable(null);
PluginTool tool = null;
if (associatedRunningTool != null && toolButton.associatedRunningTool != null) {
final PluginTool t2 = toolButton.associatedRunningTool;
SwingUtilities.invokeLater(() -> connectTools(associatedRunningTool, t2));
Swing.runLater(() -> connectTools(associatedRunningTool, t2));
return;
}
@@ -305,14 +277,14 @@ class ToolButton extends EmptyBorderButton implements Draggable, Droppable {
tool = plugin.getActiveWorkspace().runTool(toolButton.template);
accepted = tool.acceptDomainFiles(associatedRunningTool.getDomainFiles());
final PluginTool t = tool;
SwingUtilities.invokeLater(() -> connectTools(t, associatedRunningTool));
Swing.runLater(() -> connectTools(t, associatedRunningTool));
}
else {
tool = plugin.getActiveWorkspace().runTool(template);
accepted = tool.acceptDomainFiles(toolButton.associatedRunningTool.getDomainFiles());
final PluginTool t = tool;
final PluginTool t2 = toolButton.associatedRunningTool;
SwingUtilities.invokeLater(() -> connectTools(t, t2));
Swing.runLater(() -> connectTools(t, t2));
}
if (!accepted) {
@@ -332,9 +304,6 @@ class ToolButton extends EmptyBorderButton implements Draggable, Droppable {
connectAll(tc);
}
/**
* Connect all events in the connection object.
*/
private void connectAll(ToolConnection tc) {
String[] events = tc.getEvents();
for (String element : events) {
@@ -345,10 +314,6 @@ class ToolButton extends EmptyBorderButton implements Draggable, Droppable {
tc.getConsumer().getName());
}
/**
* Return true if the domain file's object class is supported by
* this tool.
*/
private boolean isSupportedDataType(DomainFile file) {
if (file == null) {
return false;
@@ -436,19 +401,11 @@ class ToolButton extends EmptyBorderButton implements Draggable, Droppable {
return true;
}
/**
* Called by the DragGestureAdapter to start the drag.
*/
@Override
public DragSourceListener getDragSourceListener() {
return dragSourceAdapter;
}
/**
* Get the object to transfer.
* @param p location of object to transfer
* @return object to transfer
*/
@Override
public Transferable getTransferable(Point p) {
return plugin.getToolButtonTransferable();
@@ -459,16 +416,6 @@ class ToolButton extends EmptyBorderButton implements Draggable, Droppable {
resetButtonAfterDrag(this);
}
/**
* Get the drag actions supported by this drag source:
* <UL>
* <li>DnDConstants.ACTION_MOVE
* <li>DnDConstants.ACTION_COPY
* <li>DnDConstants.ACTION_COPY_OR_MOVE
* </li>
*
* @return the drag actions
*/
@Override
public int getDragAction() {
return dragAction;
@@ -517,24 +464,18 @@ class ToolButton extends EmptyBorderButton implements Draggable, Droppable {
private void activateTool() {
if (associatedRunningTool == null) {
// this is a button on the tool bar, so launch a new tool
doLaunchTool(null);
openFilesAndOpenToolAsNecessary(null, null);
}
else {
associatedRunningTool.toFront();
}
}
private void doLaunchTool(DomainFile[] domainFiles) {
openFilesAndOpenToolAsNecessary(domainFiles);
}
private void openFilesAndOpenToolAsNecessary(final DomainFile[] domainFiles) {
openFilesAndOpenToolAsNecessary(domainFiles, DUMMY_RUNNABLE);
}
private void openFilesAndOpenToolAsNecessary(final DomainFile[] domainFiles,
private void openFilesAndOpenToolAsNecessary(List<DomainFile> domainFiles,
Runnable finishedCallback) {
finishedCallback = Dummy.ifNull(finishedCallback);
if (associatedRunningTool != null) {
// this button has a running tool, no need to open one
openDomainFiles(associatedRunningTool, domainFiles);
@@ -559,9 +500,8 @@ class ToolButton extends EmptyBorderButton implements Draggable, Droppable {
finishedCallback);
}
private void launchToolWithAnimationAndOpenFiles(final DomainFile[] domainFiles,
final JFrame toolFrame, final GGlassPane toolGlassPane,
final Runnable finishedCallback) {
private void launchToolWithAnimationAndOpenFiles(List<DomainFile> domainFiles, JFrame toolFrame,
GGlassPane toolGlassPane, Runnable finishedCallback) {
Icon icon = getIcon();
Point buttonLocation = getLocation();
@@ -591,8 +531,6 @@ class ToolButton extends EmptyBorderButton implements Draggable, Droppable {
MoveImageRunner moveRunner =
new MoveImageRunner(toolGlassPane, startBounds, endBounds, painter);
// a callback that lets us know when to open the given files and restore the state of
// the GhidraGlassPane
TimingTarget finishedTarget = new TimingTargetAdapter() {
@Override
public void end() {
@@ -600,8 +538,7 @@ class ToolButton extends EmptyBorderButton implements Draggable, Droppable {
try {
// cleanup any residual painting effects
toolGlassPane.paintImmediately(toolGlassPane.getBounds());
PluginTool newTool = plugin.getActiveWorkspace().runTool(template);
openDomainFiles(newTool, domainFiles);
openDomainFiles(domainFiles);
}
finally {
// always restore the cursor
@@ -619,19 +556,36 @@ class ToolButton extends EmptyBorderButton implements Draggable, Droppable {
zoomRunner.run();
}
private void openDomainFiles(PluginTool tool, DomainFile[] domainFiles) {
private void openDomainFiles(List<DomainFile> domainFiles) {
if (CollectionUtils.isBlank(domainFiles)) {
plugin.getActiveWorkspace().runTool(template);
}
else {
toolServices.launchDefaultTool(domainFiles);
}
}
private void openDomainFiles(PluginTool tool, List<DomainFile> domainFiles) {
if (domainFiles == null) {
return;
}
boolean accepted = tool.acceptDomainFiles(domainFiles);
boolean accepted = tool.acceptDomainFiles(domainFiles.toArray(DomainFile[]::new));
if (!accepted) {
showFilesNotAcceptedMessage(domainFiles);
}
}
/**
* Set up the objects so we can be a drag and drop site.
*/
private void showFilesNotAcceptedMessage(List<DomainFile> domainFiles) {
StringBuilder buffy = new StringBuilder("Tool did not accept files: ");
for (int i = 0; i < domainFiles.size(); i++) {
buffy.append(domainFiles.get(0).getName());
if (i != domainFiles.size() - 1) {
buffy.append(", ");
}
}
Msg.showError(this, null, "Error", buffy.toString());
}
private void setUpDragDrop() {
acceptableFlavors = new DataFlavor[] { DataTreeDragNDropHandler.localDomainFileFlavor,

View File

@@ -37,7 +37,7 @@ public class ProjectDataOpenDefaultToolAction extends FrontendProjectTreeAction
@Override
protected void actionPerformed(ProjectDataContext context) {
List<DomainFile> selectedFiles = context.getSelectedFiles();
AppInfo.getActiveProject().getToolServices().launchDefaultTool(selectedFiles.get(0));
AppInfo.getActiveProject().getToolServices().launchDefaultTool(selectedFiles);
}
@Override

View File

@@ -17,6 +17,7 @@ package ghidra.framework.model;
import java.io.*;
import java.net.URL;
import java.util.Collection;
import java.util.Set;
import ghidra.framework.plugintool.PluginEvent;
@@ -33,22 +34,22 @@ public interface ToolServices {
/**
* Notify the framework that the tool is closing.
*
*
* @param tool tool that is closing
*/
public void closeTool(PluginTool tool);
/**
* Saves the tool's configuration in the standard
* tool location.
*
* tool location.
*
* @param tool tool to save.
*/
public void saveTool(PluginTool tool);
/**
* Save the tool to the given location on the local file system.
*
*
* @param tool the tool template to write
* @return the file to which the tool was saved
* @throws FileNotFoundException thrown if the file's directory doesn't exist.
@@ -67,7 +68,7 @@ public interface ToolServices {
* If it finds a matching tool, then it is brought to the front.
* Otherwise, it creates one and runs it.
* It then invokes the specified event on the running tool.
*
*
* @param tool find/create a tool like this one.
* @param domainFile open this file in the found/created tool.
* @param event invoke this event on the found/created tool
@@ -75,18 +76,18 @@ public interface ToolServices {
public void displaySimilarTool(PluginTool tool, DomainFile domainFile, PluginEvent event);
/**
* Returns the default/preferred tool template which should be used to open the specified
* Returns the default/preferred tool template which should be used to open the specified
* domain file, whether defined by the user or the system default.
*
*
* @param domainFile The file whose preferred tool should be found.
* @return The preferred tool that should be used to open the given file or null if none found.
*/
public ToolTemplate getDefaultToolTemplate(DomainFile domainFile);
/**
* Returns the default/preferred tool template which should be used to open the specified
* Returns the default/preferred tool template which should be used to open the specified
* domain file content type, whether defined by the user or the system default.
*
*
* @param contentType The content type whose preferred tool should be found.
* @return The preferred tool that should be used to open the given file or null if none found.
*/
@@ -100,18 +101,18 @@ public interface ToolServices {
public Set<ToolTemplate> getCompatibleTools(Class<? extends DomainObject> domainClass);
/**
* Returns the {@link ToolAssociationInfo associations}, which describe content
* Returns the {@link ToolAssociationInfo associations}, which describe content
* types and the tools used to open them, for all content types known to the system.
*
*
* @return the associations
* @see #setContentTypeToolAssociations(Set)
*/
public Set<ToolAssociationInfo> getContentTypeToolAssociations();
/**
* Sets the {@link ToolAssociationInfo associations}, which describe content
* types and the tools used to open them, for the system.
*
* Sets the {@link ToolAssociationInfo associations}, which describe content
* types and the tools used to open them, for the system.
*
* @param infos The associations to be applied
* @see #getContentTypeToolAssociations()
*/
@@ -119,13 +120,22 @@ public interface ToolServices {
/**
* Launch the default tool and open the specified domainFile.
* NOTE: running tool re-use is implementation dependent
* NOTE: running tool reuse is implementation dependent
* @param domainFile the file to open
* @return the launched tool. Null returned if a suitable default tool
* for the file content type was not found.
*/
public PluginTool launchDefaultTool(DomainFile domainFile);
/**
* Launch the default tool and open the specified domainFiles.
* NOTE: running tool reuse is implementation dependent
* @param domainFile the file to open
* @return the launched tool. Null returned if a suitable default tool
* for the file content type was not found.
*/
public PluginTool launchDefaultTool(Collection<DomainFile> domainFile);
/**
* Launch the tool with the given name. A domainFile may be specified and will be opened
* if its content type is supported by the tool.
@@ -137,7 +147,7 @@ public interface ToolServices {
/**
* Launch the default tool and open the specified Ghidra URL resource.
* The tool choosen will be based upon the content type of the specified resource.
* The tool chosen will be based upon the content type of the specified resource.
* NOTE: running tool re-use is implementation dependent
* @param ghidraUrl resource to be opened (see {@link GhidraURL})
* @return the launched tool. Null returned if a failure occurs while accessing the specified

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.

View File

@@ -17,6 +17,7 @@ package ghidra.framework.plugintool;
import java.io.*;
import java.net.URL;
import java.util.Collection;
import java.util.Set;
import ghidra.framework.model.*;
@@ -78,6 +79,11 @@ public class ToolServicesAdapter implements ToolServices {
return null;
}
@Override
public PluginTool launchDefaultTool(Collection<DomainFile> domainFile) {
return null;
}
@Override
public PluginTool launchTool(String toolName, DomainFile domainFile) {
return null;

View File

@@ -41,6 +41,7 @@ import ghidra.util.filechooser.GhidraFileChooserModel;
import ghidra.util.filechooser.GhidraFileFilter;
import ghidra.util.task.TaskLauncher;
import ghidra.util.xml.GenericXMLOutputter;
import util.CollectionUtils;
/**
* Implementation of service used to manipulate tools.
@@ -243,6 +244,17 @@ class ToolServicesImpl implements ToolServices {
});
}
@Override
public PluginTool launchDefaultTool(Collection<DomainFile> domainFiles) {
if (CollectionUtils.isBlank(domainFiles)) {
throw new IllegalArgumentException("Domain files cannot be empty");
}
ToolTemplate template = getDefaultToolTemplate(CollectionUtils.any(domainFiles));
return defaultLaunch(template, t -> {
return t.acceptDomainFiles(domainFiles.toArray(DomainFile[]::new));
});
}
@Override
public PluginTool launchTool(String toolName, DomainFile domainFile) {
ToolTemplate template = findToolChestToolTemplate(toolName);
@@ -530,9 +542,9 @@ class ToolServicesImpl implements ToolServices {
/**
* Get all running tools that have the same tool chest tool name as this one.
*
*
* @param tool the tool for comparison.
*
*
* @return array of tools that are running and named the same as this one.
*/
private PluginTool[] getSameNamedRunningTools(PluginTool tool) {
@@ -554,10 +566,10 @@ class ToolServicesImpl implements ToolServices {
/**
* Search the array of tools for one using the given domainFile.
*
*
* @param tools array of tools to search
* @param domainFile domain file to find user of
*
*
* @return first tool found to be using the domainFile
*/
private PluginTool findToolUsingFile(PluginTool[] tools, DomainFile domainFile) {