From fabdc3739c47b4a1a7e948e1fb8fb8d371ad65c8 Mon Sep 17 00:00:00 2001 From: dragonmacher <48328597+dragonmacher@users.noreply.github.com> Date: Mon, 24 Apr 2023 19:51:52 -0400 Subject: [PATCH] GP-3254 - Added a Front End tool option to disable application-wide tooltips --- .../core/codebrowser/CodeViewerProvider.java | 2 +- .../core/hover/AbstractHoverProvider.java | 13 +++++-- .../listingpanel/ListingHoverProvider.java | 10 ++++-- .../src/main/java/docking/DockingUtils.java | 35 ++++++++++++++++--- .../java/docking/widgets/PopupWindow.java | 27 ++++++++++++-- .../ghidra/framework/main/FrontEndTool.java | 13 +++++-- 6 files changed, 85 insertions(+), 15 deletions(-) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeViewerProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeViewerProvider.java index 5f5bfd76d6..f28db45020 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeViewerProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeViewerProvider.java @@ -1095,7 +1095,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter setHover(isSelected()); } - void setHover(boolean enabled) { + private void setHover(boolean enabled) { getToolBarData().setIcon(enabled ? HOVER_ON_ICON : HOVER_OFF_ICON); setHoverEnabled(enabled); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/hover/AbstractHoverProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/hover/AbstractHoverProvider.java index ef7562bad0..8a8536a66a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/hover/AbstractHoverProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/hover/AbstractHoverProvider.java @@ -80,6 +80,10 @@ public abstract class AbstractHoverProvider implements HoverProvider { } } + public boolean isForcePopups() { + return false; // the default implementation is to be disabled if tip windows are disabled + } + private boolean hasEnabledHoverServices() { for (HoverService hoverService : hoverServices) { if (hoverService.hoverModeSelected()) { @@ -162,6 +166,10 @@ public abstract class AbstractHoverProvider implements HoverProvider { Rectangle fieldBounds) { lastField = field; + if (!enabled) { + return; + } + KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); Window activeWindow = kfm.getActiveWindow(); if (activeWindow == null) { @@ -187,9 +195,10 @@ public abstract class AbstractHoverProvider implements HoverProvider { } }); + boolean force = isForcePopups(); boolean isToolTip = comp instanceof JToolTip; if (isToolTip) { - popupWindow.showPopup(event); + popupWindow.showPopup(event, force); } else { @@ -206,7 +215,7 @@ public abstract class AbstractHoverProvider implements HoverProvider { Rectangle keepVisibleArea = new Rectangle(event.getPoint()); keepVisibleArea.grow(horizontalPad, verticalPad); - popupWindow.showOffsetPopup(event, keepVisibleArea); + popupWindow.showOffsetPopup(event, keepVisibleArea, force); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingHoverProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingHoverProvider.java index 9c5ecf8b94..09122d9618 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingHoverProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingHoverProvider.java @@ -39,6 +39,11 @@ public class ListingHoverProvider extends AbstractHoverProvider { super.removeHoverService(hoverService); } + @Override + public boolean isForcePopups() { + return true; // our enablement is controlled only by a user-level toolbar action + } + @Override protected ProgramLocation getHoverLocation(FieldLocation fieldLocation, Field field, Rectangle fieldBounds, MouseEvent event) { @@ -46,8 +51,9 @@ public class ListingHoverProvider extends AbstractHoverProvider { ProgramLocation loc = null; if (field instanceof ListingField) { ListingField listingField = (ListingField) field; - loc = listingField.getFieldFactory().getProgramLocation(fieldLocation.getRow(), - fieldLocation.getCol(), listingField); + loc = listingField.getFieldFactory() + .getProgramLocation(fieldLocation.getRow(), + fieldLocation.getCol(), listingField); } return loc; diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DockingUtils.java b/Ghidra/Framework/Docking/src/main/java/docking/DockingUtils.java index 6da2c86f27..40069ff3e3 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DockingUtils.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DockingUtils.java @@ -40,6 +40,7 @@ import docking.widgets.tree.support.GTreeRenderer; import generic.theme.GThemeDefaults.Colors.Palette; import ghidra.docking.util.LookAndFeelUtils; import ghidra.util.HTMLUtilities; +import ghidra.util.Swing; import resources.ResourceManager; /** @@ -353,14 +354,38 @@ public class DockingUtils { c.setBackground(Palette.NO_COLOR); } + /** + * Sets the application-wide Java tooltip enablement. + * @param enabled true if enabled; false prevents all Java tooltips + */ + public static void setTipWindowEnabled(boolean enabled) { + Swing.runLater(() -> ToolTipManager.sharedInstance().setEnabled(enabled)); + } + + /** + * Returns true if application-wide Java tooltips are enabled. + * @return true if application-wide Java tooltips are enabled. + */ + public static boolean isTipWindowEnabled() { + return Swing.runNow(() -> ToolTipManager.sharedInstance().isEnabled()); + } + /** Hides any open tooltip window */ public static void hideTipWindow() { - // This is a hack, since Java's manager doesn't have this method - javax.swing.ToolTipManager.sharedInstance().setEnabled(false); - javax.swing.ToolTipManager.sharedInstance().setEnabled(true); -// TODO: Ultimately, the ESCAPE key binding in the Java TTM should hide any visible tooltips. We -// need to look into why this isn't working. + Swing.runLater(() -> { + ToolTipManager ttm = ToolTipManager.sharedInstance(); + if (!ttm.isEnabled()) { + return; // nothing to do + } + + // + // This is a hack, since Java's manager doesn't have this method. Calling + // setEnabled(false) will close any open tooltips. + // + ttm.setEnabled(false); + ttm.setEnabled(true); + }); } } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/PopupWindow.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/PopupWindow.java index e81bd263d4..5abaa269c2 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/PopupWindow.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/PopupWindow.java @@ -25,6 +25,7 @@ import java.util.List; import javax.swing.*; import javax.swing.Timer; +import docking.DockingUtils; import docking.widgets.shapes.*; import generic.theme.GThemeDefaults.Colors.Palette; import generic.util.WindowUtilities; @@ -226,12 +227,32 @@ public class PopupWindow { closeTimer.setRepeats(false); } - public void showOffsetPopup(MouseEvent e, Rectangle keepVisibleSize) { - doShowPopup(e, keepVisibleSize, DEFAULT_WINDOW_PLACER); + public void showOffsetPopup(MouseEvent e, Rectangle keepVisibleSize, boolean forceShow) { + if (forceShow || DockingUtils.isTipWindowEnabled()) { + doShowPopup(e, keepVisibleSize, DEFAULT_WINDOW_PLACER); + } } + /** + * Shows this popup window unless popups are disabled as reported by + * {@link DockingUtils#isTipWindowEnabled()}. + * @param e the event + */ public void showPopup(MouseEvent e) { - doShowPopup(e, null, DEFAULT_WINDOW_PLACER); + showPopup(e, false); + } + + /** + * Shows this popup window unless popups are disabled as reported by + * {@link DockingUtils#isTipWindowEnabled()}. If {@code forceShow} is true, then the popup + * will be shown regardless of the state returned by {@link DockingUtils#isTipWindowEnabled()}. + * @param e the event + * @param forceShow true to show the popup even popups are disabled application-wide + */ + public void showPopup(MouseEvent e, boolean forceShow) { + if (forceShow || DockingUtils.isTipWindowEnabled()) { + doShowPopup(e, null, DEFAULT_WINDOW_PLACER); + } } private void doShowPopup(MouseEvent e, Rectangle keepVisibleSize, PopupWindowPlacer placer) { diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/FrontEndTool.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/FrontEndTool.java index c613a24d0a..11dd1d1a49 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/FrontEndTool.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/FrontEndTool.java @@ -90,6 +90,7 @@ public class FrontEndTool extends PluginTool implements OptionsChangeListener { public static final String DEFAULT_TOOL_LAUNCH_MODE = "Default Tool Launch Mode"; public static final String AUTOMATICALLY_SAVE_TOOLS = "Automatically Save Tools"; private static final String USE_ALERT_ANIMATION_OPTION_NAME = "Use Notification Animation"; + private static final String SHOW_TOOLTIPS_OPTION_NAME = "Show Tooltips"; // TODO: Experimental Option !! private static final String ENABLE_COMPRESSED_DATABUFFER_OUTPUT = @@ -338,11 +339,13 @@ public class FrontEndTool extends PluginTool implements OptionsChangeListener { options.registerOption(USE_ALERT_ANIMATION_OPTION_NAME, true, help, "Signals that user notifications should be animated. This makes notifications more " + "distinguishable."); - options.registerOption(ENABLE_COMPRESSED_DATABUFFER_OUTPUT, Boolean.FALSE, help, + options.registerOption(SHOW_TOOLTIPS_OPTION_NAME, true, help, + "Controls the display of tooltip popup windows."); + options.registerOption(ENABLE_COMPRESSED_DATABUFFER_OUTPUT, false, help, "When enabled data buffers sent to Ghidra Server are compressed (see server " + "configuration for other direction)"); - options.registerOption(RESTORE_PREVIOUS_PROJECT_NAME, Boolean.TRUE, help, + options.registerOption(RESTORE_PREVIOUS_PROJECT_NAME, true, help, "Restore the previous project when Ghidra starts."); defaultLaunchMode = options.getEnum(DEFAULT_TOOL_LAUNCH_MODE, defaultLaunchMode); @@ -353,6 +356,9 @@ public class FrontEndTool extends PluginTool implements OptionsChangeListener { boolean animationEnabled = options.getBoolean(USE_ALERT_ANIMATION_OPTION_NAME, true); AnimationUtils.setAnimationEnabled(animationEnabled); + boolean showToolTips = options.getBoolean(SHOW_TOOLTIPS_OPTION_NAME, true); + DockingUtils.setTipWindowEnabled(showToolTips); + boolean compressDataBuffers = options.getBoolean(ENABLE_COMPRESSED_DATABUFFER_OUTPUT, false); DataBuffer.enableCompressedSerializationOutput(compressDataBuffers); @@ -374,6 +380,9 @@ public class FrontEndTool extends PluginTool implements OptionsChangeListener { else if (USE_ALERT_ANIMATION_OPTION_NAME.equals(optionName)) { AnimationUtils.setAnimationEnabled((Boolean) newValue); } + else if (SHOW_TOOLTIPS_OPTION_NAME.equals(optionName)) { + DockingUtils.setTipWindowEnabled((Boolean) newValue); + } else if (ENABLE_COMPRESSED_DATABUFFER_OUTPUT.equals(optionName)) { DataBuffer.enableCompressedSerializationOutput((Boolean) newValue); }