GT-2973 - Navigation - fixed navigation buttons when using a snapshot in

its own window
This commit is contained in:
dragonmacher 2019-07-18 14:22:22 -04:00
parent a51ef0926d
commit 1d5f9ffd5e
16 changed files with 740 additions and 341 deletions

View file

@ -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);
}

View file

@ -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);

View file

@ -66,8 +66,7 @@ import utilities.util.reflection.ReflectionUtilities;
* <b><u>Show Provider Action</u></b> - 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 <b>Window</b> menu. You can have your provider also appear in the tool's toolbar
* by calling {@link #setIcon(Icon, boolean)}, passing <code>true</code> for
* <code>isToolbarAction</code>.
* by calling {@link #addToTool()}.
* <p>
* 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) {
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

View file

@ -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;
}

View file

@ -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<ComponentNode> componentNodes = new HashSet<>();
getComponents(windowNode, componentNodes);
//@formatter:off
Set<Component> 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<ComponentNode> results) {
List<Node> 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<Component> 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);
}
}
for (int i = 6; i < 12; i++) {
for (int j = 0; j < 3; j++) {
image.setRGB(31 - i, y - 1 + j, v);
}
private static class EmphasizeDockableComponentPainter implements GGlassPanePainter {
private Set<ComponentPaintInfo> otherComponentInfos = new HashSet<>();
private Image image;
private Component component;
private Rectangle cBounds;
private double percentComplete = 0.0;
EmphasizeDockableComponentPainter(Component component, Set<Component> otherComponents) {
this.component = component;
this.image = paintImage(component);
for (Component otherComponent : otherComponents) {
ComponentPaintInfo info = new ComponentPaintInfo(otherComponent);
otherComponentInfos.add(info);
}
}
/**
* 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);
private class ComponentPaintInfo {
private Component myComponent;
private Image myImage;
ComponentPaintInfo(Component component) {
this.myComponent = component;
this.myImage = paintImage(component);
}
Image getImage() {
return myImage;
}
for (int i = 6; i < 12; i++) {
for (int j = 0; j < 3; j++) {
image.setRGB(x - 1 + j, i, v);
}
Rectangle getRelativeBounds(Component other) {
Rectangle r = myComponent.getBounds();
return SwingUtilities.convertRectangle(myComponent.getParent(), r, other);
}
}
/**
* 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);
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;
}
for (int i = 6; i < 12; i++) {
for (int j = 0; j < 3; j++) {
image.setRGB(x - 1 + j, 31 - i, v);
}
else {
othersBounds.add(b);
}
}
/**
* 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);
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);
}
}
/**
* 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);
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;
}
}
}

View file

@ -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;

View file

@ -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;
}
}
}

View file

@ -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);

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -118,7 +118,7 @@ class ShowComponentAction extends DockingAction implements Comparable<ShowCompon
@Override
public void actionPerformed(ActionContext context) {
winMgr.showComponent(info, true, true);
winMgr.showComponent(info, true, true, true);
}
@Override

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -51,18 +50,10 @@ import docking.util.GraphicsUtils;
*/
public class DockingMenuItemUI extends MenuItemUI {
static final String MAX_TEXT_WIDTH = "maxTextWidth1";
static final String MAX_ACC_WIDTH = "maxAccWidth1";
static final String TABULATOR_PROPERTIES = "menuItemTabulator";
public static int ARROW_GAP = 4;
static final int ICON_WIDTH = 16;
static final int ICON_HEIGHT = 16;
static final int LEFT_MARGIN = 2;
static final int TOP_MARGIN = 2;
static final int RIGHT_MARGIN = 4;
static final int BOTTOM_MARGIN = 2;
static final int ACC_GAP = 5;
static final int COLUMN_PADDING = 5;
private static final String TABULATOR_PROPERTIES = "menuItemTabulator";
// make this big enough to differentiate columns
private static final int COLUMN_PADDING = 20;
protected MenuItemUI ui;
@ -127,19 +118,24 @@ public class DockingMenuItemUI extends MenuItemUI {
sg2.setDoImage(false);
Icon origIcon = c.getIcon();
int iconWidth = 0;
if (origIcon != null) {
iconWidth = origIcon.getIconWidth();
}
String origText = c.getText();
KeyStroke origAcc = c.getAccelerator();
String[] parts = origText.split("\t");
for (int i = 0; i < parts.length; i++) {
if (i == 1) {
c.setIcon(null);
c.setAccelerator(null);
}
c.setText(parts[i]);
c.setText(parts[i]);
ui.paint(sg2, c);
sg2.translate(t.columns.get(i) + COLUMN_PADDING, 0);
sg2.translate(iconWidth + t.columns.get(i) + COLUMN_PADDING, 0);
// this is only needed for the first pass
iconWidth = 0;
c.setIcon(null);
c.setAccelerator(null);
}
c.setIcon(origIcon);
@ -154,10 +150,9 @@ public class DockingMenuItemUI extends MenuItemUI {
if (text.indexOf('\t') == -1) {
return uiPref;
}
int extra = uiPref.width - textWidth(c, text);
MenuTabulator tabulator = MenuTabulator.tabulate((JMenuItem) c);
return new Dimension(tabulator.getWidth() + extra, uiPref.height);
}
@ -303,14 +298,16 @@ public class DockingMenuItemUI extends MenuItemUI {
}
@Override
public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) {
public void drawRoundRect(int x, int y, int width, int height, int arcWidth,
int arcHeight) {
if (doDraw) {
g.drawRoundRect(x, y, width, height, arcWidth, arcHeight);
}
}
@Override
public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) {
public void fillRoundRect(int x, int y, int width, int height, int arcWidth,
int arcHeight) {
if (doFill) {
g.fillRoundRect(x, y, width, height, arcWidth, arcHeight);
}

View file

@ -24,6 +24,8 @@ import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.event.*;
import org.apache.commons.lang3.StringUtils;
import docking.*;
import docking.action.*;
import docking.widgets.EmptyBorderButton;
@ -427,7 +429,7 @@ public class MultipleActionDockingToolbarButton extends EmptyBorderButton {
renderer.setHorizontalAlignment(SwingConstants.CENTER);
renderer.setVisible(true);
if (name != null && !"".equals(name)) {
if (!StringUtils.isBlank(name)) {
separatorHeight = TEXT_SEPARATOR_HEIGHT;
}
@ -437,14 +439,20 @@ public class MultipleActionDockingToolbarButton extends EmptyBorderButton {
@Override
protected void paintComponent(Graphics g) {
// assume horizontal
Dimension s = getSize();
int center = separatorHeight >> 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());

View file

@ -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();

View file

@ -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);
}
}
}

View file

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