diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/OperandFieldHelper.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/OperandFieldHelper.java index c1bfcc69bf..aadbdb2721 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/OperandFieldHelper.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/OperandFieldHelper.java @@ -608,6 +608,7 @@ abstract class OperandFieldHelper extends FieldFactory { } private ColorStyleAttributes getOpAttributes(Object opObject, Instruction inst, int opIndex) { + if (opObject instanceof String) { return getOpAttributes(inst, opIndex, inst.getProgram()); } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/ComponentProvider.java b/Ghidra/Framework/Docking/src/main/java/docking/ComponentProvider.java index 2a35e40dcb..6ac7c08922 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/ComponentProvider.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/ComponentProvider.java @@ -1076,6 +1076,10 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext private class ShowProviderAction extends DockingAction { + /** Number of milliseconds to track user requests */ + private static final int TIME_WINDOW = 2000; + private SortedSet clickTimes = new TreeSet<>(); + ShowProviderAction(boolean supportsKeyBindings) { super(name, owner, supportsKeyBindings ? KeyBindingType.SHARED : KeyBindingType.UNSUPPORTED); @@ -1100,19 +1104,37 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext @Override public void actionPerformed(ActionContext context) { - if (isShowing()) { + boolean isFrustrated = isFrustrated(); + boolean isFocused = isFocused(); + if (isFocused && !isFrustrated) { + // the user has decided to hide this component and is not madly clicking setVisible(false); return; } - DockingWindowManager myDwm = DockingWindowManager.getInstance(getComponent()); - if (myDwm == null) { - // this can happen when the tool loses focus - dockingTool.showComponentProvider(ComponentProvider.this, true); - return; - } + boolean emphasize = getComponent().isShowing() && isFrustrated; + Tool tool = getTool(); + DockingWindowManager myDwm = tool.getWindowManager(); + myDwm.showComponent(ComponentProvider.this, true, emphasize); + } - myDwm.showComponent(ComponentProvider.this, true, true); + private boolean isFrustrated() { + long time = System.currentTimeMillis(); + clickTimes.add(time); + + // grab all click times within the time window + long secondsAgo = time - TIME_WINDOW; + SortedSet recentClicks = clickTimes.tailSet(secondsAgo); + clickTimes.retainAll(recentClicks); // drop old click times + int clickCount = recentClicks.size(); + return clickCount > 2; // rapid clicking within the time window + } + + private boolean isFocused() { + KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); + Component focusOwner = kfm.getFocusOwner(); + JComponent myComponent = getComponent(); + return focusOwner != null && SwingUtilities.isDescendingFrom(focusOwner, myComponent); } @Override diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DockableHeader.java b/Ghidra/Framework/Docking/src/main/java/docking/DockableHeader.java index d5d22b99a0..0a59093f52 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DockableHeader.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DockableHeader.java @@ -107,6 +107,15 @@ public class DockableHeader extends GenericHeader super.setSelected(hasFocus); } + @Override + public void dispose() { + if (focusAnimator != null) { + focusAnimator.stop(); + focusAnimator = null; + } + super.dispose(); + } + void installRenameAction(MouseListener listener) { titlePanel.installRenameAction(listener); } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java b/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java index 567e579576..d78ea1eb0b 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java @@ -84,7 +84,6 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder private PlaceholderManager placeholderManager; private LRUSet lastFocusedPlaceholders = new LRUSet<>(20); - private ActivatedInfo activatedInfo = new ActivatedInfo(); private ComponentPlaceholder focusedPlaceholder; private ComponentPlaceholder nextFocusedPlaceholder; private ComponentProvider defaultProvider; @@ -959,7 +958,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder placeholder.show(visibleState); if (visibleState) { - movePlaceholderToFront(placeholder, false); + movePlaceholderToFront(placeholder, shouldEmphasize); if (placeholder.getNode() == null) { root.addToNewWindow(placeholder); } @@ -978,12 +977,10 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder private void movePlaceholderToFront(ComponentPlaceholder placeholder, boolean emphasisze) { placeholder.toFront(); - - if (emphasisze) { - activatedInfo.activated(placeholder); - } - toFront(root.getWindow(placeholder)); + if (emphasisze) { + placeholder.emphasize(); + } } /** @@ -1448,10 +1445,6 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder focusedPlaceholder.setSelected(false); } - // Activating placeholders is done to help users find widgets hiding in plain sight. - // Assume that the user is no longer seeking a provider if they are clicking around. - activatedInfo.clear(); - focusedPlaceholder = placeholder; // put the last focused placeholder at the front of the list for restoring focus work later @@ -2518,39 +2511,4 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder }); } - -//================================================================================================== -// Inner Classes -//================================================================================================== - - /** - * A class that tracks placeholders that are activated (brought to the front). If a placeholder - * is activated too frequently, this class will emphasize that window, under the assumption that - * the user doesn't see the window. - */ - private class ActivatedInfo { - - private long lastCalledTimestamp; - 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; - if (elapsedTime < 3000) { // somewhat arbitrary time window - placeholder.emphasize(); - } - } - else { - this.lastActivatedPlaceholder = placeholder; - } - lastCalledTimestamp = System.currentTimeMillis(); - } - - void clear() { - lastActivatedPlaceholder = null; - lastCalledTimestamp = 0; - } - } - }