mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
Merge remote-tracking branch
'origin/GP-1081-dragonmacher-menu-mnemonics--SQUASHED' (Closes #2811)
This commit is contained in:
commit
f66bad1a9c
18 changed files with 690 additions and 110 deletions
|
@ -159,9 +159,6 @@
|
|||
<tocdef id="Control the View" sortgroup="b" text="Control the View" target="help/topics/ProgramTreePlugin/program_tree.htm#ViewControl" />
|
||||
<tocdef id="Cut/Copy/Paste and Drag and Drop" sortgroup="c" text="Cut/Copy/Paste and Drag and Drop" target="help/topics/ProgramTreePlugin/program_tree.htm#CCPandDnD" />
|
||||
<tocdef id="Program Organizations" sortgroup="d" text="Program Organizations" target="help/topics/ProgramTreePlugin/Program_Organizations.htm" />
|
||||
</tocdef>
|
||||
|
||||
<tocdef id="Program Tree Manager" text="Program Tree Manager" target="help/topics/ProgramTreePlugin/view_manager.htm" >
|
||||
<tocdef id="Create view" sortgroup="a" text="Create view" target="help/topics/ProgramTreePlugin/view_manager.htm#Create_Default_Tree_View" />
|
||||
<tocdef id="Delete view" sortgroup="b" text="Delete view" target="help/topics/ProgramTreePlugin/view_manager.htm#Delete_Tree_View" />
|
||||
<tocdef id="Rename view" sortgroup="c" text="Rename view" target="help/topics/ProgramTreePlugin/view_manager.htm#Rename_Tree_View" />
|
||||
|
|
|
@ -259,11 +259,9 @@
|
|||
<P align="center"><IMG alt="" src="images/Err_Dialog.png" border="0"></P>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<BLOCKQUOTE>
|
||||
<P align="left">The <B>Details</B> >>> button expands the dialog to show the
|
||||
details of the java stack trace. (The stack trace is also output to the console.)</P>
|
||||
</BLOCKQUOTE><BR>
|
||||
|
||||
</BLOCKQUOTE>
|
||||
</BLOCKQUOTE><BR>
|
||||
|
||||
</BODY>
|
||||
</HTML>
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
"4">Subroutine</FONT></B><IMG border="0" src="../../shared/arrow.gif"><FONT size=
|
||||
"4"><B><I><block model name>.</I></B></FONT></P>
|
||||
|
||||
<P>Provided By:<I>ModularizeAlgorithmPlugin</I></P>
|
||||
<P class="providedbyplugin">Provided by: <I>ModularizeAlgorithmPlugin</I></P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
||||
|
@ -69,7 +69,7 @@
|
|||
<P>
|
||||
This action modularizes the program tree by creating a call tree of the
|
||||
code blocks and then arranges that tree by dominance such that all blocks only
|
||||
reachable from a parent <tt>p</tt> are children of <tt>p</tt> in the program tree.
|
||||
reachable from a parent <tt>'p'</tt> are children of <tt>'p'</tt> in the program tree.
|
||||
</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
|
|
@ -32,6 +32,17 @@
|
|||
on a <A href="help/topics/BlockModel/Block_Model.htm">Block Model</A>. The following paragraphs
|
||||
describe features of the Program Tree.</P>
|
||||
|
||||
<CENTER>
|
||||
<TABLE border="0" width="100%">
|
||||
<TBODY>
|
||||
<TR>
|
||||
<TD align="center" width="100%"><IMG src="images/ViewManager.png" border="0"></TD>
|
||||
</TR>
|
||||
</TBODY>
|
||||
</TABLE>
|
||||
</CENTER>
|
||||
|
||||
|
||||
<H2><A name="FoldersAndFragments"></A>Folders and Fragments</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<META name="generator" content=
|
||||
"HTML Tidy for Java (vers. 2009-12-01), see jtidy.sourceforge.net">
|
||||
|
||||
<TITLE>View Manager</TITLE>
|
||||
<TITLE>Program Tree View Management</TITLE>
|
||||
<META http-equiv="content-type" content="text/html; charset=windows-1252">
|
||||
<LINK rel="stylesheet" type="text/css" href="../../shared/Frontpage.css">
|
||||
<META name="generator" content="Microsoft FrontPage 4.0">
|
||||
|
@ -13,7 +13,7 @@
|
|||
|
||||
<BODY>
|
||||
<H1><A name="ProgramTreePlugin"></A><A name="Program_Tree"></A>
|
||||
Program Tree Plugin
|
||||
Program Tree View Management
|
||||
</H1>
|
||||
|
||||
<P>Program Trees are used to organize programs into a tree structure. Nodes within the a
|
||||
|
|
|
@ -56,36 +56,45 @@
|
|||
<H2><A name="KeyBindings_Option"></A><B>Key Bindings</B></H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>You can create a new "hot key" for a Plugin's action or modify the default key binding.
|
||||
The hot key (or accelerator) that you add can be used to execute the action with a keystroke
|
||||
combination.</P>
|
||||
<P>You can create a new key binding (<I>accelerator key</I>) for an action or modify the
|
||||
default key binding. The key binding that you add can be used to execute the action
|
||||
using the keyboard. Below we describe the <B>Key Bindings</B> options editor.</P>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P><IMG alt="" border="0" src="../../shared/note.png"> You can also change key bindings
|
||||
<P><IMG alt="" border="0" src="../../shared/tip.png">Not all key bindings are changeable
|
||||
via the tool options. For example, the following keys cannot be changed:
|
||||
<UL>
|
||||
<LI>
|
||||
<TT><B>F1, Help, Ctrl-F1, F4</B></TT> (this bindings are reserved and cannot be used
|
||||
when assigning key bindings to actions)
|
||||
</LI>
|
||||
<LI>
|
||||
Menu Navigation Actions: <TT><B>1-9, Page Up/Down, End, Home</B></TT> (these key
|
||||
bindings are usable with a menu or popup menu open and are otherwise available for
|
||||
assignment to key bindings).
|
||||
</LI>
|
||||
</UL>
|
||||
</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P><IMG alt="" border="0" src="../../shared/tip.png"> You can also change key bindings
|
||||
from within Ghidra by pressing <B>F4</B> while the mouse is over any toolbar icon or menu
|
||||
item. Click <A href="#KeyBindingPopup">here</A> for more info.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<P>The <I>Key Bindings</I> panel has a table containing the <I>Action Name</I>, <I>Key
|
||||
Binding</I>, and <I>Plugin Name</I>. You can sort the columns in ascending or descending
|
||||
order. (By default, the <I>Action Nam</I>e column is sorted in ascending order.) The Plugin
|
||||
name is the name of the plugin supplying the action.</P>
|
||||
<P>The <B>Key Bindings</B> option editor has a table with the following sortable columns:
|
||||
<I>Action Name</I>, <I>Key Binding</I>, and <I>Plugin Name</I>. To change a value in
|
||||
the table, select the row and then edit the text field below the table.</P>
|
||||
|
||||
<UL>
|
||||
<LI>Click on the category header to change the sort order.</LI>
|
||||
|
||||
<LI>Change the order of the columns by dragging the column header to another position in
|
||||
the table.</LI>
|
||||
|
||||
<LI>The text field below the table captures keystroke combinations entered.</LI>
|
||||
|
||||
<LI>If an action has a description to explain what it does, it will be displayed below the
|
||||
text field.</LI>
|
||||
</UL>
|
||||
|
||||
<P><IMG alt="" border="0" src="../../shared/note.png"> The table entries are not
|
||||
editable.</P>
|
||||
|
||||
<P>The display below shows the key bindings panel for the <I>Project Window</I>. Using the
|
||||
Key Bindings Options panel works the same as for a regular Ghidra Tool.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
@ -130,12 +139,12 @@
|
|||
</OL>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<P><IMG alt="" border="0" src="../../shared/note.png"> When a key is mapped to multiple
|
||||
<P><IMG alt="" border="0" src="../../shared/note.yellow.png"> When a key is mapped to multiple
|
||||
actions, and more than one of these actions is valid in the current context (i.e., the action
|
||||
is enabled), then a dialog is displayed for you to choose what action you want to
|
||||
perform.</P>
|
||||
|
||||
<P><IMG alt="" border="0" src="../../shared/tip.png"> To avoid the extra step of choosing the
|
||||
<P>To avoid the extra step of choosing the
|
||||
action from the dialog, do not map the same key to actions that are applicable in the same
|
||||
context.</P>
|
||||
|
||||
|
@ -185,7 +194,7 @@
|
|||
<LI>Press <B>OK</B> to import the key bindings.</LI>
|
||||
</OL>
|
||||
|
||||
<P><IMG alt="" border="0" src="../../shared/note.png"> Importing key bindings will override
|
||||
<P><IMG alt="" border="0" src="../../shared/warning.png"> Importing key bindings will override
|
||||
your current key bindings settings. It is suggested that you <A href="#Export">export your
|
||||
key bindings</A> before you import so that you may revert to your previous settings if
|
||||
necessary.</P>
|
||||
|
|
|
@ -92,4 +92,6 @@ You can reconfigure the browser display by adding / removing / moving / resizing
|
|||
|
||||
Did you know that you can run Ghidra from the command line without invoking the user interface? (See analyzeHeadlessREADME.html in the {Install Dir}/support folder.
|
||||
|
||||
You can use the following keys with an open menu or popup menu to quickly move through the menu: Page Up/Down, Home, End, and number keys 1-9.
|
||||
|
||||
This is the last tip. You can turn them off now.
|
||||
|
|
|
@ -26,6 +26,7 @@ import javax.swing.text.JTextComponent;
|
|||
|
||||
import docking.action.MultipleKeyAction;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
import docking.menu.keys.MenuKeyProcessor;
|
||||
import ghidra.util.bean.GGlassPane;
|
||||
import ghidra.util.exception.AssertException;
|
||||
|
||||
|
@ -34,7 +35,7 @@ import ghidra.util.exception.AssertException;
|
|||
* processing. See {@link #dispatchKeyEvent(KeyEvent)} for a more detailed explanation of how
|
||||
* Ghidra processes key events.
|
||||
* <p>
|
||||
* {@link #install()} must be called in order to install this <code>Singleton</code> into Java's
|
||||
* {@link #install()} must be called in order to install this <code>Singleton</code> into Java's
|
||||
* key event processing system.
|
||||
*/
|
||||
public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher {
|
||||
|
@ -45,14 +46,14 @@ public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher
|
|||
* We use this action as a signal that we intend to process a key
|
||||
* binding and that no other Java component should try to handle it (sometimes Java processes
|
||||
* bindings on key typed, after we have processed a binding on key pressed, which is not
|
||||
* what we want).
|
||||
* what we want).
|
||||
* <p>
|
||||
* This action is one that is triggered by a key pressed, but will be processed on a
|
||||
* key released. We need to do this for because on some systems, when we perform the
|
||||
* action on a key pressed, we do not get the follow-on key events, which we need to reset
|
||||
* This action is one that is triggered by a key pressed, but will be processed on a
|
||||
* key released. We need to do this for because on some systems, when we perform the
|
||||
* action on a key pressed, we do not get the follow-on key events, which we need to reset
|
||||
* our state (SCR 7040).
|
||||
* <p>
|
||||
* <b>Posterity Note:</b> While debugging we will not get a KeyEvent.KEY_RELEASED event if
|
||||
* <b>Posterity Note:</b> While debugging we will not get a KeyEvent.KEY_RELEASED event if
|
||||
* the focus changes from the application to the debugger tool.
|
||||
*/
|
||||
private DockingKeyBindingAction inProgressAction;
|
||||
|
@ -98,9 +99,9 @@ public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher
|
|||
* <P>
|
||||
* There are some exceptions to this processing chain:
|
||||
* <ol>
|
||||
* <li>We don't do any processing when the focused component is an instance of
|
||||
* <li>We don't do any processing when the focused component is an instance of
|
||||
* <code>JTextComponent</code>.</li>
|
||||
* <li>We don't do any processing if the active window is an instance of
|
||||
* <li>We don't do any processing if the active window is an instance of
|
||||
* <code>DockingDialog</code>.</li>
|
||||
* </ol>
|
||||
*
|
||||
|
@ -117,6 +118,10 @@ public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher
|
|||
return true;
|
||||
}
|
||||
|
||||
if (MenuKeyProcessor.processMenuKeyEvent(event)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
DockingKeyBindingAction action = getDockingKeyBindingActionForEvent(event);
|
||||
if (action == null) {
|
||||
return false; // let the normal event flow continue
|
||||
|
@ -147,11 +152,11 @@ public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher
|
|||
return false;
|
||||
}
|
||||
|
||||
// Process the key event in precedence order.
|
||||
// If it processes the event at any given level, the short-circuit operator will kick out.
|
||||
// Finally, if the exception statement is reached, then someone has added a new level
|
||||
// Process the key event in precedence order.
|
||||
// If it processes the event at any given level, the short-circuit operator will kick out.
|
||||
// Finally, if the exception statement is reached, then someone has added a new level
|
||||
// of precedence that this algorithm has not taken into account!
|
||||
// @formatter:off
|
||||
// @formatter:off
|
||||
return processKeyListenerPrecedence(action, keyBindingPrecedence, event) ||
|
||||
processComponentActionMapPrecedence(action, keyBindingPrecedence, event) ||
|
||||
processActionAtPrecedence(DefaultLevel, keyBindingPrecedence, action, event) ||
|
||||
|
@ -180,10 +185,10 @@ public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher
|
|||
|
||||
JRootPane rootPane = SwingUtilities.getRootPane(component);
|
||||
if (rootPane == null) {
|
||||
// This can happen when the source component of the key event has been hidden as a
|
||||
// result of processing the key event earlier, like on a key pressed event; for
|
||||
// example, when the user presses the ESC key to close a dialog.
|
||||
return true; // don't let Java process the remaining event chain
|
||||
// This can happen when the source component of the key event has been hidden as a
|
||||
// result of processing the key event earlier, like on a key pressed event; for
|
||||
// example, when the user presses the ESC key to close a dialog.
|
||||
return true; // don't let Java process the remaining event chain
|
||||
}
|
||||
|
||||
Component glassPane = rootPane.getGlassPane();
|
||||
|
@ -192,16 +197,16 @@ public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher
|
|||
return true; // out parent's glass pane is blocking..don't let events through
|
||||
}
|
||||
}
|
||||
// else {
|
||||
// Msg.debug( KeyBindingOverrideKeyEventDispatcher.this,
|
||||
// "Found a window with a non-standard glass pane--this should be fixed to " +
|
||||
// else {
|
||||
// Msg.debug( KeyBindingOverrideKeyEventDispatcher.this,
|
||||
// "Found a window with a non-standard glass pane--this should be fixed to " +
|
||||
// "use the Docking windowing system" );
|
||||
// }
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to clear the flag that signals we are in the middle of processing a Ghidra action.
|
||||
* Used to clear the flag that signals we are in the middle of processing a Ghidra action.
|
||||
*/
|
||||
private boolean actionInProgress(KeyEvent event) {
|
||||
boolean wasInProgress = inProgressAction != null;
|
||||
|
@ -219,7 +224,7 @@ public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher
|
|||
}
|
||||
|
||||
/**
|
||||
* A check to see if a given keystroke is something that should not be processed, depending
|
||||
* A check to see if a given keystroke is something that should not be processed, depending
|
||||
* upon the current state of the system.
|
||||
*
|
||||
* @param keyStroke The keystroke to check.
|
||||
|
@ -231,9 +236,9 @@ public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher
|
|||
if (activeWindow instanceof DockingDialog) {
|
||||
|
||||
// The choice to ignore modal dialogs was made long ago. We cannot remember why the
|
||||
// choice was made, but speculate that odd things can happen when keybindings are
|
||||
// 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.
|
||||
// choice was made, but speculate that odd things can happen when keybindings are
|
||||
// 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.
|
||||
DockingDialog dialog = (DockingDialog) activeWindow;
|
||||
return !dialog.isModal();
|
||||
}
|
||||
|
@ -252,8 +257,8 @@ public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher
|
|||
return false; // we only handle text components
|
||||
}
|
||||
|
||||
// Note: don't do this--it breaks key event handling for text components, as they do
|
||||
// not get to handle key events when they are not editable (they still should
|
||||
// Note: don't do this--it breaks key event handling for text components, as they do
|
||||
// not get to handle key events when they are not editable (they still should
|
||||
// though, so things like built-in copy/paste still work).
|
||||
// JTextComponent textComponent = (JTextComponent) focusOwner;
|
||||
// if (!textComponent.isEditable()) {
|
||||
|
@ -261,7 +266,7 @@ public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher
|
|||
// }
|
||||
|
||||
// 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
|
||||
if (!isModified(event)) {
|
||||
return true; // unmodified keys will be given to the text component
|
||||
|
@ -288,8 +293,8 @@ public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher
|
|||
}
|
||||
|
||||
/**
|
||||
* This method should only be called if a programmer adds a new precedence to
|
||||
* {@link KeyBindingPrecedence} and does not update the algorithm of
|
||||
* This method should only be called if a programmer adds a new precedence to
|
||||
* {@link KeyBindingPrecedence} and does not update the algorithm of
|
||||
* {@link #dispatchKeyEvent(KeyEvent)} to take into account the new precedence.
|
||||
*/
|
||||
private boolean throwAssertException() {
|
||||
|
@ -318,7 +323,7 @@ public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher
|
|||
return true;
|
||||
}
|
||||
|
||||
// O.K., there is an action for the KeyStroke, but before we process it, we have to
|
||||
// O.K., there is an action for the KeyStroke, but before we process it, we have to
|
||||
// check the proper ordering of key events (see method JavaDoc)
|
||||
if (processComponentKeyListeners(e)) {
|
||||
return true;
|
||||
|
@ -355,7 +360,7 @@ public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher
|
|||
}
|
||||
|
||||
inProgressAction = action; // this will be handled on the release
|
||||
event.consume(); // don't let this event be used later
|
||||
event.consume(); // don't let this event be used later
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -387,7 +392,7 @@ public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher
|
|||
|
||||
// note: this code is taken from the JComponent method:
|
||||
// protected boolean processKeyBinding(KeyStroke, KeyEvent, int, boolean )
|
||||
//
|
||||
//
|
||||
// returns true if there is a focused component that has an action for the given keystroke
|
||||
// and it processes that action.
|
||||
private boolean processInputAndActionMaps(KeyEvent keyEvent, KeyStroke keyStroke) {
|
||||
|
@ -406,7 +411,7 @@ public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher
|
|||
}
|
||||
|
||||
private Action getJavaActionForComponent(JComponent jComponent, KeyStroke keyStroke) {
|
||||
// first see if there is a Java key binding for when the component is in the focused
|
||||
// first see if there is a Java key binding for when the component is in the focused
|
||||
// window...
|
||||
Action action = KeyBindingUtils.getAction(jComponent, keyStroke, JComponent.WHEN_FOCUSED);
|
||||
if (action != null) {
|
||||
|
@ -447,7 +452,7 @@ public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher
|
|||
|
||||
DockingWindowManager activeManager = DockingWindowManager.getActiveInstance();
|
||||
if (activeManager == null) {
|
||||
// this can happen if clients use DockingWindows Look and Feel settings or
|
||||
// this can happen if clients use DockingWindows Look and Feel settings or
|
||||
// DockingWindows widgets without using the DockingWindows system (like in tests or
|
||||
// in stand-alone, non-Ghidra apps).
|
||||
return null;
|
||||
|
|
|
@ -20,6 +20,13 @@ import javax.swing.KeyStroke;
|
|||
import docking.KeyBindingPrecedence;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
|
||||
/**
|
||||
* An object that contains a key stroke and the precedence for when that key stroke should be used.
|
||||
*
|
||||
* <p>Note: this class creates key strokes that work on key {@code pressed}. This effectively
|
||||
* normalizes all client key bindings to work on the same type of key stroke (pressed, typed or
|
||||
* released).
|
||||
*/
|
||||
public class KeyBindingData {
|
||||
private KeyStroke keyStroke;
|
||||
private KeyBindingPrecedence keyBindingPrecedence;
|
||||
|
@ -36,6 +43,13 @@ public class KeyBindingData {
|
|||
this(KeyStroke.getKeyStroke(keyCode, modifiers));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a key stroke from the given text. See
|
||||
* {@link KeyBindingUtils#parseKeyStroke(KeyStroke)}. The key stroke created for this class
|
||||
* will always be a key {@code pressed} key stroke.
|
||||
*
|
||||
* @param keyStrokeString the key stroke string to parse
|
||||
*/
|
||||
public KeyBindingData(String keyStrokeString) {
|
||||
this(parseKeyStrokeString(keyStrokeString));
|
||||
}
|
||||
|
@ -86,7 +100,7 @@ public class KeyBindingData {
|
|||
}
|
||||
|
||||
/**
|
||||
* Updates the given data with system-independent versions of key modifiers. For example,
|
||||
* Updates the given data with system-independent versions of key modifiers. For example,
|
||||
* the <code>control</code> key will be converted to the <code>command</code> key on the Mac.
|
||||
* @param newKeyBindingData the data to validate
|
||||
* @return the potentially changed data
|
||||
|
|
|
@ -90,7 +90,7 @@ public class KeyBindingUtils {
|
|||
}
|
||||
|
||||
public static ToolOptions importKeyBindings() {
|
||||
// show a filechooser for the user to choose a location
|
||||
// show a filechooser for the user to choose a location
|
||||
InputStream inputStream = getInputStreamForFile(getStartingDir());
|
||||
return createOptionsforKeybindings(inputStream);
|
||||
}
|
||||
|
@ -145,7 +145,7 @@ public class KeyBindingUtils {
|
|||
* @param keyBindingOptions The options that contains key binding data.
|
||||
*/
|
||||
public static void exportKeyBindings(ToolOptions keyBindingOptions) {
|
||||
// show a filechooser for the user to choose a location
|
||||
// show a filechooser for the user to choose a location
|
||||
OutputStream outputStream = getOutputStreamForFile(getStartingDir());
|
||||
|
||||
if (outputStream == null) {
|
||||
|
@ -175,12 +175,12 @@ public class KeyBindingUtils {
|
|||
|
||||
/**
|
||||
* Changes the given key event to the new source component and then dispatches that event.
|
||||
* This method is intended for clients that wish to effectively take a key event given to
|
||||
* one component and give it to another component.
|
||||
* This method is intended for clients that wish to effectively take a key event given to
|
||||
* one component and give it to another component.
|
||||
*
|
||||
* <p>This method exists to deal with the complicated nature of key event processing and
|
||||
* <p>This method exists to deal with the complicated nature of key event processing and
|
||||
* how our (not Java's) framework processes key event bindings to trigger actions. If not
|
||||
* for our special processing of action key bindings, then this method would not be
|
||||
* for our special processing of action key bindings, then this method would not be
|
||||
* necessary.
|
||||
*
|
||||
* <p><b>This is seldom-used code; if you don't know when to use this code, then don't.</b>
|
||||
|
@ -201,23 +201,23 @@ public class KeyBindingUtils {
|
|||
Unusual Code Alert!
|
||||
|
||||
The KeyboardFocusManager is a complicated beast. Here we use knowledge of one such
|
||||
complication to correctly route key events. If the client of this method passes
|
||||
complication to correctly route key events. If the client of this method passes
|
||||
a component whose 'isShowing()' returns false, then the manager will not send the
|
||||
event to that component. Almost all clients will pass fully attached/realized
|
||||
event to that component. Almost all clients will pass fully attached/realized
|
||||
components to the manager. We, however, will sometimes pass components that are not
|
||||
attached; for example, when we are using said components with a renderer to perform
|
||||
our own painting. In the case of non-attached components, we must call the
|
||||
our own painting. In the case of non-attached components, we must call the
|
||||
redispatchEvent() method ourselves.
|
||||
|
||||
Why don't we just always call redispatchEvent()? Well, that
|
||||
method will not pass the new cloned event we just created back through the full
|
||||
key event pipeline. This means that tool-level (our Tool API, not Java)
|
||||
actions will not work, as tool-level actions are handled at the beginning of the
|
||||
Why don't we just always call redispatchEvent()? Well, that
|
||||
method will not pass the new cloned event we just created back through the full
|
||||
key event pipeline. This means that tool-level (our Tool API, not Java)
|
||||
actions will not work, as tool-level actions are handled at the beginning of the
|
||||
key event pipeline, not by the components themselves.
|
||||
|
||||
Also, we have here guilty knowledge that the aforementioned tool-level key processing
|
||||
will check to see if the event was consumed. If consumed, then no further processing
|
||||
will happen; if not consumed, then the framework will continue to process the event
|
||||
Also, we have here guilty knowledge that the aforementioned tool-level key processing
|
||||
will check to see if the event was consumed. If consumed, then no further processing
|
||||
will happen; if not consumed, then the framework will continue to process the event
|
||||
passed into this method. Thus, after we send the new event, we will update the
|
||||
original event to match the consumed state of our new event. This means that the
|
||||
component passed to this method must, somewhere in its processing, consume the key
|
||||
|
@ -316,7 +316,7 @@ public class KeyBindingUtils {
|
|||
* @param keyStroke the keystroke for to which the action will be bound
|
||||
* @param action the action to execute when the given keystroke is triggered
|
||||
* @param focusCondition the focus condition under which to bind the action
|
||||
* ({@link JComponent#getInputMap(int)}). See {@link JComponent} for more info;
|
||||
* ({@link JComponent#getInputMap(int)}). See {@link JComponent} for more info;
|
||||
* the default is usually {@link JComponent#WHEN_FOCUSED}
|
||||
*/
|
||||
public static void registerAction(JComponent component, KeyStroke keyStroke, Action action,
|
||||
|
@ -348,7 +348,7 @@ public class KeyBindingUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* Allows the client to clear Java key bindings when the client is creating a docking
|
||||
* Allows the client to clear Java key bindings when the client is creating a docking
|
||||
* action. Without this call, any actions bound to the given component will prevent an
|
||||
* action with the same key binding from firing. This is useful when your
|
||||
* application is using tool-level key bindings that share the same
|
||||
|
@ -370,8 +370,8 @@ public class KeyBindingUtils {
|
|||
* application is using tool-level key bindings that share the same
|
||||
* keystroke as a built-in Java action, such as Ctrl-C for the copy action.
|
||||
* <p>
|
||||
* Note: this method clears the key binding for the
|
||||
* {@link JComponent#WHEN_FOCUSED} and
|
||||
* Note: this method clears the key binding for the
|
||||
* {@link JComponent#WHEN_FOCUSED} and
|
||||
* {@link JComponent#WHEN_ANCESTOR_OF_FOCUSED_COMPONENT} focus conditions.
|
||||
*
|
||||
* @param component the component for which to clear the key binding
|
||||
|
@ -455,10 +455,10 @@ public class KeyBindingUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* A utility method to get all key binding actions. This method will
|
||||
* A utility method to get all key binding actions. This method will
|
||||
* only return actions that support {@link KeyBindingType key bindings}.
|
||||
*
|
||||
* <p>The mapping returned provides a list of items because it is possible for there to
|
||||
* <p>The mapping returned provides a list of items because it is possible for there to
|
||||
* exists multiple actions with the same name and owner. (This can happen when multiple copies
|
||||
* of a component provider are shown, each with their own set of actions that share the
|
||||
* same name.)
|
||||
|
@ -474,7 +474,7 @@ public class KeyBindingUtils {
|
|||
for (DockingActionIf action : actions) {
|
||||
if (isIgnored(action)) {
|
||||
// don't bother tracking non-keybinding actions; this would be a mistake due
|
||||
// to the potential for a shared key binding action overwriting its
|
||||
// to the potential for a shared key binding action overwriting its
|
||||
// SharedStubKeyBindingAction
|
||||
continue;
|
||||
}
|
||||
|
@ -486,8 +486,8 @@ public class KeyBindingUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* A utility method to get all key binding actions that have the given owner.
|
||||
* This method will remove duplicate actions and will only return actions
|
||||
* A utility method to get all key binding actions that have the given owner.
|
||||
* This method will remove duplicate actions and will only return actions
|
||||
* that support {@link KeyBindingType key bindings}.
|
||||
*
|
||||
* @param tool the tool containing the actions
|
||||
|
@ -502,7 +502,7 @@ public class KeyBindingUtils {
|
|||
for (DockingActionIf action : actions) {
|
||||
if (isIgnored(action)) {
|
||||
// don't bother tracking non-keybinding actions; this would be a mistake due
|
||||
// to the potential for a shared key binding action overwriting its
|
||||
// to the potential for a shared key binding action overwriting its
|
||||
// SharedStubKeyBindingAction
|
||||
continue;
|
||||
}
|
||||
|
@ -590,10 +590,10 @@ public class KeyBindingUtils {
|
|||
//@formatter:off
|
||||
String s = "Shared Key Binding Actions have different default values. These " +
|
||||
"must be the same." +
|
||||
"\n\tAction name: '"+existingAction.getName()+"'" +
|
||||
"\n\tAction name: '"+existingAction.getName()+"'" +
|
||||
"\n\tAction 1: " + existingAction.getInceptionInformation() +
|
||||
"\n\t\tKey Binding: " + existingDefaultKs +
|
||||
"\n\tAction 2: " + newAction.getInceptionInformation() +
|
||||
"\n\tAction 2: " + newAction.getInceptionInformation() +
|
||||
"\n\t\tKey Binding: " + newAction.getKeyBinding() +
|
||||
"\nUsing the " +
|
||||
"first value set - " + existingDefaultKs;
|
||||
|
@ -603,14 +603,14 @@ public class KeyBindingUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* Updates the given data with system-independent versions of key modifiers. For example,
|
||||
* Updates the given data with system-independent versions of key modifiers. For example,
|
||||
* the <code>control</code> key will be converted to the <code>command</code> key on the Mac.
|
||||
*
|
||||
* @param keyStroke the keystroke to validate
|
||||
* @return the potentially changed keystroke
|
||||
*/
|
||||
// TODO ignore the deprecation, as this method is responsible for fixing deprecated usage.
|
||||
// When all actions no longer user the deprecated modifiers, the deprecated elements
|
||||
// TODO ignore the deprecation, as this method is responsible for fixing deprecated usage.
|
||||
// When all actions no longer user the deprecated modifiers, the deprecated elements
|
||||
// of this method can be removed
|
||||
@SuppressWarnings("deprecation")
|
||||
public static KeyStroke validateKeyStroke(KeyStroke keyStroke) {
|
||||
|
@ -667,10 +667,10 @@ public class KeyBindingUtils {
|
|||
* and we want it to look like: "Ctrl-M".
|
||||
* <br>In Java 1.5.0, Ctrl-M is returned as "ctrl pressed M"
|
||||
* and we want it to look like: "Ctrl-M".
|
||||
* <br>In Java 11 we have seen toString() values get printed with repeated text, such
|
||||
* <br>In Java 11 we have seen toString() values get printed with repeated text, such
|
||||
* as: "shift ctrl pressed SHIFT". We want to trim off the repeated modifiers.
|
||||
*
|
||||
* @param keyStroke the key stroke
|
||||
* @param keyStroke the key stroke
|
||||
* @return the string value; the empty string if the key stroke is null
|
||||
*/
|
||||
public static String parseKeyStroke(KeyStroke keyStroke) {
|
||||
|
@ -744,28 +744,28 @@ public class KeyBindingUtils {
|
|||
return StringUtils.indexOfIgnoreCase(source, search, offset);
|
||||
}
|
||||
|
||||
// ignore the deprecated; remove when we are confident that all tool actions no longer use the
|
||||
// ignore the deprecated; remove when we are confident that all tool actions no longer use the
|
||||
// deprecated InputEvent mask types
|
||||
@SuppressWarnings("deprecation")
|
||||
private static boolean isShift(int mask) {
|
||||
return (mask & InputEvent.SHIFT_DOWN_MASK) != 0 || (mask & InputEvent.SHIFT_MASK) != 0;
|
||||
}
|
||||
|
||||
// ignore the deprecated; remove when we are confident that all tool actions no longer use the
|
||||
// ignore the deprecated; remove when we are confident that all tool actions no longer use the
|
||||
// deprecated InputEvent mask types
|
||||
@SuppressWarnings("deprecation")
|
||||
private static boolean isAlt(int mask) {
|
||||
return (mask & InputEvent.ALT_DOWN_MASK) != 0 || (mask & InputEvent.ALT_MASK) != 0;
|
||||
}
|
||||
|
||||
// ignore the deprecated; remove when we are confident that all tool actions no longer use the
|
||||
// ignore the deprecated; remove when we are confident that all tool actions no longer use the
|
||||
// deprecated InputEvent mask types
|
||||
@SuppressWarnings("deprecation")
|
||||
private static boolean isControl(int mask) {
|
||||
return (mask & InputEvent.CTRL_DOWN_MASK) != 0 || (mask & InputEvent.CTRL_MASK) != 0;
|
||||
}
|
||||
|
||||
// ignore the deprecated; remove when we are confident that all tool actions no longer use the
|
||||
// ignore the deprecated; remove when we are confident that all tool actions no longer use the
|
||||
// deprecated InputEvent mask types
|
||||
@SuppressWarnings("deprecation")
|
||||
private static boolean isMeta(int mask) {
|
||||
|
@ -773,18 +773,21 @@ public class KeyBindingUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* Parses the given text into a KeyStroke. This method relies upon
|
||||
* Parses the given text into a KeyStroke. This method relies upon
|
||||
* {@link KeyStroke#getKeyStroke(String)} for parsing. Before making that call, this method
|
||||
* will perform fixup on the given text for added flexibility. For example, the given
|
||||
* text may contain spaces or dashes as the separators between parts in the string. Also,
|
||||
* the text is converted such that it is not case-sensitive. So, the following example
|
||||
* will perform fixup on the given text for added flexibility. For example, the given
|
||||
* text may contain spaces or dashes as the separators between parts in the string. Also,
|
||||
* the text is converted such that it is not case-sensitive. So, the following example
|
||||
* formats are allowed:
|
||||
* <pre>
|
||||
* Alt-F
|
||||
* alt p
|
||||
* Ctrl-Alt-Z
|
||||
* ctrl Z
|
||||
* </pre>
|
||||
* </pre>
|
||||
*
|
||||
* <p><b>Note:</b> The returned keystroke will always correspond to a {@code pressed} event,
|
||||
* regardless of the value passed in (pressed, typed or released).
|
||||
*
|
||||
* @param keyStroke the key stroke
|
||||
* @return the new key stroke (as returned by {@link KeyStroke#getKeyStroke(String)}
|
||||
|
@ -855,10 +858,10 @@ public class KeyBindingUtils {
|
|||
|
||||
//==================================================================================================
|
||||
// Private Methods
|
||||
//==================================================================================================
|
||||
//==================================================================================================
|
||||
|
||||
private static boolean isIgnored(DockingActionIf action) {
|
||||
// a shared keybinding implies that this action should not be in
|
||||
// a shared keybinding implies that this action should not be in
|
||||
// the UI, as there will be a single proxy in place of all actions sharing that binding
|
||||
return !action.getKeyBindingType().isManaged();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/* ###
|
||||
* 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.menu.keys;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
class EndMenuKeyHandler extends MenuKeyHandler {
|
||||
|
||||
@Override
|
||||
void process(MenuSelectionManager manager, MenuElement[] path) {
|
||||
int popupIndex = getLeafPopupIndex(path);
|
||||
if (popupIndex == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
JPopupMenu popup = (JPopupMenu) path[popupIndex];
|
||||
int n = popup.getComponentCount();
|
||||
|
||||
MenuElement newItem = getPreviousValidItem(popup, n - 1);
|
||||
int length = path.length - 1 == popupIndex ? path.length + 1 : path.length;
|
||||
|
||||
MenuElement[] newPath = new MenuElement[length];
|
||||
System.arraycopy(path, 0, newPath, 0, popupIndex + 1);
|
||||
newPath[popupIndex + 1] = newItem;
|
||||
|
||||
// replace last path element
|
||||
manager.setSelectedPath(newPath);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/* ###
|
||||
* 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.menu.keys;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
class HomeMenuKeyHandler extends MenuKeyHandler {
|
||||
|
||||
@Override
|
||||
void process(MenuSelectionManager manager, MenuElement[] path) {
|
||||
|
||||
int popupIndex = getLeafPopupIndex(path);
|
||||
if (popupIndex == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
JPopupMenu popup = (JPopupMenu) path[popupIndex];
|
||||
MenuElement newItem = getNextValidItem(popup, 0);
|
||||
int length = path.length - 1 == popupIndex ? path.length + 1 : path.length;
|
||||
|
||||
MenuElement[] newPath = new MenuElement[length];
|
||||
System.arraycopy(path, 0, newPath, 0, popupIndex + 1);
|
||||
newPath[popupIndex + 1] = newItem;
|
||||
|
||||
// replace last path element
|
||||
manager.setSelectedPath(newPath);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,260 @@
|
|||
/* ###
|
||||
* 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.menu.keys;
|
||||
|
||||
import java.awt.Component;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
/**
|
||||
* The interface for work to be done on an open menu.
|
||||
*/
|
||||
abstract class MenuKeyHandler {
|
||||
|
||||
/**
|
||||
* The work method of this handler. This method will only be called when a menu or popup
|
||||
* menu is open.
|
||||
*
|
||||
* @param manager the active menu selection manager
|
||||
* @param path the active menu path
|
||||
*/
|
||||
abstract void process(MenuSelectionManager manager, MenuElement[] path);
|
||||
|
||||
protected int getLeafPopupIndex(MenuElement[] path) {
|
||||
|
||||
if (path != null) {
|
||||
for (int i = path.length - 1; i >= 0; i--) {
|
||||
MenuElement menuElement = path[i];
|
||||
if (menuElement instanceof JPopupMenu) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
protected JPopupMenu getLeafPopupMenu(MenuElement[] path) {
|
||||
|
||||
int index = getLeafPopupIndex(path);
|
||||
if (index >= 0) {
|
||||
return (JPopupMenu) path[index];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected int findActiveMenuItemIndex(MenuSelectionManager manager, MenuElement[] path) {
|
||||
|
||||
int popupIndexdex = getLeafPopupIndex(path);
|
||||
if (popupIndexdex == -1) {
|
||||
return -1; // not sure if this can happen
|
||||
}
|
||||
|
||||
if (popupIndexdex == path.length - 1) {
|
||||
// last item is the popup--no selected menu item
|
||||
return -1;
|
||||
}
|
||||
|
||||
MenuElement activeItem = path[path.length - 1];
|
||||
JPopupMenu popup = (JPopupMenu) path[popupIndexdex];
|
||||
int count = 0;
|
||||
int n = popup.getComponentCount();
|
||||
for (int i = 0; i < n; i++) {
|
||||
Component c = popup.getComponent(i);
|
||||
if (isValidItem(c)) {
|
||||
if (activeItem == c) {
|
||||
return count;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
protected int findActiveMenuItemRawIndex(MenuSelectionManager manager, MenuElement[] path) {
|
||||
|
||||
int popupIndexdex = getLeafPopupIndex(path);
|
||||
if (popupIndexdex == -1) {
|
||||
return -1; // not sure if this can happen
|
||||
}
|
||||
|
||||
if (popupIndexdex == path.length - 1) {
|
||||
// last item is the popup--no selected menu item
|
||||
return -1;
|
||||
}
|
||||
|
||||
MenuElement activeItem = path[path.length - 1];
|
||||
JPopupMenu popup = (JPopupMenu) path[popupIndexdex];
|
||||
int count = 0;
|
||||
int n = popup.getComponentCount();
|
||||
for (int i = 0; i < n; i++) {
|
||||
Component c = popup.getComponent(i);
|
||||
if (activeItem == c) {
|
||||
return count;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
protected int findNextSeparatorIndex(JPopupMenu popup, int startIndex) {
|
||||
|
||||
int n = popup.getComponentCount();
|
||||
for (int i = startIndex; i < n; i++) {
|
||||
Component c = popup.getComponent(i);
|
||||
if (c instanceof JSeparator) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
protected int findPreviousSeparatorIndex(JPopupMenu popup, int startIndex) {
|
||||
|
||||
for (int i = startIndex; i >= 0; i--) {
|
||||
Component c = popup.getComponent(i);
|
||||
if (c instanceof JSeparator) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
protected int findNextValidIndex(JPopupMenu popup, int startIndex) {
|
||||
|
||||
int n = popup.getComponentCount();
|
||||
for (int i = startIndex; i < n; i++) {
|
||||
Component c = popup.getComponent(i);
|
||||
if (isValidItem(c)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
protected int findPreviousValidIndex(JPopupMenu popup, int startIndex) {
|
||||
|
||||
for (int i = startIndex; i >= 0; i--) {
|
||||
Component c = popup.getComponent(i);
|
||||
if (isValidItem(c)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
protected int moveForward(MenuSelectionManager manager, MenuElement[] path,
|
||||
int offset) {
|
||||
|
||||
JPopupMenu popup = getLeafPopupMenu(path);
|
||||
if (popup == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int itemCount = getItemCount(popup);
|
||||
|
||||
// handle wrapping around to the top again
|
||||
int updatedOffset = offset >= itemCount ? offset % itemCount : offset;
|
||||
int progress = 0;
|
||||
int n = popup.getComponentCount();
|
||||
for (int i = 0; i < n; i++) {
|
||||
Component c = popup.getComponent(i);
|
||||
if (isValidItem(c)) {
|
||||
if (progress == updatedOffset) {
|
||||
return i;
|
||||
}
|
||||
progress++;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
protected void setNewMenuItemIndex(MenuSelectionManager manager, MenuElement[] path,
|
||||
int index) {
|
||||
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int popupIndex = getLeafPopupIndex(path);
|
||||
JPopupMenu popup = (JPopupMenu) path[popupIndex];
|
||||
|
||||
JMenuItem newItem = (JMenuItem) popup.getComponent(index);
|
||||
int length = path.length - 1 == popupIndex ? path.length + 1 : path.length;
|
||||
|
||||
MenuElement[] newPath = new MenuElement[length];
|
||||
System.arraycopy(path, 0, newPath, 0, popupIndex + 1);
|
||||
newPath[popupIndex + 1] = newItem;
|
||||
|
||||
// replace last path element
|
||||
manager.setSelectedPath(newPath);
|
||||
}
|
||||
|
||||
protected MenuElement getNextValidItem(JPopupMenu popup, int start) {
|
||||
|
||||
int n = popup.getComponentCount();
|
||||
for (int i = 0; i < n; i++) {
|
||||
|
||||
// handle wrapping
|
||||
int updated = i + start;
|
||||
int index = (updated < n) ? updated : Math.abs(i - (n - start));
|
||||
|
||||
Component c = popup.getComponent(index);
|
||||
if (isValidItem(c)) {
|
||||
return (MenuElement) c;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected MenuElement getPreviousValidItem(JPopupMenu popup, int offset) {
|
||||
|
||||
int n = popup.getComponentCount();
|
||||
for (int i = n - 1; i >= 0; i--) {
|
||||
|
||||
// handle wrapping
|
||||
int updated = (n - (i + 1));
|
||||
int index = (updated > offset) ? updated : offset - updated;
|
||||
|
||||
Component c = popup.getComponent(index);
|
||||
if (isValidItem(c)) {
|
||||
return (MenuElement) c;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected int getItemCount(JPopupMenu popup) {
|
||||
int count = 0;
|
||||
int n = popup.getComponentCount();
|
||||
for (int i = 0; i < n; i++) {
|
||||
Component c = popup.getComponent(i);
|
||||
if (isValidItem(c)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
protected boolean isValidItem(Component c) {
|
||||
return c instanceof JMenuItem && c.isEnabled();
|
||||
}
|
||||
}
|
|
@ -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.menu.keys;
|
||||
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
/**
|
||||
* Handles the processing of key events while menus or popup menus are open.
|
||||
*/
|
||||
public class MenuKeyProcessor {
|
||||
|
||||
private static Map<KeyStroke, MenuKeyHandler> menuHandlersByKeyStroke = new HashMap<>();
|
||||
|
||||
static {
|
||||
|
||||
menuHandlersByKeyStroke.put(keyStroke("HOME"), new HomeMenuKeyHandler());
|
||||
menuHandlersByKeyStroke.put(keyStroke("END"), new EndMenuKeyHandler());
|
||||
menuHandlersByKeyStroke.put(keyStroke("PAGE_UP"), new PageUpMenuKeyHandler());
|
||||
menuHandlersByKeyStroke.put(keyStroke("PAGE_DOWN"), new PageDownMenuKeyHandler());
|
||||
|
||||
for (int i = 1; i < 10; i++) {
|
||||
menuHandlersByKeyStroke.put(keyStroke(Integer.toString(i)),
|
||||
new NumberMenuKeyHandler(i));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the given event to see if it has a registered action to perform while a menu is open.
|
||||
* If a menu is open and a handler exists, the handler will be called.
|
||||
*
|
||||
* @param event the event to check
|
||||
* @return true if the event triggered a handler
|
||||
*/
|
||||
public static boolean processMenuKeyEvent(KeyEvent event) {
|
||||
|
||||
MenuSelectionManager manager = MenuSelectionManager.defaultManager();
|
||||
MenuElement[] path = manager.getSelectedPath();
|
||||
if (path == null || path.length == 0) {
|
||||
return false; // no menu showing
|
||||
}
|
||||
|
||||
KeyStroke eventStroke = KeyStroke.getKeyStrokeForEvent(event);
|
||||
MenuKeyHandler Handler = menuHandlersByKeyStroke.get(eventStroke);
|
||||
if (Handler != null) {
|
||||
Handler.process(manager, path);
|
||||
event.consume();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static KeyStroke keyStroke(String s) {
|
||||
return KeyStroke.getKeyStroke("pressed " + s);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/* ###
|
||||
* 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.menu.keys;
|
||||
|
||||
import javax.swing.MenuElement;
|
||||
import javax.swing.MenuSelectionManager;
|
||||
|
||||
class NumberMenuKeyHandler extends MenuKeyHandler {
|
||||
|
||||
private int number;
|
||||
|
||||
NumberMenuKeyHandler(int number) {
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(MenuSelectionManager manager, MenuElement[] path) {
|
||||
int activeMenuItemIndex = findActiveMenuItemIndex(manager, path);
|
||||
int amount = activeMenuItemIndex + number;
|
||||
int nextIndex = moveForward(manager, path, amount);
|
||||
setNewMenuItemIndex(manager, path, nextIndex);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/* ###
|
||||
* 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.menu.keys;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
class PageDownMenuKeyHandler extends MenuKeyHandler {
|
||||
|
||||
@Override
|
||||
void process(MenuSelectionManager manager, MenuElement[] path) {
|
||||
|
||||
JPopupMenu popup = getLeafPopupMenu(path);
|
||||
if (popup == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int activeIndex = findActiveMenuItemRawIndex(manager, path);
|
||||
int separatorIndex = findNextSeparatorIndex(popup, activeIndex + 1);
|
||||
int nextIndex = findNextValidIndex(popup, separatorIndex + 1);
|
||||
if (nextIndex < 0) {
|
||||
separatorIndex = -1; // wrap the search; start at the top
|
||||
nextIndex = findNextValidIndex(popup, separatorIndex + 1);
|
||||
}
|
||||
|
||||
setNewMenuItemIndex(manager, path, nextIndex);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/* ###
|
||||
* 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.menu.keys;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
class PageUpMenuKeyHandler extends MenuKeyHandler {
|
||||
|
||||
@Override
|
||||
void process(MenuSelectionManager manager, MenuElement[] path) {
|
||||
JPopupMenu popup = getLeafPopupMenu(path);
|
||||
if (popup == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int activeIndex = findActiveMenuItemRawIndex(manager, path);
|
||||
int separatorIndex = -1;
|
||||
if (activeIndex >= 0) {
|
||||
// Only search for separator with an active item. This will trigger the search
|
||||
// to start at the bottom of the menu
|
||||
separatorIndex = findPreviousSeparatorIndex(popup, activeIndex - 1);
|
||||
}
|
||||
|
||||
int nextIndex = findPreviousValidIndex(popup, separatorIndex - 1);
|
||||
if (nextIndex < 0) {
|
||||
separatorIndex = popup.getComponentCount(); // wrap the search; start at the bottom
|
||||
nextIndex = findPreviousValidIndex(popup, separatorIndex - 1);
|
||||
}
|
||||
|
||||
setNewMenuItemIndex(manager, path, nextIndex);
|
||||
}
|
||||
|
||||
}
|
|
@ -199,7 +199,7 @@ public class ToolOptions extends AbstractOptions {
|
|||
Element elem = null;
|
||||
if (value == null) {
|
||||
// Handle the null case ourselves, not using the wrapped option (and when
|
||||
// reading from xml) so that the logic does not need to in each wrapped option
|
||||
// reading from xml) so the logic does not need to be in each wrapped option
|
||||
elem = ss.saveToXml();
|
||||
elem.addContent(new Element(CLEARED_VALUE_ELEMENT_NAME));
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue