From 1d5f9ffd5e9d1c6969c7702dd2e7f38926fd6e83 Mon Sep 17 00:00:00 2001
From: dragonmacher <48328597+dragonmacher@users.noreply.github.com>
Date: Thu, 18 Jul 2019 14:22:22 -0400
Subject: [PATCH 1/2] GT-2973 - Navigation - fixed navigation buttons when
using a snapshot in its own window
---
.../navigation/NextPrevAddressPlugin.java | 26 +-
.../base/actions/HorizontalRuleAction.java | 2 +-
.../main/java/docking/ComponentProvider.java | 13 +-
.../main/java/docking/DockableComponent.java | 74 +--
.../src/main/java/docking/DockableHeader.java | 458 ++++++++++--------
.../java/docking/DockingWindowManager.java | 53 +-
.../src/main/java/docking/DropCode.java | 74 +++
.../docking/GlobalMenuAndToolBarManager.java | 10 +-
.../src/main/java/docking/HeaderCursor.java | 168 +++++++
.../java/docking/ShowAllComponentsAction.java | 2 +-
.../java/docking/ShowComponentAction.java | 2 +-
.../java/docking/menu/DockingMenuItemUI.java | 43 +-
.../MultipleActionDockingToolbarButton.java | 22 +-
.../java/docking/menu/ToolBarItemManager.java | 2 +-
.../java/docking/util/AnimationUtils.java | 126 ++++-
.../ghidra/graph/GraphAlgorithmsTest.java | 6 +-
16 files changed, 740 insertions(+), 341 deletions(-)
create mode 100644 Ghidra/Framework/Docking/src/main/java/docking/DropCode.java
create mode 100644 Ghidra/Framework/Docking/src/main/java/docking/HeaderCursor.java
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/NextPrevAddressPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/NextPrevAddressPlugin.java
index 7f28126c1d..ed6bd70079 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/NextPrevAddressPlugin.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/NextPrevAddressPlugin.java
@@ -19,6 +19,7 @@ import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.util.*;
+import javax.swing.Icon;
import javax.swing.ImageIcon;
import docking.ActionContext;
@@ -195,8 +196,9 @@ public class NextPrevAddressPlugin extends Plugin {
}
Navigatable navigatable = getNavigatable(context);
- return historyService.hasNext(navigatable) ||
- historyService.hasPrevious(navigatable);
+ boolean hasNext = historyService.hasNext(navigatable);
+ boolean hasPrevious = historyService.hasPrevious(navigatable);
+ return hasNext || hasPrevious;
}
};
clearAction.setHelpLocation(new HelpLocation(HelpTopics.NAVIGATION, clearAction.getName()));
@@ -289,11 +291,9 @@ public class NextPrevAddressPlugin extends Plugin {
return null;
}
- //////////////////////////////////////////////////////////////////////
- // //
- // Inner Classes //
- // //
- //////////////////////////////////////////////////////////////////////
+//==================================================================================================
+// Inner Classes
+//==================================================================================================
private class NextPreviousAction extends MultiActionDockingAction {
@@ -311,7 +311,12 @@ public class NextPrevAddressPlugin extends Plugin {
@Override
public boolean isValidContext(ActionContext context) {
- return (context instanceof NavigatableActionContext);
+ return false;
+ }
+
+ @Override
+ public boolean isValidGlobalContext(ActionContext globalContext) {
+ return (globalContext instanceof NavigatableActionContext);
}
@Override
@@ -374,8 +379,9 @@ public class NextPrevAddressPlugin extends Plugin {
this.service = service;
this.navigatable = navigatable;
- setMenuBarData(new MenuData(new String[] { buildActionName(location, formatter) },
- navigatable.getNavigatableIcon()));
+ Icon navIcon = navigatable.getNavigatableIcon();
+ setMenuBarData(
+ new MenuData(new String[] { buildActionName(location, formatter) }, navIcon));
setEnabled(true);
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/base/actions/HorizontalRuleAction.java b/Ghidra/Features/Base/src/main/java/ghidra/base/actions/HorizontalRuleAction.java
index a65e811fc7..7b8b8abe8c 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/base/actions/HorizontalRuleAction.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/base/actions/HorizontalRuleAction.java
@@ -32,7 +32,7 @@ public class HorizontalRuleAction extends DockingAction {
*
* @param owner the action owner
* @param topName the name that will appear above the separator bar
- * @param bottomName the name that will apppear below the separator bar
+ * @param bottomName the name that will appear below the separator bar
*/
public HorizontalRuleAction(String owner, String topName, String bottomName) {
super("HorizontalRuleAction: " + ++idCount, owner, false);
diff --git a/Ghidra/Framework/Docking/src/main/java/docking/ComponentProvider.java b/Ghidra/Framework/Docking/src/main/java/docking/ComponentProvider.java
index e9d98fa0da..921ecc4442 100644
--- a/Ghidra/Framework/Docking/src/main/java/docking/ComponentProvider.java
+++ b/Ghidra/Framework/Docking/src/main/java/docking/ComponentProvider.java
@@ -66,8 +66,7 @@ import utilities.util.reflection.ReflectionUtilities;
* Show Provider Action - Each provider has an action to show the provider. For
* typical, non-transient providers (see {@link #setTransient()}) the action will appear in
* the tool's Window menu. You can have your provider also appear in the tool's toolbar
- * by calling {@link #setIcon(Icon, boolean)}, passing true
for
- * isToolbarAction
.
+ * by calling {@link #addToTool()}.
*
* Historical Note: This class was created so that implementors could add local actions within the constructor
* without having to understand that they must first add themselves to the WindowManager.
@@ -800,7 +799,15 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
@Override
public void actionPerformed(ActionContext context) {
- dockingTool.showComponentProvider(ComponentProvider.this, true);
+
+ DockingWindowManager myDwm = DockingWindowManager.getInstance(getComponent());
+ if (myDwm == null) {
+ // don't think this can happen
+ dockingTool.showComponentProvider(ComponentProvider.this, true);
+ return;
+ }
+
+ myDwm.showComponent(ComponentProvider.this, true, true);
}
@Override
diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DockableComponent.java b/Ghidra/Framework/Docking/src/main/java/docking/DockableComponent.java
index 4083aa4458..9b569c4224 100644
--- a/Ghidra/Framework/Docking/src/main/java/docking/DockableComponent.java
+++ b/Ghidra/Framework/Docking/src/main/java/docking/DockableComponent.java
@@ -42,13 +42,9 @@ public class DockableComponent extends JPanel implements ContainerListener {
public static ComponentPlaceholder SOURCE_INFO;
public static boolean DROP_CODE_SET;
- enum DropCode {
- INVALID, STACK, LEFT, RIGHT, TOP, BOTTOM, ROOT, WINDOW
- }
-
private DockableHeader header;
private MouseListener popupListener;
- private ComponentPlaceholder componentInfo;
+ private ComponentPlaceholder placeholder;
private JComponent providerComp;
private Component focusedComponent;
private DockingWindowManager winMgr;
@@ -62,7 +58,7 @@ public class DockableComponent extends JPanel implements ContainerListener {
*/
DockableComponent(ComponentPlaceholder placeholder, boolean isDocking) {
if (placeholder != null) {
- this.componentInfo = placeholder;
+ this.placeholder = placeholder;
winMgr = placeholder.getNode().winMgr;
actionMgr = winMgr.getActionToGuiMapper();
@@ -108,11 +104,11 @@ public class DockableComponent extends JPanel implements ContainerListener {
}
}
- private JComponent initializeComponentPlaceholder(ComponentPlaceholder placeholder) {
- JComponent providerComponent = placeholder.getProviderComponent();
+ private JComponent initializeComponentPlaceholder(ComponentPlaceholder newPlaceholder) {
+ JComponent providerComponent = newPlaceholder.getProviderComponent();
// Ensure that every provider component has a registered help location
- ComponentProvider provider = placeholder.getProvider();
+ ComponentProvider provider = newPlaceholder.getProvider();
HelpLocation helpLocation = provider.getHelpLocation();
HelpLocation location = registerHelpLocation(provider, helpLocation);
@@ -167,45 +163,38 @@ public class DockableComponent extends JPanel implements ContainerListener {
boolean withinBounds = bounds.contains(point);
if (e.isPopupTrigger() && withinBounds) {
- actionMgr.showPopupMenu(componentInfo, e);
+ actionMgr.showPopupMenu(placeholder, e);
}
}
- /**
- * @see java.awt.Component#getMinimumSize()
- */
@Override
public Dimension getMinimumSize() {
return MIN_DIM;
}
- /**
- * Returns the user component that this wraps.
- */
JComponent getProviderComponent() {
return providerComp;
}
/**
- * Returns the info object associated with this DockableComponent.
+ * Returns the placeholder object associated with this DockableComponent
+ * @return the placeholder object associated with this DockableComponent
*/
public ComponentPlaceholder getComponentWindowingPlaceholder() {
- return componentInfo;
+ return placeholder;
}
@Override
public String toString() {
- if (componentInfo == null) {
+ if (placeholder == null) {
return "";
}
- return componentInfo.getFullTitle();
+ return placeholder.getFullTitle();
}
/**
- * Set up for drag and drop.
- *
+ * Translates the given point so that it is relative to the given component
*/
-
private void translate(Point p, Component c) {
Point cLoc = c.getLocationOnScreen();
Point myLoc = getLocationOnScreen();
@@ -219,10 +208,6 @@ public class DockableComponent extends JPanel implements ContainerListener {
super(comp, null);
}
- /**
- *
- * @see java.awt.dnd.DropTargetListener#drop(java.awt.dnd.DropTargetDropEvent)
- */
@Override
public synchronized void drop(DropTargetDropEvent dtde) {
clearAutoscroll();
@@ -230,7 +215,7 @@ public class DockableComponent extends JPanel implements ContainerListener {
Point p = dtde.getLocation();
translate(p, ((DropTarget) dtde.getSource()).getComponent());
setDropCode(p);
- TARGET_INFO = componentInfo;
+ TARGET_INFO = placeholder;
dtde.acceptDrop(dtde.getDropAction());
dtde.dropComplete(true);
}
@@ -239,10 +224,6 @@ public class DockableComponent extends JPanel implements ContainerListener {
}
}
- /**
- *
- * @see java.awt.dnd.DropTargetListener#dragEnter(java.awt.dnd.DropTargetDragEvent)
- */
@Override
public synchronized void dragEnter(DropTargetDragEvent dtde) {
super.dragEnter(dtde);
@@ -258,7 +239,7 @@ public class DockableComponent extends JPanel implements ContainerListener {
Point p = dtde.getLocation();
translate(p, ((DropTarget) dtde.getSource()).getComponent());
setDropCode(p);
- DRAGGED_OVER_INFO = componentInfo;
+ DRAGGED_OVER_INFO = placeholder;
dtde.acceptDrag(dtde.getDropAction());
}
else {
@@ -266,10 +247,6 @@ public class DockableComponent extends JPanel implements ContainerListener {
}
}
- /**
- *
- * @see java.awt.dnd.DropTargetListener#dragOver(java.awt.dnd.DropTargetDragEvent)
- */
@Override
public synchronized void dragOver(DropTargetDragEvent dtde) {
super.dragOver(dtde);
@@ -285,7 +262,7 @@ public class DockableComponent extends JPanel implements ContainerListener {
Point p = dtde.getLocation();
translate(p, ((DropTarget) dtde.getSource()).getComponent());
setDropCode(p);
- DRAGGED_OVER_INFO = componentInfo;
+ DRAGGED_OVER_INFO = placeholder;
dtde.acceptDrag(dtde.getDropAction());
}
else {
@@ -293,10 +270,6 @@ public class DockableComponent extends JPanel implements ContainerListener {
}
}
- /**
- *
- * @see java.awt.dnd.DropTargetListener#dragExit(java.awt.dnd.DropTargetEvent)
- */
@Override
public synchronized void dragExit(DropTargetEvent dte) {
super.dragExit(dte);
@@ -362,7 +335,7 @@ public class DockableComponent extends JPanel implements ContainerListener {
private void setDropCode(Point p) {
DROP_CODE_SET = true;
- if (componentInfo == null) {
+ if (placeholder == null) {
DROP_CODE = DropCode.ROOT;
return;
}
@@ -370,11 +343,11 @@ public class DockableComponent extends JPanel implements ContainerListener {
DROP_CODE = DropCode.WINDOW;
return;
}
- if (SOURCE_INFO.getNode().winMgr != componentInfo.getNode().winMgr) {
+ if (SOURCE_INFO.getNode().winMgr != placeholder.getNode().winMgr) {
DROP_CODE = DropCode.WINDOW;
return;
}
- if (SOURCE_INFO == componentInfo && !componentInfo.isStacked()) {
+ if (SOURCE_INFO == placeholder && !placeholder.isStacked()) {
DROP_CODE = DropCode.INVALID;
return;
}
@@ -390,7 +363,7 @@ public class DockableComponent extends JPanel implements ContainerListener {
else if (p.y > getHeight() - DROP_EDGE_OFFSET) {
DROP_CODE = DropCode.BOTTOM;
}
- else if (SOURCE_INFO == componentInfo) {
+ else if (SOURCE_INFO == placeholder) {
DROP_CODE = DropCode.INVALID;
}
else {
@@ -409,10 +382,6 @@ public class DockableComponent extends JPanel implements ContainerListener {
header.emphasize();
}
- /**
- * Set the title displayed in this component's header.
- * @param title
- */
void setTitle(String title) {
header.setTitle(title);
}
@@ -421,13 +390,10 @@ public class DockableComponent extends JPanel implements ContainerListener {
header.setIcon(icon);
}
- /**
- * Releases all resources for this object.
- */
void dispose() {
header.dispose();
header = null;
- componentInfo = null;
+ placeholder = null;
providerComp = null;
actionMgr = null;
}
diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DockableHeader.java b/Ghidra/Framework/Docking/src/main/java/docking/DockableHeader.java
index 5ad8af4a72..cdd4d28ef5 100644
--- a/Ghidra/Framework/Docking/src/main/java/docking/DockableHeader.java
+++ b/Ghidra/Framework/Docking/src/main/java/docking/DockableHeader.java
@@ -20,13 +20,16 @@ import java.awt.dnd.*;
import java.awt.event.InputEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.*;
+import java.util.List;
+import java.util.stream.Collectors;
-import javax.swing.JFrame;
-import javax.swing.SwingUtilities;
+import javax.swing.*;
import org.jdesktop.animation.timing.Animator;
+import org.jdesktop.animation.timing.Animator.RepeatBehavior;
+import org.jdesktop.animation.timing.TimingTargetAdapter;
+import org.jdesktop.animation.timing.interpolation.PropertySetter;
import docking.help.Help;
import docking.help.HelpService;
@@ -37,6 +40,8 @@ import ghidra.framework.Platform;
import ghidra.util.HelpLocation;
import ghidra.util.Msg;
import ghidra.util.bean.GGlassPane;
+import ghidra.util.bean.GGlassPanePainter;
+import resources.ResourceManager;
/**
* Component for providing component titles and toolbar. Also provides Drag
@@ -46,41 +51,6 @@ public class DockableHeader extends GenericHeader
implements DragGestureListener, DragSourceListener {
private DockableComponent dockComp;
- private static Cursor leftCursor;
- private static Cursor rightCursor;
- private static Cursor topCursor;
- private static Cursor bottomCursor;
- private static Cursor stackCursor;
- private static Cursor newWindowCursor;
- private static Cursor noDropCursor = DragSource.DefaultMoveNoDrop;
-
- static {
- Toolkit tk = Toolkit.getDefaultToolkit();
-
- BufferedImage image = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB);
- drawLeftArrow(image);
- leftCursor = tk.createCustomCursor(image, new Point(0, 6), "LEFT");
-
- image = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB);
- drawRightArrow(image);
- rightCursor = tk.createCustomCursor(image, new Point(31, 6), "RIGHT");
-
- image = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB);
- drawTopArrow(image);
- topCursor = tk.createCustomCursor(image, new Point(6, 0), "TOP");
-
- image = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB);
- drawBottomArrow(image);
- bottomCursor = tk.createCustomCursor(image, new Point(6, 31), "BOTTOM");
-
- image = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB);
- drawStack(image);
- stackCursor = tk.createCustomCursor(image, new Point(8, 8), "STACK");
-
- image = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB);
- drawNewWindow(image);
- newWindowCursor = tk.createCustomCursor(image, new Point(0, 0), "NEW_WINDOW");
- }
private DragCursorManager dragCursorManager = createDragCursorManager();
private DragSource dragSource = null;
@@ -174,17 +144,61 @@ public class DockableHeader extends GenericHeader
protected Animator createEmphasizingAnimator(JFrame parentFrame) {
double random = Math.random();
- int choices = 4;
+ int choices = 7;
int value = (int) (choices * random);
+
switch (value) {
case 0:
return AnimationUtils.shakeComponent(component);
case 1:
return AnimationUtils.rotateComponent(component);
case 2:
- return raiseComponent(parentFrame);
- default:
return AnimationUtils.pulseComponent(component);
+ case 3:
+ return AnimationUtils.showTheDragonOverComponent(component);
+ case 4:
+ return AnimationUtils.focusComponent(component);
+ case 5:
+ return emphasizeDockableComponent();
+ default:
+ return raiseComponent(parentFrame);
+ }
+ }
+
+ private Animator emphasizeDockableComponent() {
+
+ ComponentPlaceholder placeholder = dockComp.getComponentWindowingPlaceholder();
+ ComponentNode node = placeholder.getNode();
+ WindowNode windowNode = node.getTopLevelNode();
+ Set componentNodes = new HashSet<>();
+ getComponents(windowNode, componentNodes);
+
+ //@formatter:off
+ Set components = componentNodes.stream()
+ .map(cn -> cn.getComponent())
+ .filter(c -> c != null)
+ .filter(c -> !SwingUtilities.isDescendingFrom(component, c))
+ .collect(Collectors.toSet())
+ ;
+ //@formatter:on
+
+ components.remove(component);
+
+ EmphasizeDockableComponentAnimationDriver driver =
+ new EmphasizeDockableComponentAnimationDriver(component, components);
+ return driver.animator;
+ }
+
+ private void getComponents(Node node, Set results) {
+
+ List children = node.getChildren();
+ for (Node child : children) {
+ if (child instanceof ComponentNode) {
+ results.add((ComponentNode) child);
+ }
+ else {
+ getComponents(child, results);
+ }
}
}
@@ -235,7 +249,7 @@ public class DockableHeader extends GenericHeader
(modifiers & InputEvent.BUTTON3_DOWN_MASK) != 0) {
return;
}
- DockableComponent.DROP_CODE = DockableComponent.DropCode.WINDOW;
+ DockableComponent.DROP_CODE = DropCode.WINDOW;
DockableComponent.DROP_CODE_SET = true;
DockableComponent.SOURCE_INFO = dockComp.getComponentWindowingPlaceholder();
@@ -252,7 +266,7 @@ public class DockableHeader extends GenericHeader
ComponentPlaceholder info = dockComp.getComponentWindowingPlaceholder();
DockingWindowManager winMgr = info.getNode().winMgr;
- if (DockableComponent.DROP_CODE == DockableComponent.DropCode.INVALID) {
+ if (DockableComponent.DROP_CODE == DropCode.INVALID) {
return;
}
@@ -262,28 +276,12 @@ public class DockableHeader extends GenericHeader
// return;
// }
// else
- if (DockableComponent.DROP_CODE == DockableComponent.DropCode.WINDOW) {
+ if (DockableComponent.DROP_CODE == DropCode.WINDOW) {
winMgr.movePlaceholder(info, event.getLocation());
}
else {
- winMgr.movePlaceholder(info, DockableComponent.TARGET_INFO, getWindowPosition());
- }
- }
-
- private WindowPosition getWindowPosition() {
- switch (DockableComponent.DROP_CODE) {
- case BOTTOM:
- return WindowPosition.BOTTOM;
- case LEFT:
- return WindowPosition.LEFT;
- case RIGHT:
- return WindowPosition.RIGHT;
- case STACK:
- return WindowPosition.STACK;
- case TOP:
- return WindowPosition.TOP;
- default:
- return WindowPosition.STACK;
+ winMgr.movePlaceholder(info, DockableComponent.TARGET_INFO,
+ DockableComponent.DROP_CODE.getWindowPosition());
}
}
@@ -314,34 +312,7 @@ public class DockableHeader extends GenericHeader
// }
DockableComponent.DROP_CODE_SET = false;
- Cursor c = noDropCursor;
- switch (DockableComponent.DROP_CODE) {
- case LEFT:
- c = leftCursor;
- break;
- case RIGHT:
- c = rightCursor;
- break;
- case TOP:
- c = topCursor;
- break;
- case BOTTOM:
- c = bottomCursor;
- break;
- case STACK:
- c = stackCursor;
- break;
- case ROOT:
- c = stackCursor;
- break;
- case WINDOW:
- c = newWindowCursor;
- break;
- case INVALID:
- break;
-
- }
-
+ Cursor c = DockableComponent.DROP_CODE.getCursor();
dragCursorManager.setCursor(event, c);
}
@@ -467,129 +438,206 @@ public class DockableHeader extends GenericHeader
}
}
-//==================================================================================================
-// Static Methods
-//==================================================================================================
+ public static class EmphasizeDockableComponentAnimationDriver {
- /**
- * Draws the left arrow cursor image.
- *
- * @param image the image object to draw into.
- */
- private static void drawLeftArrow(BufferedImage image) {
- int v = 0xff000000;
- int y = 6;
- for (int i = 0; i < 6; i++) {
- for (int j = 0; j < 2 * i + 1; j++) {
- image.setRGB(i, y - i + j, v);
- }
+ private Animator animator;
+ private GGlassPane glassPane;
+ private EmphasizeDockableComponentPainter rotatePainter;
+
+ EmphasizeDockableComponentAnimationDriver(Component component, Set others) {
+
+ glassPane = AnimationUtils.getGlassPane(component);
+ rotatePainter = new EmphasizeDockableComponentPainter(component, others);
+
+ double start = 0;
+ double max = 1;
+ int duration = 1000;
+ animator = PropertySetter.createAnimator(duration, this, "percentComplete", start, max);
+
+ animator.setAcceleration(0.2f);
+ animator.setDeceleration(0.8f);
+
+ animator.setRepeatCount(2);
+ animator.setRepeatBehavior(RepeatBehavior.REVERSE);
+
+ animator.addTarget(new TimingTargetAdapter() {
+ @Override
+ public void end() {
+ done();
+ }
+ });
+
+ glassPane.addPainter(rotatePainter);
+
+ animator.start();
}
- for (int i = 6; i < 12; i++) {
- for (int j = 0; j < 3; j++) {
- image.setRGB(i, y - 1 + j, v);
- }
+
+ public void setPercentComplete(double percentComplete) {
+ rotatePainter.setPercentComplete(percentComplete);
+ glassPane.repaint();
+ }
+
+ void done() {
+ glassPane.repaint();
+ glassPane.removePainter(rotatePainter);
}
}
- /**
- * Draws the right arrow cursor image.
- *
- * @param image the image object to draw into.
- */
- private static void drawRightArrow(BufferedImage image) {
- int v = 0xff000000;
- int y = 6;
- for (int i = 0; i < 6; i++) {
- for (int j = 0; j < 2 * i + 1; j++) {
- image.setRGB(31 - i, y - i + j, v);
+ private static class EmphasizeDockableComponentPainter implements GGlassPanePainter {
+
+ private Set otherComponentInfos = new HashSet<>();
+ private Image image;
+
+ private Component component;
+ private Rectangle cBounds;
+ private double percentComplete = 0.0;
+
+ EmphasizeDockableComponentPainter(Component component, Set otherComponents) {
+ this.component = component;
+ this.image = paintImage(component);
+
+ for (Component otherComponent : otherComponents) {
+ ComponentPaintInfo info = new ComponentPaintInfo(otherComponent);
+ otherComponentInfos.add(info);
}
}
- for (int i = 6; i < 12; i++) {
- for (int j = 0; j < 3; j++) {
- image.setRGB(31 - i, y - 1 + j, v);
+
+ private class ComponentPaintInfo {
+
+ private Component myComponent;
+ private Image myImage;
+
+ ComponentPaintInfo(Component component) {
+ this.myComponent = component;
+ this.myImage = paintImage(component);
}
+
+ Image getImage() {
+ return myImage;
+ }
+
+ Rectangle getRelativeBounds(Component other) {
+ Rectangle r = myComponent.getBounds();
+ return SwingUtilities.convertRectangle(myComponent.getParent(), r, other);
+ }
+ }
+
+ void setPercentComplete(double percent) {
+ percentComplete = percent;
+ }
+
+ @Override
+ public void paint(GGlassPane glassPane, Graphics g) {
+
+ Graphics2D g2d = (Graphics2D) g;
+ g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+ RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+
+ Color background = new Color(218, 232, 250);
+ g.setColor(background);
+
+ Rectangle othersBounds = null;
+ for (ComponentPaintInfo info : otherComponentInfos) {
+
+ Rectangle b = info.getRelativeBounds(glassPane);
+ if (othersBounds == null) {
+ othersBounds = b;
+ }
+ else {
+ othersBounds.add(b);
+ }
+ }
+
+ if (othersBounds == null) {
+ // No other components in this window. In this case, use the bounds of the
+ // active component. This has the effect of showing the image behind the
+ // active component.
+ Rectangle componentBounds = component.getBounds();
+ componentBounds = SwingUtilities.convertRectangle(component.getParent(),
+ componentBounds, glassPane);
+ othersBounds = componentBounds;
+
+ othersBounds = new Rectangle();
+ }
+
+ g2d.fillRect(othersBounds.x, othersBounds.y, othersBounds.width, othersBounds.height);
+
+ ImageIcon ghidra = ResourceManager.loadImage("images/GhidraIcon256.png");
+ Image ghidraImage = ghidra.getImage();
+
+ double scale = percentComplete * 7;
+ int gw = ghidraImage.getWidth(null);
+ int gh = ghidraImage.getHeight(null);
+ int w = (int) (gw * scale);
+ int h = (int) (gh * scale);
+
+ Rectangle gpBounds = glassPane.getBounds();
+ double cx = gpBounds.getCenterX();
+ double cy = gpBounds.getCenterY();
+ int offsetX = (int) (cx - (w >> 1));
+ int offsetY = (int) (cy - (h >> 1));
+
+ Shape originalClip = g2d.getClip();
+ if (!othersBounds.isEmpty()) {
+ // restrict the icon to the 'others' area; otherwise, place it behind the provider
+ g2d.setClip(othersBounds);
+ }
+ g2d.drawImage(ghidraImage, offsetX, offsetY, w, h, null);
+ g2d.setClip(originalClip);
+
+ paintOthers(glassPane, (Graphics2D) g, background);
+
+ Rectangle b = component.getBounds();
+ Point p = new Point(b.getLocation());
+ p = SwingUtilities.convertPoint(component.getParent(), p, glassPane);
+
+ g2d.setRenderingHints(new RenderingHints(null));
+ g2d.drawImage(image, p.x, p.y, b.width, b.height, null);
+ }
+
+ private void paintOthers(GGlassPane glassPane, Graphics2D g2d, Color background) {
+
+ if (cBounds == null) {
+ cBounds = component.getBounds();
+ cBounds =
+ SwingUtilities.convertRectangle(component.getParent(), cBounds, glassPane);
+ }
+
+ double destinationX = cBounds.getCenterX();
+ double destinationY = cBounds.getCenterY();
+
+ g2d.setColor(background);
+ for (ComponentPaintInfo info : otherComponentInfos) {
+
+ Rectangle b = info.getRelativeBounds(glassPane);
+ double scale = 1 - percentComplete;
+ int w = (int) (b.width * scale);
+ int h = (int) (b.height * scale);
+
+ int offsetX = b.x - ((w - b.width) >> 1);
+ int offsetY = b.y - ((h - b.height) >> 1);
+
+ double deltaX = destinationX - offsetX;
+ double deltaY = destinationY - offsetY;
+
+ double moveX = percentComplete * deltaX;
+ double moveY = percentComplete * deltaY;
+ offsetX += moveX;
+ offsetY += moveY;
+
+ g2d.drawImage(info.getImage(), offsetX, offsetY, w, h, null);
+ }
+ }
+
+ private Image paintImage(Component c) {
+ Rectangle bounds = c.getBounds();
+ BufferedImage bufferedImage =
+ new BufferedImage(bounds.width, bounds.height, BufferedImage.TYPE_INT_ARGB);
+ Graphics g = bufferedImage.getGraphics();
+ c.paint(g);
+ g.dispose();
+ return bufferedImage;
}
}
- /**
- * Draws the up arrow cursor image.
- *
- * @param image the image object to draw into.
- */
- private static void drawTopArrow(BufferedImage image) {
- int v = 0xff000000;
- int x = 6;
- for (int i = 0; i < 6; i++) {
- for (int j = 0; j < 2 * i + 1; j++) {
- image.setRGB(x - i + j, i, v);
- }
- }
- for (int i = 6; i < 12; i++) {
- for (int j = 0; j < 3; j++) {
- image.setRGB(x - 1 + j, i, v);
- }
- }
- }
-
- /**
- * Draws the down arrow cursor image.
- *
- * @param image the image object to draw into.
- */
- private static void drawBottomArrow(BufferedImage image) {
- int v = 0xff000000;
- int x = 6;
- for (int i = 0; i < 6; i++) {
- for (int j = 0; j < 2 * i + 1; j++) {
- image.setRGB(x - i + j, 31 - i, v);
- }
- }
- for (int i = 6; i < 12; i++) {
- for (int j = 0; j < 3; j++) {
- image.setRGB(x - 1 + j, 31 - i, v);
- }
- }
- }
-
- /**
- * Draws the stack cursor image.
- *
- * @param image the image object to draw into.
- */
- private static void drawStack(BufferedImage image) {
- int v = 0xff000000;
- for (int i = 0; i < 3; i++) {
- int x = i * 3;
- int y = 6 - i * 3;
- for (int j = 0; j < 10; j++) {
- image.setRGB(x, y + j, v);
- image.setRGB(x + 10, y + j, v);
- image.setRGB(x + j, y, v);
- image.setRGB(x + j, y + 10, v);
- }
-
- }
- }
-
- /**
- * Draws the "new window" cursor image.
- *
- * @param image the image object to draw into.
- */
- private static void drawNewWindow(BufferedImage image) {
- int v = 0xff000000;
- for (int i = 0; i < 5; i++) {
- for (int j = 0; j < 14; j++) {
- image.setRGB(j, i, 0xff0000ff);
- }
- }
- for (int i = 0; i < 14; i++) {
- image.setRGB(i, 0, v);
- image.setRGB(i, 10, v);
- }
- for (int i = 0; i < 10; i++) {
- image.setRGB(0, i, v);
- image.setRGB(14, i, v);
- }
- }
}
diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java b/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java
index 7799c80bf1..745dedf1e1 100644
--- a/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java
+++ b/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java
@@ -707,19 +707,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
* @see #addComponent(ComponentProvider, boolean)
*/
public void showComponent(ComponentProvider provider, boolean visibleState) {
- ComponentPlaceholder placeholder = getActivePlaceholder(provider);
- if (placeholder != null) {
- showComponent(placeholder, visibleState, true);
- return;
- }
-
- if (visibleState) {
-
- // a null placeholder implies the client is trying to show a provider that has not
- // been added to the tool
- Msg.warn(this, "Attempting to show an unknown Component Provider '" +
- provider.getName() + "' - " + "check that the provider has been added to the tool");
- }
+ showComponent(provider, visibleState, false);
}
public void toFront(ComponentProvider provider) {
@@ -809,6 +797,23 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
root = null;
}
+ void showComponent(ComponentProvider provider, boolean visibleState, boolean shouldEmphasize) {
+
+ ComponentPlaceholder placeholder = getActivePlaceholder(provider);
+ if (placeholder != null) {
+ showComponent(placeholder, visibleState, true, shouldEmphasize);
+ return;
+ }
+
+ if (visibleState) {
+
+ // a null placeholder implies the client is trying to show a provider that has not
+ // been added to the tool
+ Msg.warn(this, "Attempting to show an unknown Component Provider '" +
+ provider.getName() + "' - " + "check that the provider has been added to the tool");
+ }
+ }
+
/**
* Shows or hides the component associated with the given placeholder object.
*
@@ -816,15 +821,20 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
* @param visibleState true to show or false to hide.
* @param requestFocus True signals that the system should request focus on the component.
*/
- void showComponent(final ComponentPlaceholder placeholder, final boolean visibleState,
+ private void showComponent(ComponentPlaceholder placeholder, final boolean visibleState,
boolean requestFocus) {
+ showComponent(placeholder, visibleState, requestFocus, false);
+ }
+
+ void showComponent(ComponentPlaceholder placeholder, final boolean visibleState,
+ boolean requestFocus, boolean shouldEmphasize) {
if (root == null) {
return;
}
if (visibleState == placeholder.isShowing()) {
if (visibleState) {
- movePlaceholderToFront(placeholder, true);
+ movePlaceholderToFront(placeholder, shouldEmphasize);
setNextFocusPlaceholder(placeholder);
scheduleUpdate();
}
@@ -2066,17 +2076,19 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
}
public void contextChanged(ComponentProvider provider) {
+
if (provider == null) {
- actionToGuiMapper.contextChangedAll(); // update all windows;
+ actionToGuiMapper.contextChangedAll(); // this updates the actions for all windows
return;
}
- ComponentPlaceholder placeHolder = getActivePlaceholder(provider);
- if (placeHolder == null) {
+ ComponentPlaceholder placeholder = getActivePlaceholder(provider);
+ if (placeholder == null) {
return;
}
- placeHolder.contextChanged();
- actionToGuiMapper.contextChanged(placeHolder);
+
+ placeholder.contextChanged();
+ actionToGuiMapper.contextChanged(placeholder);
}
public void addContextListener(DockingContextListener listener) {
@@ -2143,7 +2155,6 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
private ComponentPlaceholder lastActivatedPlaceholder;
void activated(ComponentPlaceholder placeholder) {
-
if (lastActivatedPlaceholder == placeholder) {
// repeat call--see if it was quickly called again (a sign of confusion/frustration)
long elapsedTime = System.currentTimeMillis() - lastCalledTimestamp;
diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DropCode.java b/Ghidra/Framework/Docking/src/main/java/docking/DropCode.java
new file mode 100644
index 0000000000..6de555096e
--- /dev/null
+++ b/Ghidra/Framework/Docking/src/main/java/docking/DropCode.java
@@ -0,0 +1,74 @@
+/* ###
+ * 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 docking;
+
+import java.awt.Cursor;
+
+/**
+ * An enum that represents available drag-n-drop options for a docking tool. There are also
+ * convenience methods for translating this drop code into a cursor and window position.
+ */
+enum DropCode {
+ INVALID, STACK, LEFT, RIGHT, TOP, BOTTOM, ROOT, WINDOW;
+
+ public Cursor getCursor() {
+ Cursor c = HeaderCursor.NO_DROP;
+ switch (this) {
+ case LEFT:
+ c = HeaderCursor.LEFT;
+ break;
+ case RIGHT:
+ c = HeaderCursor.RIGHT;
+ break;
+ case TOP:
+ c = HeaderCursor.TOP;
+ break;
+ case BOTTOM:
+ c = HeaderCursor.BOTTOM;
+ break;
+ case STACK:
+ c = HeaderCursor.STACK;
+ break;
+ case ROOT:
+ c = HeaderCursor.STACK;
+ break;
+ case WINDOW:
+ c = HeaderCursor.NEW_WINDOW;
+ break;
+ case INVALID:
+ break;
+ }
+
+ return c;
+ }
+
+ public WindowPosition getWindowPosition() {
+ switch (this) {
+ case BOTTOM:
+ return WindowPosition.BOTTOM;
+ case LEFT:
+ return WindowPosition.LEFT;
+ case RIGHT:
+ return WindowPosition.RIGHT;
+ case STACK:
+ return WindowPosition.STACK;
+ case TOP:
+ return WindowPosition.TOP;
+ default:
+ return WindowPosition.STACK;
+ }
+ }
+}
diff --git a/Ghidra/Framework/Docking/src/main/java/docking/GlobalMenuAndToolBarManager.java b/Ghidra/Framework/Docking/src/main/java/docking/GlobalMenuAndToolBarManager.java
index 4ae713f1ed..fd2096b8ce 100644
--- a/Ghidra/Framework/Docking/src/main/java/docking/GlobalMenuAndToolBarManager.java
+++ b/Ghidra/Framework/Docking/src/main/java/docking/GlobalMenuAndToolBarManager.java
@@ -164,13 +164,17 @@ public class GlobalMenuAndToolBarManager implements DockingWindowListener {
}
public void contextChanged(ComponentPlaceholder placeHolder) {
+ if (placeHolder == null) {
+ return;
+ }
+
WindowNode topLevelNode = placeHolder.getTopLevelNode();
if (topLevelNode == null) {
- return; // no provider in this window has focus - can this happen?
+ return;
}
+
if (topLevelNode.getLastFocusedProviderInWindow() != placeHolder) {
- return; // actions in this window are not currently responding to the provider
- // whose context has changed.
+ return; // actions in this window are not currently responding to this provider
}
WindowActionManager windowActionManager = windowToActionManagerMap.get(topLevelNode);
diff --git a/Ghidra/Framework/Docking/src/main/java/docking/HeaderCursor.java b/Ghidra/Framework/Docking/src/main/java/docking/HeaderCursor.java
new file mode 100644
index 0000000000..a5b03c1574
--- /dev/null
+++ b/Ghidra/Framework/Docking/src/main/java/docking/HeaderCursor.java
@@ -0,0 +1,168 @@
+/* ###
+ * 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 docking;
+
+import java.awt.*;
+import java.awt.dnd.DragSource;
+import java.awt.image.BufferedImage;
+
+/**
+ * The cursor values used when drag-n-dropping dockable components
+ */
+public class HeaderCursor {
+
+ static Cursor LEFT;
+ static Cursor RIGHT;
+ static Cursor TOP;
+ static Cursor BOTTOM;
+ static Cursor STACK;
+ static Cursor NEW_WINDOW;
+ static Cursor NO_DROP = DragSource.DefaultMoveNoDrop;
+
+ static {
+ Toolkit tk = Toolkit.getDefaultToolkit();
+
+ Image image = drawLeftArrow();
+ LEFT = tk.createCustomCursor(image, new Point(0, 6), "LEFT");
+
+ image = drawRightArrow();
+ RIGHT = tk.createCustomCursor(image, new Point(31, 6), "RIGHT");
+
+ image = drawTopArrow();
+ TOP = tk.createCustomCursor(image, new Point(6, 0), "TOP");
+
+ image = drawBottomArrow();
+ BOTTOM = tk.createCustomCursor(image, new Point(6, 31), "BOTTOM");
+
+ image = drawStack();
+ STACK = tk.createCustomCursor(image, new Point(8, 8), "STACK");
+
+ image = drawNewWindow();
+ NEW_WINDOW = tk.createCustomCursor(image, new Point(0, 0), "NEW_WINDOW");
+ }
+
+ private static Image drawLeftArrow() {
+
+ BufferedImage image = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB);
+ int v = 0xff000000;
+ int y = 6;
+ for (int i = 0; i < 6; i++) {
+ for (int j = 0; j < 2 * i + 1; j++) {
+ image.setRGB(i, y - i + j, v);
+ }
+ }
+ for (int i = 6; i < 12; i++) {
+ for (int j = 0; j < 3; j++) {
+ image.setRGB(i, y - 1 + j, v);
+ }
+ }
+
+ return image;
+ }
+
+ private static Image drawRightArrow() {
+
+ BufferedImage image = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB);
+ int v = 0xff000000;
+ int y = 6;
+ for (int i = 0; i < 6; i++) {
+ for (int j = 0; j < 2 * i + 1; j++) {
+ image.setRGB(31 - i, y - i + j, v);
+ }
+ }
+ for (int i = 6; i < 12; i++) {
+ for (int j = 0; j < 3; j++) {
+ image.setRGB(31 - i, y - 1 + j, v);
+ }
+ }
+
+ return image;
+ }
+
+ private static Image drawTopArrow() {
+
+ BufferedImage image = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB);
+ int v = 0xff000000;
+ int x = 6;
+ for (int i = 0; i < 6; i++) {
+ for (int j = 0; j < 2 * i + 1; j++) {
+ image.setRGB(x - i + j, i, v);
+ }
+ }
+ for (int i = 6; i < 12; i++) {
+ for (int j = 0; j < 3; j++) {
+ image.setRGB(x - 1 + j, i, v);
+ }
+ }
+ return image;
+ }
+
+ private static Image drawBottomArrow() {
+
+ BufferedImage image = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB);
+ int v = 0xff000000;
+ int x = 6;
+ for (int i = 0; i < 6; i++) {
+ for (int j = 0; j < 2 * i + 1; j++) {
+ image.setRGB(x - i + j, 31 - i, v);
+ }
+ }
+ for (int i = 6; i < 12; i++) {
+ for (int j = 0; j < 3; j++) {
+ image.setRGB(x - 1 + j, 31 - i, v);
+ }
+ }
+ return image;
+ }
+
+ private static Image drawStack() {
+
+ BufferedImage image = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB);
+ int v = 0xff000000;
+ for (int i = 0; i < 3; i++) {
+ int x = i * 3;
+ int y = 6 - i * 3;
+ for (int j = 0; j < 10; j++) {
+ image.setRGB(x, y + j, v);
+ image.setRGB(x + 10, y + j, v);
+ image.setRGB(x + j, y, v);
+ image.setRGB(x + j, y + 10, v);
+ }
+ }
+
+ return image;
+ }
+
+ private static Image drawNewWindow() {
+
+ BufferedImage image = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB);
+ int v = 0xff000000;
+ for (int i = 0; i < 5; i++) {
+ for (int j = 0; j < 14; j++) {
+ image.setRGB(j, i, 0xff0000ff);
+ }
+ }
+ for (int i = 0; i < 14; i++) {
+ image.setRGB(i, 0, v);
+ image.setRGB(i, 10, v);
+ }
+ for (int i = 0; i < 10; i++) {
+ image.setRGB(0, i, v);
+ image.setRGB(14, i, v);
+ }
+ return image;
+ }
+}
diff --git a/Ghidra/Framework/Docking/src/main/java/docking/ShowAllComponentsAction.java b/Ghidra/Framework/Docking/src/main/java/docking/ShowAllComponentsAction.java
index 9d60c0ba54..5786869d0d 100644
--- a/Ghidra/Framework/Docking/src/main/java/docking/ShowAllComponentsAction.java
+++ b/Ghidra/Framework/Docking/src/main/java/docking/ShowAllComponentsAction.java
@@ -44,7 +44,7 @@ public class ShowAllComponentsAction extends ShowComponentAction {
public void actionPerformed(ActionContext context) {
boolean focusMe = true;
for (ComponentPlaceholder info : infoList) {
- winMgr.showComponent(info, true, focusMe);
+ winMgr.showComponent(info, true, focusMe, true);
focusMe = false;
}
}
diff --git a/Ghidra/Framework/Docking/src/main/java/docking/ShowComponentAction.java b/Ghidra/Framework/Docking/src/main/java/docking/ShowComponentAction.java
index 569a19691a..70cb070cb6 100644
--- a/Ghidra/Framework/Docking/src/main/java/docking/ShowComponentAction.java
+++ b/Ghidra/Framework/Docking/src/main/java/docking/ShowComponentAction.java
@@ -118,7 +118,7 @@ class ShowComponentAction extends DockingAction implements Comparable> 1;
- g.setColor(getForeground());
- g.drawLine(0, center, s.width, center);
+ Dimension d = getSize();
+ // some edge padding, for classiness
+ int pad = 10;
+ int center = separatorHeight >> 1;
+ int x = 0 + pad;
+ int y = center;
+ int w = d.width - pad;
+ g.setColor(getForeground());
+ g.drawLine(x, y, w, y);
+
+ // drop-shadow
g.setColor(getBackground());
- g.drawLine(0, (center + 1), s.width, (center + 1));
+ g.drawLine(x, (y + 1), w, (y + 1));
// now add our custom text
renderer.setSize(getSize());
diff --git a/Ghidra/Framework/Docking/src/main/java/docking/menu/ToolBarItemManager.java b/Ghidra/Framework/Docking/src/main/java/docking/menu/ToolBarItemManager.java
index c1fd4bff3a..aea2fbfcd8 100644
--- a/Ghidra/Framework/Docking/src/main/java/docking/menu/ToolBarItemManager.java
+++ b/Ghidra/Framework/Docking/src/main/java/docking/menu/ToolBarItemManager.java
@@ -116,7 +116,7 @@ public class ToolBarItemManager implements PropertyChangeListener, ActionListene
private String getToolTipText(DockingActionIf action) {
String description = action.getDescription();
- if (description != null && description.trim().length() > 0) {
+ if (!StringUtils.isEmpty(description)) {
return description;
}
return action.getName();
diff --git a/Ghidra/Framework/Docking/src/main/java/docking/util/AnimationUtils.java b/Ghidra/Framework/Docking/src/main/java/docking/util/AnimationUtils.java
index 678fca57b6..efc2bca288 100644
--- a/Ghidra/Framework/Docking/src/main/java/docking/util/AnimationUtils.java
+++ b/Ghidra/Framework/Docking/src/main/java/docking/util/AnimationUtils.java
@@ -30,6 +30,7 @@ import ghidra.util.Msg;
import ghidra.util.bean.GGlassPane;
import ghidra.util.bean.GGlassPanePainter;
import ghidra.util.exception.AssertException;
+import resources.ResourceManager;
public class AnimationUtils {
@@ -216,6 +217,21 @@ public class AnimationUtils {
return pulser.animator;
}
+ public static Animator showTheDragonOverComponent(Component component) {
+ if (!animationEnabled) {
+ return null;
+ }
+
+ GGlassPane glassPane = getGlassPane(component);
+ if (glassPane == null) {
+ // could happen if the given component has not yet been realized
+ return null;
+ }
+
+ DragonImageDriver pulser = new DragonImageDriver(component);
+ return pulser.animator;
+ }
+
public static Animator executeSwingAnimationCallback(SwingAnimationCallback callback) {
// note: instead of checking for 'animationEnabled' here, it will happen in the driver
// so that the we can call SwingAnimationCallback.done(), which will let the client
@@ -225,11 +241,7 @@ public class AnimationUtils {
return driver.animator;
}
-//==================================================================================================
-// Private Methods
-//==================================================================================================
-
- private static GGlassPane getGlassPane(Component component) {
+ public static GGlassPane getGlassPane(Component component) {
// TODO: validate component has been realized? ...check for window, but that would
// then put the onus on the client
@@ -491,10 +503,6 @@ public class AnimationUtils {
new Point((int) startBounds.getCenterX(), (int) startBounds.getCenterY());
return SwingUtilities.convertPoint(component.getParent(), relativeStartCenter,
glassPane);
-
-// TODO do we need this?
-// Rectangle glassPaneBounds = glassPane.getBounds();
-// return new Point((int) glassPaneBounds.getCenterX(), (int) glassPaneBounds.getCenterY());
}
// note: must be public--it is a callback from the animator (also, its name must
@@ -1143,4 +1151,104 @@ public class AnimationUtils {
}
}
+ // Draws the system dragon icon over the given component
+ public static class DragonImageDriver {
+
+ private Animator animator;
+ private GGlassPane glassPane;
+ private DragonImagePainter rotatePainter;
+
+ DragonImageDriver(Component component) {
+
+ glassPane = AnimationUtils.getGlassPane(component);
+ rotatePainter = new DragonImagePainter(component);
+
+ double start = 0;
+ double max = 1;
+ int duration = 1500;
+ animator =
+ PropertySetter.createAnimator(duration, this, "percentComplete", start, max, start);
+
+ animator.setAcceleration(0.2f);
+ animator.setDeceleration(0.8f);
+
+ animator.addTarget(new TimingTargetAdapter() {
+ @Override
+ public void end() {
+ done();
+ }
+ });
+
+ glassPane.addPainter(rotatePainter);
+
+ animator.start();
+ }
+
+ public void setPercentComplete(double percentComplete) {
+ rotatePainter.setPercentComplete(percentComplete);
+ glassPane.repaint();
+ }
+
+ void done() {
+ glassPane.repaint();
+ glassPane.removePainter(rotatePainter);
+ }
+ }
+
+ private static class DragonImagePainter implements GGlassPanePainter {
+
+ private Component component;
+ private double percentComplete = 0.0;
+
+ DragonImagePainter(Component component) {
+ this.component = component;
+ }
+
+ void setPercentComplete(double percent) {
+ percentComplete = percent;
+ }
+
+ @Override
+ public void paint(GGlassPane glassPane, Graphics g) {
+
+ Graphics2D g2d = (Graphics2D) g;
+ g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+ RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+
+ float alpha = (float) percentComplete;
+ alpha = Math.min(alpha, .5f);
+ Composite originaComposite = g2d.getComposite();
+ AlphaComposite alphaComposite =
+ AlphaComposite.getInstance(AlphaComposite.SrcOver.getRule(), alpha);
+ g2d.setComposite(alphaComposite);
+
+ ImageIcon ghidra = ResourceManager.loadImage("images/GhidraIcon256.png");
+ Image ghidraImage = ghidra.getImage();
+
+ Rectangle fullBounds = component.getBounds();
+ fullBounds =
+ SwingUtilities.convertRectangle(component.getParent(), fullBounds, glassPane);
+
+ int gw = ghidraImage.getWidth(null);
+ int gh = ghidraImage.getHeight(null);
+ double smallest =
+ fullBounds.width > fullBounds.height ? fullBounds.height : fullBounds.width;
+ smallest -= 10; // padding
+
+ double scale = smallest / gw;
+ int w = (int) (gw * scale);
+ int h = (int) (gh * scale);
+
+ double cx = fullBounds.getCenterX();
+ double cy = fullBounds.getCenterY();
+ int offsetX = (int) (cx - (w >> 1));
+ int offsetY = (int) (cy - (h >> 1));
+
+ g2d.setClip(fullBounds);
+ g2d.drawImage(ghidraImage, offsetX, offsetY, w, h, null);
+
+ g2d.setComposite(originaComposite);
+ }
+ }
+
}
diff --git a/Ghidra/Framework/Graph/src/test/java/ghidra/graph/GraphAlgorithmsTest.java b/Ghidra/Framework/Graph/src/test/java/ghidra/graph/GraphAlgorithmsTest.java
index d32a0c99ff..f553fb6708 100644
--- a/Ghidra/Framework/Graph/src/test/java/ghidra/graph/GraphAlgorithmsTest.java
+++ b/Ghidra/Framework/Graph/src/test/java/ghidra/graph/GraphAlgorithmsTest.java
@@ -15,8 +15,10 @@
*/
package ghidra.graph;
-import static org.hamcrest.CoreMatchers.*;
-import static org.hamcrest.Matchers.isOneOf;
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.CoreMatchers.hasItems;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.util.*;
From e512cebc91ec979b6ce82f563952a106fafad909 Mon Sep 17 00:00:00 2001
From: dragonmacher <48328597+dragonmacher@users.noreply.github.com>
Date: Thu, 18 Jul 2019 18:57:08 -0400
Subject: [PATCH 2/2] GT-2973 - Navigation - review fixes
---
.../src/main/java/docking/DockableHeader.java | 16 +----
.../java/docking/util/AnimationUtils.java | 71 ++++---------------
.../java/generic/util/image/ImageUtils.java | 20 ++++++
3 files changed, 37 insertions(+), 70 deletions(-)
diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DockableHeader.java b/Ghidra/Framework/Docking/src/main/java/docking/DockableHeader.java
index cdd4d28ef5..335d7abb5f 100644
--- a/Ghidra/Framework/Docking/src/main/java/docking/DockableHeader.java
+++ b/Ghidra/Framework/Docking/src/main/java/docking/DockableHeader.java
@@ -19,7 +19,6 @@ import java.awt.*;
import java.awt.dnd.*;
import java.awt.event.InputEvent;
import java.awt.event.MouseListener;
-import java.awt.image.BufferedImage;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;
@@ -35,6 +34,7 @@ import docking.help.Help;
import docking.help.HelpService;
import docking.util.AnimationUtils;
import generic.util.WindowUtilities;
+import generic.util.image.ImageUtils;
import ghidra.framework.OperatingSystem;
import ghidra.framework.Platform;
import ghidra.util.HelpLocation;
@@ -494,7 +494,7 @@ public class DockableHeader extends GenericHeader
EmphasizeDockableComponentPainter(Component component, Set otherComponents) {
this.component = component;
- this.image = paintImage(component);
+ this.image = ImageUtils.createImage(component);
for (Component otherComponent : otherComponents) {
ComponentPaintInfo info = new ComponentPaintInfo(otherComponent);
@@ -509,7 +509,7 @@ public class DockableHeader extends GenericHeader
ComponentPaintInfo(Component component) {
this.myComponent = component;
- this.myImage = paintImage(component);
+ this.myImage = ImageUtils.createImage(component);
}
Image getImage() {
@@ -628,16 +628,6 @@ public class DockableHeader extends GenericHeader
g2d.drawImage(info.getImage(), offsetX, offsetY, w, h, null);
}
}
-
- private Image paintImage(Component c) {
- Rectangle bounds = c.getBounds();
- BufferedImage bufferedImage =
- new BufferedImage(bounds.width, bounds.height, BufferedImage.TYPE_INT_ARGB);
- Graphics g = bufferedImage.getGraphics();
- c.paint(g);
- g.dispose();
- return bufferedImage;
- }
}
}
diff --git a/Ghidra/Framework/Docking/src/main/java/docking/util/AnimationUtils.java b/Ghidra/Framework/Docking/src/main/java/docking/util/AnimationUtils.java
index efc2bca288..80cf115ca2 100644
--- a/Ghidra/Framework/Docking/src/main/java/docking/util/AnimationUtils.java
+++ b/Ghidra/Framework/Docking/src/main/java/docking/util/AnimationUtils.java
@@ -26,6 +26,7 @@ import org.jdesktop.animation.timing.TimingTargetAdapter;
import org.jdesktop.animation.timing.interpolation.PropertySetter;
import generic.util.WindowUtilities;
+import generic.util.image.ImageUtils;
import ghidra.util.Msg;
import ghidra.util.bean.GGlassPane;
import ghidra.util.bean.GGlassPanePainter;
@@ -241,12 +242,18 @@ public class AnimationUtils {
return driver.animator;
}
- public static GGlassPane getGlassPane(Component component) {
+ /**
+ * Returns the {@link GGlassPane} for the given component
+ *
+ * @param c the component
+ * @return the glass pane
+ */
+ public static GGlassPane getGlassPane(Component c) {
// TODO: validate component has been realized? ...check for window, but that would
// then put the onus on the client
- Window window = WindowUtilities.windowForComponent(component);
+ Window window = WindowUtilities.windowForComponent(c);
if (window instanceof JFrame) {
JFrame frame = (JFrame) window;
Component glass = frame.getGlassPane();
@@ -376,7 +383,7 @@ public class AnimationUtils {
FocusPainter(Component component, double max) {
this.component = component;
this.max = max;
- image = paintImage();
+ image = ImageUtils.createImage(component);
}
void setPercentComplete(double percent) {
@@ -446,16 +453,6 @@ public class AnimationUtils {
g.drawImage(image, x, y, width, height, null);
}
-
- private Image paintImage() {
- Rectangle bounds = component.getBounds();
- BufferedImage bufferedImage =
- new BufferedImage(bounds.width, bounds.height, BufferedImage.TYPE_INT_ARGB);
- Graphics g = bufferedImage.getGraphics();
- component.paint(g);
- g.dispose();
- return bufferedImage;
- }
}
public static class PointToComponentDriver {
@@ -530,7 +527,7 @@ public class AnimationUtils {
PointToComponentPainter(Point startPoint, Component component) {
this.startPoint = startPoint;
this.component = component;
- image = paintImage();
+ image = ImageUtils.createImage(component);
}
void setPercentComplete(double percent) {
@@ -565,16 +562,6 @@ public class AnimationUtils {
//
g2d.drawImage(image, (int) currentX, (int) currentY, scaledWidth, scaledHeight, null);
}
-
- private Image paintImage() {
- Rectangle bounds = component.getBounds();
- BufferedImage bufferedImage =
- new BufferedImage(bounds.width, bounds.height, BufferedImage.TYPE_INT_ARGB);
- Graphics g = bufferedImage.getGraphics();
- component.paint(g);
- g.dispose();
- return bufferedImage;
- }
}
// note: must be public due to reflection used by the timing framework
@@ -806,7 +793,7 @@ public class AnimationUtils {
RotatePainter(Component component) {
this.component = component;
- image = paintImage();
+ image = ImageUtils.createImage(component);
}
void setPercentComplete(double percent) {
@@ -881,16 +868,6 @@ public class AnimationUtils {
g.drawRect(offsetX, offsetY, iw, ih);
g.drawImage(image, offsetX, offsetY, iw, ih, null);
}
-
- private Image paintImage() {
- Rectangle bounds = component.getBounds();
- BufferedImage bufferedImage =
- new BufferedImage(bounds.width, bounds.height, BufferedImage.TYPE_INT_ARGB);
- Graphics g = bufferedImage.getGraphics();
- component.paint(g);
- g.dispose();
- return bufferedImage;
- }
}
public static class ShakeDriver {
@@ -1001,7 +978,7 @@ public class AnimationUtils {
PulsePainter(Component component) {
this.component = component;
- image = paintImage();
+ image = ImageUtils.createImage(component);
}
void setEmphasis(double emphasis) {
@@ -1032,16 +1009,6 @@ public class AnimationUtils {
g.drawImage(image, offsetX, offsetY, width, height, null);
}
-
- private Image paintImage() {
- Rectangle bounds = component.getBounds();
- BufferedImage bufferedImage =
- new BufferedImage(bounds.width, bounds.height, BufferedImage.TYPE_INT_ARGB);
- Graphics g = bufferedImage.getGraphics();
- component.paint(g);
- g.dispose();
- return bufferedImage;
- }
}
private static class ShakePainter implements GGlassPanePainter {
@@ -1051,7 +1018,7 @@ public class AnimationUtils {
ShakePainter(Component component) {
this.component = component;
- image = paintImage();
+ image = ImageUtils.createImage(component);
}
@Override
@@ -1091,16 +1058,6 @@ public class AnimationUtils {
g2d.rotate(lastDirection, emphasizedBounds.getCenterX(), emphasizedBounds.getCenterY());
g.drawImage(image, offsetX, offsetY, width, height, null);
}
-
- private Image paintImage() {
- Rectangle bounds = component.getBounds();
- BufferedImage bufferedImage =
- new BufferedImage(bounds.width, bounds.height, BufferedImage.TYPE_INT_ARGB);
- Graphics g = bufferedImage.getGraphics();
- component.paint(g);
- g.dispose();
- return bufferedImage;
- }
}
private static class PulseAndShakePainter extends PulsePainter {
diff --git a/Ghidra/Framework/Generic/src/main/java/generic/util/image/ImageUtils.java b/Ghidra/Framework/Generic/src/main/java/generic/util/image/ImageUtils.java
index 3593cd28b5..3994a912dd 100644
--- a/Ghidra/Framework/Generic/src/main/java/generic/util/image/ImageUtils.java
+++ b/Ghidra/Framework/Generic/src/main/java/generic/util/image/ImageUtils.java
@@ -41,6 +41,26 @@ public class ImageUtils {
// no
}
+ /**
+ * Creates an image of the given component
+ *
+ * @param c the component
+ * @return the image
+ */
+ public static Image createImage(Component c) {
+
+ // prevent this from being called when the user has made the window too small to work
+ Rectangle bounds = c.getBounds();
+ int w = Math.max(bounds.width, 1);
+ int h = Math.max(bounds.height, 1);
+
+ BufferedImage bufferedImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
+ Graphics g = bufferedImage.getGraphics();
+ c.paint(g);
+ g.dispose();
+ return bufferedImage;
+ }
+
/**
* Pads the given image with space in the amount given.
*