mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-10 13:28:42 +08:00
Merge remote-tracking branch
'origin/GP-6336-dragonmacher-filters-hide-action--SQUASHED' (#8771)
This commit is contained in:
@@ -63,10 +63,10 @@
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
||||
<H3><A name="Toggle_Filter"></A>Toggle Filter</H3>
|
||||
<H3><A name="Hide_Filter"></A>Hide Filter</H3>
|
||||
<BLOCKQUOTE>
|
||||
<P>
|
||||
The <B>Toggle Filter</B> action will hide and show the filter field of the tree or table.
|
||||
The <B>Hide Filter</B> action will hide the filter field of the tree or table.
|
||||
To use this action you must first assign it a key binding from the tool options.
|
||||
</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
+19
@@ -24,6 +24,7 @@ import java.awt.event.KeyListener;
|
||||
import javax.swing.*;
|
||||
import javax.swing.text.JTextComponent;
|
||||
|
||||
import docking.action.*;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
import docking.menu.keys.MenuKeyProcessor;
|
||||
import ghidra.util.bean.GGlassPane;
|
||||
@@ -36,6 +37,24 @@ import ghidra.util.exception.AssertException;
|
||||
* <p>
|
||||
* {@link #install()} must be called in order to install this <code>Singleton</code> into Java's
|
||||
* key event processing system.
|
||||
* <P>
|
||||
* Keybindings are processed here to manage how {@link DockingAction}s will get executed. The basic
|
||||
* action processing flow is:
|
||||
* <OL>
|
||||
* <LI>System actions (e.g., F1 for help)</LI>
|
||||
* <LI>Java text components</LI>
|
||||
* <LI>Java widget key listeners</LI>
|
||||
* <LI>Java action map bindings</LI>
|
||||
* <LI>{@link ComponentBasedDockingAction}s</LI>
|
||||
* <LI>{@link ComponentProvider} local actions</LI>
|
||||
* <LI>Tool global actions</LI>
|
||||
* </OL>
|
||||
* When a key event is processed, if that event has a binding at one of these levels, then that
|
||||
* binding will be processed, either by our framework or the default Java processing framework.
|
||||
* Our framework allows for multiple actions to share a key bindings. When that happens, the
|
||||
* {@link MultipleKeyAction} class is responsible for determining the correct priority for the
|
||||
* action to be processed. If there is more than one action that maps to a binding, then the user
|
||||
* will be shown a dialog to choose which action to execute.
|
||||
*/
|
||||
public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher {
|
||||
|
||||
|
||||
@@ -29,7 +29,11 @@ import generic.util.WindowUtilities;
|
||||
import ghidra.util.Swing;
|
||||
|
||||
/**
|
||||
* Action that manages multiple {@link DockingAction}s mapped to a given key binding
|
||||
* Action that manages multiple {@link DockingAction}s mapped to a given key binding.
|
||||
* <P>
|
||||
* Actions are ordered in a way that mimics how the {@link KeyBindingOverrideKeyEventDispatcher}
|
||||
* orders its event processing, lowest level components get precedence over higher-level actions,
|
||||
* such as global actions. See the javadoc of the dispatcher for more info.
|
||||
*/
|
||||
public class MultipleKeyAction extends DockingKeyBindingAction {
|
||||
private List<ActionData> actions = new ArrayList<>();
|
||||
@@ -134,19 +138,24 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
|
||||
|
||||
MultiExecutableAction multiAction = new MultiExecutableAction();
|
||||
|
||||
//
|
||||
// 1) Prefer local actions for the active provider
|
||||
//
|
||||
getLocalContextActions(localContext, multiAction);
|
||||
if (multiAction.isValid()) {
|
||||
// At this point, we have local docking actions that may or may not be enabled. Exit
|
||||
// so that any component specific actions or global found below will not interfere with
|
||||
// the provider's local actions
|
||||
return multiAction;
|
||||
}
|
||||
/*
|
||||
Note on action ordering: order matters, as any action found first may prevent actions
|
||||
found later from being executed, *even if the lower-level action is not enabled. Having
|
||||
disabled actions prevent higher-level actions from being executed helps us maintain
|
||||
consistent action execution behavior. The downside of this is that if the user assigns
|
||||
the same key binding to actions at 2 different levels, the lower priority action will
|
||||
never get executed. We currently have no way of supporting both actions in this
|
||||
scenario. It is currently up to the user to avoid this. The one exception we have to
|
||||
this rule is for ComponentBasedDockingActions. These actions can optionally mark
|
||||
themselves as invalid for a given context, which then allows the action processing to
|
||||
go higher up the chain of actions. This is currently done by hard-coding the action
|
||||
behavior, with no means for the user to change it. This solution seemed good enough
|
||||
for now.
|
||||
*/
|
||||
|
||||
//
|
||||
// 2) Check for actions local to the source component (e.g., GTable and GTree)
|
||||
// 1) Check for actions local to the source component (e.g., GTable and GTree). These are
|
||||
// considered lower level actions that can be processed before the component provider.
|
||||
//
|
||||
getLocalComponentActions(localContext, multiAction);
|
||||
if (multiAction.isValid()) {
|
||||
@@ -156,6 +165,17 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
|
||||
return multiAction;
|
||||
}
|
||||
|
||||
//
|
||||
// 2) Check for local actions for the active provider
|
||||
//
|
||||
getLocalContextActions(localContext, multiAction);
|
||||
if (multiAction.isValid()) {
|
||||
// At this point, we have local docking actions that may or may not be enabled. Exit
|
||||
// so that any component specific actions or global found below will not interfere with
|
||||
// the provider's local actions
|
||||
return multiAction;
|
||||
}
|
||||
|
||||
//
|
||||
// 3) Check for global actions using the current context
|
||||
//
|
||||
@@ -332,14 +352,13 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
|
||||
return multiAction;
|
||||
}
|
||||
|
||||
//
|
||||
// 1) Check for local actions
|
||||
//
|
||||
// Note: dialog key binding actions are proxy actions that get added to the tool as global
|
||||
// actions. Thus, there are no 'local' actions for the dialog.
|
||||
/*
|
||||
See the note in createNonDialogExecutableAction().
|
||||
*/
|
||||
|
||||
//
|
||||
// 2) Check for actions local to the source component (e.g., GTable and GTree)
|
||||
// 1) Check for actions local to the source component (e.g., GTable and GTree). These are
|
||||
// considered lower level actions that can be processed before the component provider.
|
||||
//
|
||||
getLocalComponentActions(context, multiAction);
|
||||
if (multiAction.isValid()) {
|
||||
@@ -349,6 +368,12 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
|
||||
return multiAction;
|
||||
}
|
||||
|
||||
//
|
||||
// 2) Check for dialog local actions
|
||||
//
|
||||
// Note: dialog key binding actions are proxy actions that get added to the tool as global
|
||||
// actions. Thus, there are no 'local' actions for the dialog.
|
||||
|
||||
//
|
||||
// 3) Check for global actions using the current context. As noted above, at the time of
|
||||
// writing, dialog actions are all registered at the global level.
|
||||
|
||||
@@ -1532,7 +1532,7 @@ public class GTable extends JTable {
|
||||
activateFilterAction.setHelpLocation(new HelpLocation("Trees", "Activate_Filter"));
|
||||
//@formatter:on
|
||||
|
||||
GTableAction toggleFilterAction = new GTableAction("Table/Tree Toggle Filter", owner) {
|
||||
GTableAction hideFilterAction = new GTableAction("Table/Tree Hide Filter", owner) {
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
@@ -1546,22 +1546,43 @@ public class GTable extends JTable {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
GTable gTable = (GTable) context.getSourceComponent();
|
||||
GTableFilterPanel<?> filterPanel = gTable.getTableFilterPanel();
|
||||
filterPanel.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidComponentContext(ActionContext context) {
|
||||
/*
|
||||
Subtle Code Alert!
|
||||
We use this method to signal that this action is only to be included in the key
|
||||
binding processing when the filter is showing. This is different than normal
|
||||
docking actions in that normal actions are always valid, just enabled/disabled.
|
||||
Returning false here prevents this action from interfering with key bindings
|
||||
further up the processing chain when the filter is not showing.
|
||||
*/
|
||||
if (!super.isValidComponentContext(context)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GTable gTable = (GTable) context.getSourceComponent();
|
||||
GTableFilterPanel<?> filterPanel = gTable.getTableFilterPanel();
|
||||
filterPanel.toggleVisibility();
|
||||
if (filterPanel == null) {
|
||||
return false;
|
||||
}
|
||||
return filterPanel.isShowing();
|
||||
}
|
||||
};
|
||||
//@formatter:off
|
||||
toggleFilterAction.setPopupMenuData(new MenuData(
|
||||
new String[] { "Toggle Filter" },
|
||||
hideFilterAction.setPopupMenuData(new MenuData(
|
||||
new String[] { "Hide Filter" },
|
||||
null /*icon*/,
|
||||
actionMenuGroup,
|
||||
NO_MNEMONIC,
|
||||
Integer.toString(subGroupIndex++)
|
||||
)
|
||||
);
|
||||
toggleFilterAction.setHelpLocation(new HelpLocation("Trees", "Toggle_Filter"));
|
||||
hideFilterAction.setHelpLocation(new HelpLocation("Trees", "Hide_Filter"));
|
||||
//@formatter:on
|
||||
|
||||
toolActions.addGlobalAction(copyAction);
|
||||
@@ -1571,7 +1592,7 @@ public class GTable extends JTable {
|
||||
toolActions.addGlobalAction(exportColumnsAction);
|
||||
toolActions.addGlobalAction(selectAllAction);
|
||||
toolActions.addGlobalAction(activateFilterAction);
|
||||
toolActions.addGlobalAction(toggleFilterAction);
|
||||
toolActions.addGlobalAction(hideFilterAction);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
|
||||
+11
-1
@@ -402,10 +402,20 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
||||
setVisible(true);
|
||||
isFilterDisplayed = true;
|
||||
}
|
||||
|
||||
requestFocus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides this filter if showing.
|
||||
*/
|
||||
public void close() {
|
||||
if (isFilterDisplayed) {
|
||||
setVisible(false);
|
||||
isFilterDisplayed = false;
|
||||
table.requestFocus();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the visibility of this filter panel, make it not visible it if showing, showing it if
|
||||
* not visible.
|
||||
|
||||
+11
@@ -198,6 +198,17 @@ public class DefaultGTreeFilterProvider implements GTreeFilterProvider {
|
||||
if (isFilterDisplayed) {
|
||||
filterPanel.requestFocus();
|
||||
}
|
||||
else {
|
||||
gTree.requestFocus();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (isFilterDisplayed) {
|
||||
doToggleVisibility();
|
||||
gTree.requestFocus();
|
||||
}
|
||||
}
|
||||
|
||||
private void doToggleVisibility() {
|
||||
|
||||
@@ -1978,22 +1978,43 @@ public class GTree extends JPanel implements BusyListener {
|
||||
)
|
||||
);
|
||||
activateFilterAction.setKeyBindingData(new KeyBindingData("Control F"));
|
||||
activateFilterAction.setHelpLocation(new HelpLocation("Trees", "Toggle_Filter"));
|
||||
|
||||
GTreeAction toggleFilterAction = new GTreeAction("Table/Tree Toggle Filter", owner) {
|
||||
activateFilterAction.setHelpLocation(new HelpLocation("Trees", "Activate_Filter"));
|
||||
//@formatter:on
|
||||
|
||||
GTreeAction hideFilterAction = new GTreeAction("Table/Tree Hide Filter", owner) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
GTree gTree = getTree(context);
|
||||
gTree.filterProvider.toggleVisibility();
|
||||
gTree.filterProvider.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidComponentContext(ActionContext context) {
|
||||
/*
|
||||
Subtle Code Alert!
|
||||
We use this method to signal that this action is only to be included in the key
|
||||
binding processing when the filter is showing. This is different than normal
|
||||
docking actions in that normal actions are always valid, just enabled/disabled.
|
||||
Returning false here prevents this action from interfering with key bindings
|
||||
further up the processing chain when the filter is not showing.
|
||||
*/
|
||||
if (!super.isValidComponentContext(context)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GTree gTree = getTree(context);
|
||||
return gTree.filterProvider.isShowing();
|
||||
}
|
||||
};
|
||||
//@formatter:on
|
||||
toggleFilterAction.setPopupMenuData(new MenuData(
|
||||
new String[] { "Toggle Filter" },
|
||||
|
||||
//@formatter:off
|
||||
hideFilterAction.setPopupMenuData(new MenuData(
|
||||
new String[] { "Hide Filter" },
|
||||
null,
|
||||
actionMenuGroup, NO_MNEMONIC,
|
||||
Integer.toString(subGroupIndex++)));
|
||||
toggleFilterAction.setHelpLocation(new HelpLocation("Trees", "Toggle_Filter"));
|
||||
hideFilterAction.setHelpLocation(new HelpLocation("Trees", "Hide_Filter"));
|
||||
//@formatter:on
|
||||
|
||||
// these actions are self-explanatory and do need help
|
||||
collapseAction.markHelpUnnecessary();
|
||||
@@ -2007,7 +2028,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||
toolActions.addGlobalAction(expandTreeAction);
|
||||
toolActions.addGlobalAction(copyFormattedAction);
|
||||
toolActions.addGlobalAction(activateFilterAction);
|
||||
toolActions.addGlobalAction(toggleFilterAction);
|
||||
toolActions.addGlobalAction(hideFilterAction);
|
||||
}
|
||||
|
||||
private static String generateFilterPreferenceKey() {
|
||||
|
||||
@@ -126,6 +126,28 @@ public interface GTreeFilterProvider {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides this filter if showing.
|
||||
*/
|
||||
public default void close() {
|
||||
JComponent c = getFilterComponent();
|
||||
if (c.isShowing()) {
|
||||
c.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the filter is showing.
|
||||
* @return true if the filter is showing.
|
||||
* @see #activate()
|
||||
* @see #toggleVisibility()
|
||||
* @see #close()
|
||||
*/
|
||||
public default boolean isShowing() {
|
||||
JComponent c = getFilterComponent();
|
||||
return c.isShowing();
|
||||
}
|
||||
|
||||
/**
|
||||
* A method for subclasses to do any optional cleanup
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user