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="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="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 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="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="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" />
|
<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>
|
<P align="center"><IMG alt="" src="images/Err_Dialog.png" border="0"></P>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<BLOCKQUOTE>
|
|
||||||
<P align="left">The <B>Details</B> >>> button expands the dialog to show the
|
<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>
|
details of the java stack trace. (The stack trace is also output to the console.)</P>
|
||||||
</BLOCKQUOTE><BR>
|
</BLOCKQUOTE><BR>
|
||||||
|
|
||||||
</BLOCKQUOTE>
|
|
||||||
</BODY>
|
</BODY>
|
||||||
</HTML>
|
</HTML>
|
||||||
|
|
|
@ -59,7 +59,7 @@
|
||||||
"4">Subroutine</FONT></B><IMG border="0" src="../../shared/arrow.gif"><FONT size=
|
"4">Subroutine</FONT></B><IMG border="0" src="../../shared/arrow.gif"><FONT size=
|
||||||
"4"><B><I><block model name>.</I></B></FONT></P>
|
"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>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@
|
||||||
<P>
|
<P>
|
||||||
This action modularizes the program tree by creating a call tree of the
|
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
|
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>
|
</P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,17 @@
|
||||||
on a <A href="help/topics/BlockModel/Block_Model.htm">Block Model</A>. The following paragraphs
|
on a <A href="help/topics/BlockModel/Block_Model.htm">Block Model</A>. The following paragraphs
|
||||||
describe features of the Program Tree.</P>
|
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>
|
<H2><A name="FoldersAndFragments"></A>Folders and Fragments</H2>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<META name="generator" content=
|
<META name="generator" content=
|
||||||
"HTML Tidy for Java (vers. 2009-12-01), see jtidy.sourceforge.net">
|
"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">
|
<META http-equiv="content-type" content="text/html; charset=windows-1252">
|
||||||
<LINK rel="stylesheet" type="text/css" href="../../shared/Frontpage.css">
|
<LINK rel="stylesheet" type="text/css" href="../../shared/Frontpage.css">
|
||||||
<META name="generator" content="Microsoft FrontPage 4.0">
|
<META name="generator" content="Microsoft FrontPage 4.0">
|
||||||
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
<BODY>
|
<BODY>
|
||||||
<H1><A name="ProgramTreePlugin"></A><A name="Program_Tree"></A>
|
<H1><A name="ProgramTreePlugin"></A><A name="Program_Tree"></A>
|
||||||
Program Tree Plugin
|
Program Tree View Management
|
||||||
</H1>
|
</H1>
|
||||||
|
|
||||||
<P>Program Trees are used to organize programs into a tree structure. Nodes within the a
|
<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>
|
<H2><A name="KeyBindings_Option"></A><B>Key Bindings</B></H2>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P>You can create a new "hot key" for a Plugin's action or modify the default key binding.
|
<P>You can create a new key binding (<I>accelerator key</I>) for an action or modify the
|
||||||
The hot key (or accelerator) that you add can be used to execute the action with a keystroke
|
default key binding. The key binding that you add can be used to execute the action
|
||||||
combination.</P>
|
using the keyboard. Below we describe the <B>Key Bindings</B> options editor.</P>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<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
|
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>
|
item. Click <A href="#KeyBindingPopup">here</A> for more info.</P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
<P>The <I>Key Bindings</I> panel has a table containing the <I>Action Name</I>, <I>Key
|
<P>The <B>Key Bindings</B> option editor has a table with the following sortable columns:
|
||||||
Binding</I>, and <I>Plugin Name</I>. You can sort the columns in ascending or descending
|
<I>Action Name</I>, <I>Key Binding</I>, and <I>Plugin Name</I>. To change a value in
|
||||||
order. (By default, the <I>Action Nam</I>e column is sorted in ascending order.) The Plugin
|
the table, select the row and then edit the text field below the table.</P>
|
||||||
name is the name of the plugin supplying the action.</P>
|
|
||||||
|
|
||||||
<UL>
|
<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>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
|
<LI>If an action has a description to explain what it does, it will be displayed below the
|
||||||
text field.</LI>
|
text field.</LI>
|
||||||
</UL>
|
</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
|
<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>
|
Key Bindings Options panel works the same as for a regular Ghidra Tool.</P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
@ -130,12 +139,12 @@
|
||||||
</OL>
|
</OL>
|
||||||
</BLOCKQUOTE>
|
</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
|
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
|
is enabled), then a dialog is displayed for you to choose what action you want to
|
||||||
perform.</P>
|
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
|
action from the dialog, do not map the same key to actions that are applicable in the same
|
||||||
context.</P>
|
context.</P>
|
||||||
|
|
||||||
|
@ -185,7 +194,7 @@
|
||||||
<LI>Press <B>OK</B> to import the key bindings.</LI>
|
<LI>Press <B>OK</B> to import the key bindings.</LI>
|
||||||
</OL>
|
</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
|
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
|
key bindings</A> before you import so that you may revert to your previous settings if
|
||||||
necessary.</P>
|
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.
|
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.
|
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.action.MultipleKeyAction;
|
||||||
import docking.actions.KeyBindingUtils;
|
import docking.actions.KeyBindingUtils;
|
||||||
|
import docking.menu.keys.MenuKeyProcessor;
|
||||||
import ghidra.util.bean.GGlassPane;
|
import ghidra.util.bean.GGlassPane;
|
||||||
import ghidra.util.exception.AssertException;
|
import ghidra.util.exception.AssertException;
|
||||||
|
|
||||||
|
@ -117,6 +118,10 @@ public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (MenuKeyProcessor.processMenuKeyEvent(event)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
DockingKeyBindingAction action = getDockingKeyBindingActionForEvent(event);
|
DockingKeyBindingAction action = getDockingKeyBindingActionForEvent(event);
|
||||||
if (action == null) {
|
if (action == null) {
|
||||||
return false; // let the normal event flow continue
|
return false; // let the normal event flow continue
|
||||||
|
|
|
@ -20,6 +20,13 @@ import javax.swing.KeyStroke;
|
||||||
import docking.KeyBindingPrecedence;
|
import docking.KeyBindingPrecedence;
|
||||||
import docking.actions.KeyBindingUtils;
|
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 {
|
public class KeyBindingData {
|
||||||
private KeyStroke keyStroke;
|
private KeyStroke keyStroke;
|
||||||
private KeyBindingPrecedence keyBindingPrecedence;
|
private KeyBindingPrecedence keyBindingPrecedence;
|
||||||
|
@ -36,6 +43,13 @@ public class KeyBindingData {
|
||||||
this(KeyStroke.getKeyStroke(keyCode, modifiers));
|
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) {
|
public KeyBindingData(String keyStrokeString) {
|
||||||
this(parseKeyStrokeString(keyStrokeString));
|
this(parseKeyStrokeString(keyStrokeString));
|
||||||
}
|
}
|
||||||
|
|
|
@ -786,6 +786,9 @@ public class KeyBindingUtils {
|
||||||
* ctrl 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
|
* @param keyStroke the key stroke
|
||||||
* @return the new key stroke (as returned by {@link KeyStroke#getKeyStroke(String)}
|
* @return the new key stroke (as returned by {@link KeyStroke#getKeyStroke(String)}
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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;
|
Element elem = null;
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
// Handle the null case ourselves, not using the wrapped option (and when
|
// 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 = ss.saveToXml();
|
||||||
elem.addContent(new Element(CLEARED_VALUE_ELEMENT_NAME));
|
elem.addContent(new Element(CLEARED_VALUE_ELEMENT_NAME));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue