mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-21 19:42:14 +00:00
GP-5114 - Updated component providers to close on Escape when they are the only provider in a window
This commit is contained in:
parent
7e0a96e477
commit
aaf23cf096
@ -62,6 +62,10 @@ public class AnalysisOptionsDialog extends DialogComponentProvider
|
|||||||
addCancelButton();
|
addCancelButton();
|
||||||
addApplyButton();
|
addApplyButton();
|
||||||
setOkButtonText("Analyze");
|
setOkButtonText("Analyze");
|
||||||
|
|
||||||
|
// This allows user to press Enter to launch analysis when the dialog is shown. Without
|
||||||
|
// this, the table takes focus, which consumes Enter key presses.
|
||||||
|
setFocusComponent(okButton);
|
||||||
okButton.setMnemonic('A');
|
okButton.setMnemonic('A');
|
||||||
setOkEnabled(true);
|
setOkEnabled(true);
|
||||||
setPreferredSize(1000, 600);
|
setPreferredSize(1000, 600);
|
||||||
|
@ -227,8 +227,12 @@ class ComponentNode extends Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
int getComponentCount() {
|
int getComponentCount() {
|
||||||
return windowPlaceholders.size();
|
// we may be a single component or in a tabbed pane of components
|
||||||
|
List<ComponentPlaceholder> activeComponents = new ArrayList<>();
|
||||||
|
populateActiveComponents(activeComponents);
|
||||||
|
return activeComponents.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -101,6 +101,11 @@ class DetachedWindowNode extends WindowNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
int getComponentCount() {
|
||||||
|
return child.getComponentCount();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
String getTitle() {
|
String getTitle() {
|
||||||
if (window instanceof JDialog) {
|
if (window instanceof JDialog) {
|
||||||
|
@ -27,13 +27,14 @@ import org.jdesktop.animation.timing.Animator;
|
|||||||
import org.jdesktop.animation.timing.TimingTargetAdapter;
|
import org.jdesktop.animation.timing.TimingTargetAdapter;
|
||||||
|
|
||||||
import docking.action.*;
|
import docking.action.*;
|
||||||
import docking.actions.KeyBindingUtils;
|
import docking.action.builder.ActionBuilder;
|
||||||
import docking.event.mouse.GMouseListenerAdapter;
|
import docking.event.mouse.GMouseListenerAdapter;
|
||||||
import docking.menu.DialogToolbarButton;
|
import docking.menu.DialogToolbarButton;
|
||||||
import docking.util.AnimationUtils;
|
import docking.util.AnimationUtils;
|
||||||
import docking.widgets.label.GDHtmlLabel;
|
import docking.widgets.label.GDHtmlLabel;
|
||||||
import generic.theme.GColor;
|
import generic.theme.GColor;
|
||||||
import generic.theme.GThemeDefaults.Colors.Messages;
|
import generic.theme.GThemeDefaults.Colors.Messages;
|
||||||
|
import generic.util.WindowUtilities;
|
||||||
import ghidra.util.*;
|
import ghidra.util.*;
|
||||||
import ghidra.util.exception.AssertException;
|
import ghidra.util.exception.AssertException;
|
||||||
import ghidra.util.task.*;
|
import ghidra.util.task.*;
|
||||||
@ -81,6 +82,7 @@ public class DialogComponentProvider
|
|||||||
private TaskMonitorComponent taskMonitorComponent;
|
private TaskMonitorComponent taskMonitorComponent;
|
||||||
|
|
||||||
private static final KeyStroke ESC_KEYSTROKE = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
|
private static final KeyStroke ESC_KEYSTROKE = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
|
||||||
|
private DockingAction closeAction;
|
||||||
|
|
||||||
private CardLayout progressCardLayout;
|
private CardLayout progressCardLayout;
|
||||||
private JButton defaultButton;
|
private JButton defaultButton;
|
||||||
@ -181,15 +183,31 @@ public class DialogComponentProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void installEscapeAction() {
|
private void installEscapeAction() {
|
||||||
Action escAction = new AbstractAction("ESCAPE") {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent ev) {
|
|
||||||
escapeCallback();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
KeyBindingUtils.registerAction(rootPanel, ESC_KEYSTROKE, escAction,
|
closeAction = new ActionBuilder("Close Dialog", title)
|
||||||
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
|
.sharedKeyBinding()
|
||||||
|
.keyBinding(ESC_KEYSTROKE)
|
||||||
|
.enabledWhen(this::isMyDialog)
|
||||||
|
.onAction(c -> escapeCallback())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
addAction(closeAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isMyDialog(ActionContext c) {
|
||||||
|
//
|
||||||
|
// Each dialog registers a shared action bound to Escape. If all dialog actions are
|
||||||
|
// enabled, then the user will get prompted to pick which dialog to close when pressing
|
||||||
|
// Escape. Thus, we limit the enablement of each action to be the dialog that contains the
|
||||||
|
// focused component. We use the action context to find out if this dialog is the active
|
||||||
|
// dialog.
|
||||||
|
//
|
||||||
|
Window window = WindowUtilities.windowForComponent(c.getSourceComponent());
|
||||||
|
if (!(window instanceof DockingDialog dockingDialog)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dockingDialog.containsProvider(DialogComponentProvider.this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** a callback mechanism for children to do work */
|
/** a callback mechanism for children to do work */
|
||||||
@ -197,6 +215,16 @@ public class DialogComponentProvider
|
|||||||
// may be overridden by subclasses
|
// may be overridden by subclasses
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the given keystroke is the trigger for this dialog's close action.
|
||||||
|
* @param ks the keystroke
|
||||||
|
* @return true if the given keystroke is the trigger for this dialog's close action
|
||||||
|
*/
|
||||||
|
public boolean isCloseKeyStroke(KeyStroke ks) {
|
||||||
|
KeyStroke currentCloseKs = closeAction.getKeyBinding();
|
||||||
|
return Objects.equals(ks, currentCloseKs);
|
||||||
|
}
|
||||||
|
|
||||||
public int getId() {
|
public int getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
@ -75,12 +75,15 @@ class DockableToolBarManager {
|
|||||||
ToolBarCloseAction closeAction = new ToolBarCloseAction(owner);
|
ToolBarCloseAction closeAction = new ToolBarCloseAction(owner);
|
||||||
closeButtonManager = new ToolBarItemManager(closeAction, winMgr);
|
closeButtonManager = new ToolBarItemManager(closeAction, winMgr);
|
||||||
|
|
||||||
|
CloseLastProviderAction closeLastProviderAction = new CloseLastProviderAction(owner);
|
||||||
|
|
||||||
ToolBarMenuAction dropDownAction = new ToolBarMenuAction(owner);
|
ToolBarMenuAction dropDownAction = new ToolBarMenuAction(owner);
|
||||||
menuButtonManager = new ToolBarItemManager(dropDownAction, winMgr);
|
menuButtonManager = new ToolBarItemManager(dropDownAction, winMgr);
|
||||||
|
|
||||||
// we need to add this action to the tool in order to use key bindings
|
// we need to add this action to the tool in order to use key bindings
|
||||||
Tool tool = winMgr.getTool();
|
Tool tool = winMgr.getTool();
|
||||||
tool.addLocalAction(provider, closeAction);
|
tool.addLocalAction(provider, closeAction);
|
||||||
|
tool.addLocalAction(provider, closeLastProviderAction);
|
||||||
tool.addLocalAction(provider, dropDownAction);
|
tool.addLocalAction(provider, dropDownAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,6 +219,42 @@ class DockableToolBarManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An action to close the provider on Escape if the provider is the last in the window. This
|
||||||
|
* allows users to close transient providers (like search results) easily.
|
||||||
|
*/
|
||||||
|
private class CloseLastProviderAction extends DockingAction {
|
||||||
|
|
||||||
|
CloseLastProviderAction(String owner) {
|
||||||
|
super("Close Window for Last Provider", owner, KeyBindingType.SHARED);
|
||||||
|
setKeyBindingData(new KeyBindingData("ESCAPE"));
|
||||||
|
setDescription("Close the window if this provider is the last provider in the window");
|
||||||
|
markHelpUnnecessary();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionContext context) {
|
||||||
|
ComponentPlaceholder placeholder = dockableComponent.getComponentWindowingPlaceholder();
|
||||||
|
if (placeholder != null) {
|
||||||
|
placeholder.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabledForContext(ActionContext context) {
|
||||||
|
DockingWindowManager dwm = DockingWindowManager.getActiveInstance();
|
||||||
|
ComponentProvider provider = context.getComponentProvider();
|
||||||
|
if (provider == null) {
|
||||||
|
// Some context providers do not specify the provider when creating a contexts
|
||||||
|
provider = dwm.getActiveComponentProvider();
|
||||||
|
}
|
||||||
|
if (provider != dockableComponent.getComponentProvider()) {
|
||||||
|
return false; // not my provider
|
||||||
|
}
|
||||||
|
return dwm.isLastProviderInDetachedWindow(provider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Actions added to toolbar for displaying the drop-down menu.
|
* Actions added to toolbar for displaying the drop-down menu.
|
||||||
*/
|
*/
|
||||||
|
@ -62,10 +62,9 @@ public class DockingDialog extends JDialog implements HelpDescriptor {
|
|||||||
* only happens during tests and one-off main methods that are not part of a
|
* only happens during tests and one-off main methods that are not part of a
|
||||||
* running tool.
|
* running tool.
|
||||||
*
|
*
|
||||||
* @param componentProvider the dialog content for this dialog
|
|
||||||
* @return the hidden frame
|
* @return the hidden frame
|
||||||
*/
|
*/
|
||||||
private static JFrame createHiddenParentFrame(DialogComponentProvider componentProvider) {
|
private static JFrame createHiddenParentFrame() {
|
||||||
|
|
||||||
//
|
//
|
||||||
// Note: we expect to only get here when there is no parent window found. This usually
|
// Note: we expect to only get here when there is no parent window found. This usually
|
||||||
@ -118,7 +117,7 @@ public class DockingDialog extends JDialog implements HelpDescriptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private DockingDialog(DialogComponentProvider comp, Component centeredOnComponent) {
|
private DockingDialog(DialogComponentProvider comp, Component centeredOnComponent) {
|
||||||
super(createHiddenParentFrame(comp), comp.getTitle(), comp.isModal());
|
super(createHiddenParentFrame(), comp.getTitle(), comp.isModal());
|
||||||
init(comp);
|
init(comp);
|
||||||
initializeLocationAndSize(centeredOnComponent);
|
initializeLocationAndSize(centeredOnComponent);
|
||||||
}
|
}
|
||||||
@ -299,6 +298,15 @@ public class DockingDialog extends JDialog implements HelpDescriptor {
|
|||||||
return component;
|
return component;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the given provider is the provider owned by this dialog.
|
||||||
|
* @param dcp the provider to check
|
||||||
|
* @return true if the given provider is the provider owned by this dialog
|
||||||
|
*/
|
||||||
|
public boolean containsProvider(DialogComponentProvider dcp) {
|
||||||
|
return component == dcp;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Centers the dialog on the given component.
|
* Centers the dialog on the given component.
|
||||||
* @param c the component to center over.
|
* @param c the component to center over.
|
||||||
|
@ -461,6 +461,23 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||||||
return isActiveWindowManager && isFocusedProvider;
|
return isActiveWindowManager && isFocusedProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the given provider is in a non-main window (a {@link DetachedWindowNode})
|
||||||
|
* and is the last component provider in that window.
|
||||||
|
* @param provider the provider
|
||||||
|
* @return true if the last provider in a non-main window
|
||||||
|
*/
|
||||||
|
public boolean isLastProviderInDetachedWindow(ComponentProvider provider) {
|
||||||
|
|
||||||
|
Window providerWindow = getProviderWindow(provider);
|
||||||
|
WindowNode providerNode = root.getNodeForWindow(providerWindow);
|
||||||
|
if (!(providerNode instanceof DetachedWindowNode windowNode)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return windowNode.getComponentCount() == 1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the visible state of the set of docking windows.
|
* Sets the visible state of the set of docking windows.
|
||||||
*
|
*
|
||||||
@ -944,7 +961,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||||||
if (visibleState) {
|
if (visibleState) {
|
||||||
movePlaceholderToFront(placeholder, false);
|
movePlaceholderToFront(placeholder, false);
|
||||||
if (placeholder.getNode() == null) {
|
if (placeholder.getNode() == null) {
|
||||||
root.add(placeholder);
|
root.addToNewWindow(placeholder);
|
||||||
}
|
}
|
||||||
if (requestFocus) {
|
if (requestFocus) {
|
||||||
setNextFocusPlaceholder(placeholder);
|
setNextFocusPlaceholder(placeholder);
|
||||||
@ -1142,7 +1159,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||||||
void movePlaceholder(ComponentPlaceholder source, Point p) {
|
void movePlaceholder(ComponentPlaceholder source, Point p) {
|
||||||
ComponentNode sourceNode = source.getNode();
|
ComponentNode sourceNode = source.getNode();
|
||||||
sourceNode.remove(source);
|
sourceNode.remove(source);
|
||||||
root.add(source, p);
|
root.addToNewWindow(source, p);
|
||||||
scheduleUpdate();
|
scheduleUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,7 +240,16 @@ public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher
|
|||||||
// processed with modal dialogs open. For now, do not let key bindings get processed
|
// processed with modal dialogs open. For now, do not let key bindings get processed
|
||||||
// for modal dialogs. This can be changed in the future if needed.
|
// for modal dialogs. This can be changed in the future if needed.
|
||||||
DockingDialog dialog = (DockingDialog) activeWindow;
|
DockingDialog dialog = (DockingDialog) activeWindow;
|
||||||
return !dialog.isModal();
|
if (!dialog.isModal()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow modal dialogs to process close keystrokes (e.g., ESCAPE) so they can be closed
|
||||||
|
DialogComponentProvider provider = dialog.getComponent();
|
||||||
|
if (provider.isCloseKeyStroke(keyStroke)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false; // modal dialog; non-escape key
|
||||||
}
|
}
|
||||||
return true; // default case; allow it through
|
return true; // default case; allow it through
|
||||||
}
|
}
|
||||||
@ -275,6 +284,15 @@ public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher
|
|||||||
// return false;
|
// return false;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
// Special Case: We allow Escape to go through. This doesn't seem useful to text widgets
|
||||||
|
// but does allow for closing of windows. If we find text widgets that need Escape, then
|
||||||
|
// we will have to update how we make this decision, such as by having the concerned text
|
||||||
|
// widgets register actions for Escape and then check for that action.
|
||||||
|
int code = event.getKeyCode();
|
||||||
|
if (code == KeyEvent.VK_ESCAPE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// We've made the executive decision to allow all keys to go through to the text component
|
// We've made the executive decision to allow all keys to go through to the text component
|
||||||
// unless they are modified with the 'Alt'/'Ctrl'/etc keys, unless they directly used
|
// unless they are modified with the 'Alt'/'Ctrl'/etc keys, unless they directly used
|
||||||
// by the text component
|
// by the text component
|
||||||
|
@ -52,6 +52,12 @@ abstract class Node {
|
|||||||
*/
|
*/
|
||||||
abstract List<Node> getChildren();
|
abstract List<Node> getChildren();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of visible components in this node.
|
||||||
|
* @return the number of visible components in this node.
|
||||||
|
*/
|
||||||
|
abstract int getComponentCount();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively closes all nodes.
|
* Recursively closes all nodes.
|
||||||
*/
|
*/
|
||||||
|
@ -213,19 +213,19 @@ class RootNode extends WindowNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void add(ComponentPlaceholder info) {
|
void addToNewWindow(ComponentPlaceholder placeholder) {
|
||||||
add(info, (Point) null);
|
addToNewWindow(placeholder, (Point) null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new sub-window for the given component a positions it at the given location.
|
* Creates a new sub-window for the given component a positions it at the given location.
|
||||||
*
|
*
|
||||||
* @param info the component to be put in its own window.
|
* @param placeholder the component to be put in its own window.
|
||||||
* @param loc the location for the new window.
|
* @param loc the location for the new window.
|
||||||
*/
|
*/
|
||||||
void add(ComponentPlaceholder info, Point loc) {
|
void addToNewWindow(ComponentPlaceholder placeholder, Point loc) {
|
||||||
ComponentNode node = new ComponentNode(winMgr);
|
ComponentNode node = new ComponentNode(winMgr);
|
||||||
info.setNode(node);
|
placeholder.setNode(node);
|
||||||
node.parent = this;
|
node.parent = this;
|
||||||
DetachedWindowNode windowNode =
|
DetachedWindowNode windowNode =
|
||||||
new DetachedWindowNode(winMgr, this, node, dropTargetFactory);
|
new DetachedWindowNode(winMgr, this, node, dropTargetFactory);
|
||||||
@ -233,18 +233,18 @@ class RootNode extends WindowNode {
|
|||||||
windowNode.setInitialLocation(loc.x, loc.y);
|
windowNode.setInitialLocation(loc.x, loc.y);
|
||||||
}
|
}
|
||||||
detachedWindows.add(windowNode);
|
detachedWindows.add(windowNode);
|
||||||
info.getNode().add(info);
|
placeholder.getNode().add(placeholder);
|
||||||
info.requestFocusWhenReady();
|
placeholder.requestFocusWhenReady();
|
||||||
notifyWindowAdded(windowNode);
|
notifyWindowAdded(windowNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
void add(ComponentPlaceholder info, WindowPosition initialPosition) {
|
void add(ComponentPlaceholder placeholder, WindowPosition initialPosition) {
|
||||||
if (initialPosition == WindowPosition.WINDOW) {
|
if (initialPosition == WindowPosition.WINDOW) {
|
||||||
add(info);
|
addToNewWindow(placeholder);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ComponentNode node = new ComponentNode(winMgr);
|
ComponentNode node = new ComponentNode(winMgr);
|
||||||
info.setNode(node);
|
placeholder.setNode(node);
|
||||||
if (child == null) {
|
if (child == null) {
|
||||||
node.parent = this;
|
node.parent = this;
|
||||||
child = node;
|
child = node;
|
||||||
@ -266,7 +266,7 @@ class RootNode extends WindowNode {
|
|||||||
}
|
}
|
||||||
child.parent = this;
|
child.parent = this;
|
||||||
}
|
}
|
||||||
info.getNode().add(info);
|
placeholder.getNode().add(placeholder);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -635,6 +635,11 @@ class RootNode extends WindowNode {
|
|||||||
return windowWrapper.getWindow();
|
return windowWrapper.getWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
int getComponentCount() {
|
||||||
|
return child.getComponentCount();
|
||||||
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Inner Classes
|
// Inner Classes
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -205,6 +205,18 @@ class SplitNode extends Node {
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
int getComponentCount() {
|
||||||
|
int n = 0;
|
||||||
|
if (child1 != null) {
|
||||||
|
n += child1.getComponentCount();
|
||||||
|
}
|
||||||
|
if (child2 != null) {
|
||||||
|
n += child2.getComponentCount();
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return printTree();
|
return printTree();
|
||||||
|
Loading…
Reference in New Issue
Block a user