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. *