mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 10:19:23 +02:00
GT-2925 - Key Bindings - Support Window Menu Provider Key Bindings -
Step - Added 'snapshot' concept to base Component Provider to fix how actions are added to the toolbar and menu; fixed bugs and tests
This commit is contained in:
parent
3946a05ded
commit
10621008e0
18 changed files with 291 additions and 84 deletions
|
@ -13,7 +13,7 @@
|
||||||
</HEAD>
|
</HEAD>
|
||||||
|
|
||||||
<BODY>
|
<BODY>
|
||||||
<H1><A name="Code_Browser"></A>Listing View</H1>
|
<H1><A name="Code_Browser"></A><A name="Listing"></A>Listing View</H1>
|
||||||
|
|
||||||
<P>The Listing View is the main windows for displaying and working with a program's instruction
|
<P>The Listing View is the main windows for displaying and working with a program's instruction
|
||||||
and data.</P>
|
and data.</P>
|
||||||
|
|
|
@ -476,6 +476,17 @@
|
||||||
|
|
||||||
<P class="providedbyplugin">Provided by: <I>Go To Next-Previous Code Unit</I> plugin</P>
|
<P class="providedbyplugin">Provided by: <I>Go To Next-Previous Code Unit</I> plugin</P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
This file is different than most, since it has multiple plugins contributing to the
|
||||||
|
content. Add some space at the bottom of the file to separate this last contribution
|
||||||
|
-->
|
||||||
|
<BR>
|
||||||
|
<BR>
|
||||||
|
<BR>
|
||||||
|
<BR>
|
||||||
|
<BR>
|
||||||
|
<BR>
|
||||||
|
|
||||||
<H2><A name="Next_Previous_Function"></A>Next/Previous Function</H2>
|
<H2><A name="Next_Previous_Function"></A>Next/Previous Function</H2>
|
||||||
|
|
||||||
|
@ -497,8 +508,21 @@
|
||||||
<P> This action navigates the cursor to the closest function entry point that is at an
|
<P> This action navigates the cursor to the closest function entry point that is at an
|
||||||
address less than the current address. The default keybinding is <TT><B>Control-Up Arrow</B></TT>.</P>
|
address less than the current address. The default keybinding is <TT><B>Control-Up Arrow</B></TT>.</P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
</BLOCKQUOTE>
|
|
||||||
<P class="providedbyplugin">Provided by: <I>CodeBrowser</I> plugin</P>
|
<P class="providedbyplugin">Provided by: <I>CodeBrowser</I> plugin</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
|
||||||
|
<!--
|
||||||
|
This file is different than most, since it has multiple plugins contributing to the
|
||||||
|
content. Add some space at the bottom of the file to separate this last contribution
|
||||||
|
-->
|
||||||
|
<BR>
|
||||||
|
<BR>
|
||||||
|
<BR>
|
||||||
|
<BR>
|
||||||
|
<BR>
|
||||||
|
<BR>
|
||||||
|
|
||||||
<H2><A name="Navigation_History"></A>Navigation History</H2>
|
<H2><A name="Navigation_History"></A>Navigation History</H2>
|
||||||
|
|
||||||
|
@ -554,9 +578,54 @@
|
||||||
<P>After clearing the history, the <IMG src="images/left.png" border="0"> and <IMG
|
<P>After clearing the history, the <IMG src="images/left.png" border="0"> and <IMG
|
||||||
src="images/right.png" border="0"> buttons are disabled</P>
|
src="images/right.png" border="0"> buttons are disabled</P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
</BLOCKQUOTE>
|
|
||||||
|
<P class="providedbyplugin">Provided by: <I>Next/Previous</I> plugin</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
This file is different than most, since it has multiple plugins contributing to the
|
||||||
|
content. Add some space at the bottom of the file to separate this contribution.
|
||||||
|
-->
|
||||||
|
<BR>
|
||||||
|
<BR>
|
||||||
|
<BR>
|
||||||
|
<BR>
|
||||||
|
<BR>
|
||||||
|
<BR>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<H2>Component Provider Navigation</H2>
|
||||||
|
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>
|
||||||
|
This section lists actions that allow the user to navigate between component providers.
|
||||||
|
</P>
|
||||||
|
|
||||||
|
<H3><A name="Navigation_Previous_Provider"></A>Go To Last Active Component</H3>
|
||||||
|
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>
|
||||||
|
Allows the user to switch focus back to the previously focused component provider.
|
||||||
|
</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<P class="providedbyplugin">Provided by: <I>ProviderNavigation</I> plugin</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
This file is different than most, since it has multiple plugins contributing to the
|
||||||
|
content. Add some space at the bottom of the file to separate this contribution.
|
||||||
|
-->
|
||||||
|
<BR>
|
||||||
|
<BR>
|
||||||
|
<BR>
|
||||||
|
<BR>
|
||||||
|
<BR>
|
||||||
|
<BR>
|
||||||
|
|
||||||
|
|
||||||
<P class="providedbyplugin">Provided by: <I>Next/Previous</I> plugin</P>
|
|
||||||
|
|
||||||
<P class="relatedtopic">Related Topics:</P>
|
<P class="relatedtopic">Related Topics:</P>
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
completely different instruction sets. To disassemble properly, the mode register must be set
|
completely different instruction sets. To disassemble properly, the mode register must be set
|
||||||
at the address where the disassembly begins.</P>
|
at the address where the disassembly begins.</P>
|
||||||
|
|
||||||
<H2><A name="RegisterManager"></A>Register Manager</H2>
|
<H2><A name="Register_Manager"></A>Register Manager</H2>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P>The <I>Register Manager</I> displays the assigned values of registers at addresses within
|
<P>The <I>Register Manager</I> displays the assigned values of registers at addresses within
|
||||||
|
|
|
@ -54,6 +54,7 @@ import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
import ghidra.program.util.*;
|
import ghidra.program.util.*;
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.HelpLocation;
|
||||||
|
import ghidra.util.Swing;
|
||||||
import resources.ResourceManager;
|
import resources.ResourceManager;
|
||||||
|
|
||||||
public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||||
|
@ -117,17 +118,21 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||||
private MultiListingLayoutModel multiModel;
|
private MultiListingLayoutModel multiModel;
|
||||||
|
|
||||||
public CodeViewerProvider(CodeBrowserPluginInterface plugin, FormatManager formatMgr,
|
public CodeViewerProvider(CodeBrowserPluginInterface plugin, FormatManager formatMgr,
|
||||||
boolean connected) {
|
boolean isConnected) {
|
||||||
super(plugin.getTool(), plugin.getName(), plugin.getName(), CodeViewerActionContext.class);
|
super(plugin.getTool(), "Listing", plugin.getName(), CodeViewerActionContext.class);
|
||||||
|
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.formatMgr = formatMgr;
|
this.formatMgr = formatMgr;
|
||||||
setConnected(connected);
|
setConnected(isConnected);
|
||||||
|
if (!isConnected) {
|
||||||
|
setTransient();
|
||||||
|
}
|
||||||
setHelpLocation(new HelpLocation("CodeBrowserPlugin", "Code_Browser"));
|
setHelpLocation(new HelpLocation("CodeBrowserPlugin", "Code_Browser"));
|
||||||
setDefaultWindowPosition(WindowPosition.RIGHT);
|
setDefaultWindowPosition(WindowPosition.RIGHT);
|
||||||
setIcon(ResourceManager.loadImage("images/Browser.gif"));
|
setIcon(ResourceManager.loadImage("images/Browser.gif"), isConnected);
|
||||||
listingPanel = new ListingPanel(formatMgr);
|
listingPanel = new ListingPanel(formatMgr);
|
||||||
listingPanel.enablePropertyBasedColorModel(true);
|
listingPanel.enablePropertyBasedColorModel(true);
|
||||||
decorationPanel = new ListingPanelContainer(listingPanel, connected);
|
decorationPanel = new ListingPanelContainer(listingPanel, isConnected);
|
||||||
ListingHighlightProvider listingHighlighter =
|
ListingHighlightProvider listingHighlighter =
|
||||||
createListingHighlighter(listingPanel, tool, decorationPanel);
|
createListingHighlighter(listingPanel, tool, decorationPanel);
|
||||||
highlighterAdapter = new ProgramHighlighterProvider(listingHighlighter);
|
highlighterAdapter = new ProgramHighlighterProvider(listingHighlighter);
|
||||||
|
@ -136,7 +141,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||||
setWindowMenuGroup("Listing");
|
setWindowMenuGroup("Listing");
|
||||||
setIntraGroupPosition(WindowPosition.RIGHT);
|
setIntraGroupPosition(WindowPosition.RIGHT);
|
||||||
|
|
||||||
setTitle(connected ? TITLE : "[" + TITLE + "]");
|
setTitle(isConnected ? TITLE : "[" + TITLE + "]");
|
||||||
fieldNavigator = new FieldNavigator(tool, this);
|
fieldNavigator = new FieldNavigator(tool, this);
|
||||||
listingPanel.addButtonPressedListener(fieldNavigator);
|
listingPanel.addButtonPressedListener(fieldNavigator);
|
||||||
addToTool();
|
addToTool();
|
||||||
|
@ -150,6 +155,12 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||||
tool.addPopupListener(this);
|
tool.addPopupListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSnapshot() {
|
||||||
|
// we are a snapshot when we are 'disconnected'
|
||||||
|
return !isConnected();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if this listing is backed by a dynamic data source (e.g., debugger)
|
* @return true if this listing is backed by a dynamic data source (e.g., debugger)
|
||||||
*/
|
*/
|
||||||
|
@ -399,9 +410,9 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateTitle() {
|
void updateTitle() {
|
||||||
String subTitle = program == null ? "" : program.getDomainFile().getName();
|
String subTitle = program == null ? "" : ' ' + program.getDomainFile().getName();
|
||||||
String newTitle = isConnected() ? TITLE : "[" + TITLE + "]";
|
String newTitle = isConnected() ? TITLE : "[" + TITLE + subTitle + "]";
|
||||||
setTitle(newTitle + subTitle);
|
setTitle(newTitle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -882,7 +893,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||||
final ViewerPosition vp = listingPanel.getFieldPanel().getViewerPosition();
|
final ViewerPosition vp = listingPanel.getFieldPanel().getViewerPosition();
|
||||||
// invoke later to give the window manage a chance to create the new window
|
// invoke later to give the window manage a chance to create the new window
|
||||||
// (its done in an invoke later)
|
// (its done in an invoke later)
|
||||||
SwingUtilities.invokeLater(() -> {
|
Swing.runLater(() -> {
|
||||||
newProvider.doSetProgram(program);
|
newProvider.doSetProgram(program);
|
||||||
newProvider.listingPanel.getFieldPanel().setViewerPosition(vp.getIndex(),
|
newProvider.listingPanel.getFieldPanel().setViewerPosition(vp.getIndex(),
|
||||||
vp.getXOffset(), vp.getYOffset());
|
vp.getXOffset(), vp.getYOffset());
|
||||||
|
@ -949,7 +960,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||||
|
|
||||||
class ToggleHeaderAction extends ToggleDockingAction {
|
class ToggleHeaderAction extends ToggleDockingAction {
|
||||||
ToggleHeaderAction() {
|
ToggleHeaderAction() {
|
||||||
super("Toggle Header", CodeViewerProvider.this.getName());
|
super("Toggle Header", plugin.getName());
|
||||||
setEnabled(true);
|
setEnabled(true);
|
||||||
|
|
||||||
setToolBarData(new ToolBarData(LISTING_FORMAT_EXPAND_ICON, "zzz"));
|
setToolBarData(new ToolBarData(LISTING_FORMAT_EXPAND_ICON, "zzz"));
|
||||||
|
@ -960,8 +971,8 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||||
public void actionPerformed(ActionContext context) {
|
public void actionPerformed(ActionContext context) {
|
||||||
boolean show = !listingPanel.isHeaderShowing();
|
boolean show = !listingPanel.isHeaderShowing();
|
||||||
listingPanel.showHeader(show);
|
listingPanel.showHeader(show);
|
||||||
getToolBarData()
|
getToolBarData().setIcon(
|
||||||
.setIcon(show ? LISTING_FORMAT_COLLAPSE_ICON : LISTING_FORMAT_EXPAND_ICON);
|
show ? LISTING_FORMAT_COLLAPSE_ICON : LISTING_FORMAT_EXPAND_ICON);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,11 +64,11 @@ public class ConsoleComponentProvider extends ComponentProviderAdapter
|
||||||
private PrintWriter stdin;
|
private PrintWriter stdin;
|
||||||
private Program currentProgram;
|
private Program currentProgram;
|
||||||
|
|
||||||
public ConsoleComponentProvider(PluginTool tool, String name) {
|
public ConsoleComponentProvider(PluginTool tool, String owner) {
|
||||||
super(tool, "Console", name);
|
super(tool, "Console", owner);
|
||||||
|
|
||||||
setDefaultWindowPosition(WindowPosition.BOTTOM);
|
setDefaultWindowPosition(WindowPosition.BOTTOM);
|
||||||
setHelpLocation(new HelpLocation(getName(), "console"));
|
setHelpLocation(new HelpLocation(owner, owner));
|
||||||
setIcon(ResourceManager.loadImage(CONSOLE_GIF));
|
setIcon(ResourceManager.loadImage(CONSOLE_GIF));
|
||||||
setWindowMenuGroup("Console");
|
setWindowMenuGroup("Console");
|
||||||
setSubTitle("Scripting");
|
setSubTitle("Scripting");
|
||||||
|
@ -94,7 +94,7 @@ public class ConsoleComponentProvider extends ComponentProviderAdapter
|
||||||
|
|
||||||
private void createOptions() {
|
private void createOptions() {
|
||||||
ToolOptions options = tool.getOptions("Console");
|
ToolOptions options = tool.getOptions("Console");
|
||||||
HelpLocation help = new HelpLocation(getName(), "ConsolePlugin");
|
HelpLocation help = new HelpLocation(getOwner(), getOwner());
|
||||||
options.registerOption(FONT_OPTION_LABEL, DEFAULT_FONT, help, FONT_DESCRIPTION);
|
options.registerOption(FONT_OPTION_LABEL, DEFAULT_FONT, help, FONT_DESCRIPTION);
|
||||||
options.setOptionsHelpLocation(help);
|
options.setOptionsHelpLocation(help);
|
||||||
font = options.getFont(FONT_OPTION_LABEL, DEFAULT_FONT);
|
font = options.getFont(FONT_OPTION_LABEL, DEFAULT_FONT);
|
||||||
|
@ -260,7 +260,7 @@ public class ConsoleComponentProvider extends ComponentProviderAdapter
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createActions() {
|
private void createActions() {
|
||||||
clearAction = new DockingAction("Clear Console", getName()) {
|
clearAction = new DockingAction("Clear Console", getOwner()) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionContext context) {
|
public void actionPerformed(ActionContext context) {
|
||||||
|
@ -273,7 +273,7 @@ public class ConsoleComponentProvider extends ComponentProviderAdapter
|
||||||
|
|
||||||
clearAction.setEnabled(true);
|
clearAction.setEnabled(true);
|
||||||
|
|
||||||
scrollAction = new ToggleDockingAction("Scroll Lock", getName()) {
|
scrollAction = new ToggleDockingAction("Scroll Lock", getOwner()) {
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionContext context) {
|
public void actionPerformed(ActionContext context) {
|
||||||
textPane.setScrollLock(isSelected());
|
textPane.setScrollLock(isSelected());
|
||||||
|
|
|
@ -27,6 +27,7 @@ import ghidra.app.plugin.PluginCategoryNames;
|
||||||
import ghidra.framework.plugintool.*;
|
import ghidra.framework.plugintool.*;
|
||||||
import ghidra.framework.plugintool.util.PluginStatus;
|
import ghidra.framework.plugintool.util.PluginStatus;
|
||||||
import ghidra.framework.plugintool.util.ToolConstants;
|
import ghidra.framework.plugintool.util.ToolConstants;
|
||||||
|
import ghidra.util.HelpLocation;
|
||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
@PluginInfo(
|
@PluginInfo(
|
||||||
|
@ -86,6 +87,8 @@ public class ProviderNavigationPlugin extends Plugin {
|
||||||
"xLowInMenuSubGroup"));
|
"xLowInMenuSubGroup"));
|
||||||
previousProviderAction.setKeyBindingData(new KeyBindingData(
|
previousProviderAction.setKeyBindingData(new KeyBindingData(
|
||||||
KeyStroke.getKeyStroke(KeyEvent.VK_F6, DockingUtils.CONTROL_KEY_MODIFIER_MASK)));
|
KeyStroke.getKeyStroke(KeyEvent.VK_F6, DockingUtils.CONTROL_KEY_MODIFIER_MASK)));
|
||||||
|
previousProviderAction.setHelpLocation(
|
||||||
|
new HelpLocation("Navigation", "Navigation_Previous_Provider"));
|
||||||
|
|
||||||
tool.addAction(previousProviderAction);
|
tool.addAction(previousProviderAction);
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,7 @@ public class RegisterManagerProvider extends ComponentProviderAdapter {
|
||||||
super(tool, "Register Manager", owner, ProgramActionContext.class);
|
super(tool, "Register Manager", owner, ProgramActionContext.class);
|
||||||
buildComponent();
|
buildComponent();
|
||||||
|
|
||||||
setHelpLocation(new HelpLocation("RegisterPlugin", "RegisterManager"));
|
setHelpLocation(new HelpLocation("RegisterPlugin", "Register_Manager"));
|
||||||
setIcon(REGISTER_ICON, true);
|
setIcon(REGISTER_ICON, true);
|
||||||
setDefaultWindowPosition(WindowPosition.WINDOW);
|
setDefaultWindowPosition(WindowPosition.WINDOW);
|
||||||
|
|
||||||
|
|
|
@ -170,11 +170,13 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio
|
||||||
assertProviderKeyStroke(newKs);
|
assertProviderKeyStroke(newKs);
|
||||||
assertOptionsKeyStroke(newKs);
|
assertOptionsKeyStroke(newKs);
|
||||||
assertMenuItemHasKeyStroke(newKs);
|
assertMenuItemHasKeyStroke(newKs);
|
||||||
|
assertNoToolbarAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetKeyBinding_ViaOptions_WithToolbarAction() {
|
public void testSetKeyBinding_ViaOptions_WithToolbarAction() {
|
||||||
|
|
||||||
|
setToolbarIcon(ICON);
|
||||||
showProvider();
|
showProvider();
|
||||||
|
|
||||||
KeyStroke newKs = CONTROL_T;
|
KeyStroke newKs = CONTROL_T;
|
||||||
|
@ -183,6 +185,7 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio
|
||||||
assertProviderKeyStroke(newKs);
|
assertProviderKeyStroke(newKs);
|
||||||
assertOptionsKeyStroke(newKs);
|
assertOptionsKeyStroke(newKs);
|
||||||
assertMenuItemHasKeyStroke(newKs);
|
assertMenuItemHasKeyStroke(newKs);
|
||||||
|
assertToolbarAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -306,6 +309,14 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void assertNoToolbarAction() {
|
||||||
|
assertNotNull("No toolbar action found for provider", getToolbarShowProviderAction());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertToolbarAction() {
|
||||||
|
assertNotNull("No toolbar action found for provider", getToolbarShowProviderAction());
|
||||||
|
}
|
||||||
|
|
||||||
private void assertProviderKeyStroke(KeyStroke expectedKs) {
|
private void assertProviderKeyStroke(KeyStroke expectedKs) {
|
||||||
|
|
||||||
DockingActionIf action = getShowProviderAction();
|
DockingActionIf action = getShowProviderAction();
|
||||||
|
|
|
@ -70,9 +70,13 @@ public class ProgramByteViewerComponentProvider extends ByteViewerComponentProvi
|
||||||
public ProgramByteViewerComponentProvider(PluginTool tool, ByteViewerPlugin plugin,
|
public ProgramByteViewerComponentProvider(PluginTool tool, ByteViewerPlugin plugin,
|
||||||
boolean isConnected) {
|
boolean isConnected) {
|
||||||
super(tool, plugin, "Bytes", ByteViewerActionContext.class);
|
super(tool, plugin, "Bytes", ByteViewerActionContext.class);
|
||||||
this.isConnected = isConnected;
|
|
||||||
|
|
||||||
setIcon(ResourceManager.loadImage("images/binaryData.gif"), true);
|
this.isConnected = isConnected;
|
||||||
|
if (!isConnected) {
|
||||||
|
setTransient();
|
||||||
|
}
|
||||||
|
|
||||||
|
setIcon(ResourceManager.loadImage("images/binaryData.gif"), isConnected);
|
||||||
|
|
||||||
decorationComponent = new DecoratorPanel(panel, isConnected);
|
decorationComponent = new DecoratorPanel(panel, isConnected);
|
||||||
clipboardProvider = new ByteViewerClipboardProvider(this, tool);
|
clipboardProvider = new ByteViewerClipboardProvider(this, tool);
|
||||||
|
@ -88,6 +92,12 @@ public class ProgramByteViewerComponentProvider extends ByteViewerComponentProvi
|
||||||
tool.addLocalAction(this, cloneByteViewerAction);
|
tool.addLocalAction(this, cloneByteViewerAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSnapshot() {
|
||||||
|
// we are a snapshot when we are 'disconnected'
|
||||||
|
return !isConnected();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JComponent getComponent() {
|
public JComponent getComponent() {
|
||||||
return decorationComponent;
|
return decorationComponent;
|
||||||
|
@ -132,11 +142,6 @@ public class ProgramByteViewerComponentProvider extends ByteViewerComponentProvi
|
||||||
plugin.closeProvider(this);
|
plugin.closeProvider(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isTransient() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setSelection(ProgramSelection selection) {
|
public void setSelection(ProgramSelection selection) {
|
||||||
setSelection(selection, true);
|
setSelection(selection, true);
|
||||||
|
|
|
@ -28,8 +28,10 @@ import javax.swing.event.TableColumnModelEvent;
|
||||||
|
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
import docking.ActionContext;
|
import docking.*;
|
||||||
import docking.action.DockingActionIf;
|
import docking.action.DockingActionIf;
|
||||||
|
import docking.menu.ToolBarItemManager;
|
||||||
|
import docking.menu.ToolBarManager;
|
||||||
import docking.widgets.EventTrigger;
|
import docking.widgets.EventTrigger;
|
||||||
import docking.widgets.fieldpanel.FieldPanel;
|
import docking.widgets.fieldpanel.FieldPanel;
|
||||||
import docking.widgets.fieldpanel.field.Field;
|
import docking.widgets.fieldpanel.field.Field;
|
||||||
|
@ -168,7 +170,7 @@ public class ByteViewerPlugin1Test extends AbstractGhidraHeadedIntegrationTest {
|
||||||
DataModelInfo info = panel.getDataModelInfo();
|
DataModelInfo info = panel.getDataModelInfo();
|
||||||
String[] names = info.getNames();
|
String[] names = info.getNames();
|
||||||
assertEquals(3, names.length);
|
assertEquals(3, names.length);
|
||||||
Set<String> viewNames = new HashSet<String>(Arrays.asList(names));
|
Set<String> viewNames = new HashSet<>(Arrays.asList(names));
|
||||||
assertTrue(viewNames.contains("Hex"));
|
assertTrue(viewNames.contains("Hex"));
|
||||||
assertTrue(viewNames.contains("Octal"));
|
assertTrue(viewNames.contains("Octal"));
|
||||||
assertTrue(viewNames.contains("Ascii"));
|
assertTrue(viewNames.contains("Ascii"));
|
||||||
|
@ -668,6 +670,64 @@ public class ByteViewerPlugin1Test extends AbstractGhidraHeadedIntegrationTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testShowingSnapshotDoesNotAddMultipleToolbarActions() {
|
||||||
|
|
||||||
|
DockingActionIf cloneAction = getAction(plugin, "ByteViewer Clone");
|
||||||
|
performAction(cloneAction);
|
||||||
|
waitForSwing();
|
||||||
|
assertOnlyOneProviderToolbarAction();
|
||||||
|
|
||||||
|
performAction(cloneAction);
|
||||||
|
waitForSwing();
|
||||||
|
assertOnlyOneProviderToolbarAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Private Methods
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private void assertOnlyOneProviderToolbarAction() {
|
||||||
|
|
||||||
|
DockingWindowManager dwm = tool.getWindowManager();
|
||||||
|
ActionToGuiMapper guiActions =
|
||||||
|
(ActionToGuiMapper) getInstanceField("actionToGuiMapper", dwm);
|
||||||
|
GlobalMenuAndToolBarManager menuManager =
|
||||||
|
(GlobalMenuAndToolBarManager) getInstanceField("menuAndToolBarManager", guiActions);
|
||||||
|
|
||||||
|
Map<WindowNode, WindowActionManager> windowToActionManagerMap =
|
||||||
|
(Map<WindowNode, WindowActionManager>) getInstanceField("windowToActionManagerMap",
|
||||||
|
menuManager);
|
||||||
|
|
||||||
|
ProgramByteViewerComponentProvider provider = plugin.getProvider();
|
||||||
|
DockingActionIf showAction =
|
||||||
|
(DockingActionIf) getInstanceField("showProviderAction", provider);
|
||||||
|
String actionName = showAction.getName();
|
||||||
|
List<DockingActionIf> matches = new ArrayList<>();
|
||||||
|
for (WindowActionManager actionManager : windowToActionManagerMap.values()) {
|
||||||
|
|
||||||
|
ToolBarManager toolbarManager =
|
||||||
|
(ToolBarManager) getInstanceField("toolBarMgr", actionManager);
|
||||||
|
Map<String, List<ToolBarItemManager>> groupToItems =
|
||||||
|
(Map<String, List<ToolBarItemManager>>) getInstanceField("groupToItemsMap",
|
||||||
|
toolbarManager);
|
||||||
|
|
||||||
|
Collection<List<ToolBarItemManager>> values = groupToItems.values();
|
||||||
|
for (List<ToolBarItemManager> list : values) {
|
||||||
|
for (ToolBarItemManager manager : list) {
|
||||||
|
DockingActionIf action = manager.getAction();
|
||||||
|
if (actionName.equals(action.getName())) {
|
||||||
|
matches.add(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals("Should only have 1 action on toolbar to show the provider", 1,
|
||||||
|
matches.size());
|
||||||
|
}
|
||||||
|
|
||||||
private void goToOperand(String addr) {
|
private void goToOperand(String addr) {
|
||||||
goTo(addr(addr), OperandFieldFactory.FIELD_NAME);
|
goTo(addr(addr), OperandFieldFactory.FIELD_NAME);
|
||||||
}
|
}
|
||||||
|
@ -731,11 +791,6 @@ public class ByteViewerPlugin1Test extends AbstractGhidraHeadedIntegrationTest {
|
||||||
assertEquals(expectedColumn, location.getCol());
|
assertEquals(expectedColumn, location.getCol());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void goTo(String addr) {
|
|
||||||
GoToService goToService = tool.getService(GoToService.class);
|
|
||||||
goToService.goTo(addr(addr));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void goToByte(String addr) {
|
private void goToByte(String addr) {
|
||||||
goToByte(addr(addr));
|
goToByte(addr(addr));
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,9 +124,10 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||||
|
|
||||||
public DecompilerProvider(DecompilePlugin plugin, boolean isConnected) {
|
public DecompilerProvider(DecompilePlugin plugin, boolean isConnected) {
|
||||||
super(plugin.getTool(), "Decompiler", plugin.getName(), DecompilerActionContext.class);
|
super(plugin.getTool(), "Decompiler", plugin.getName(), DecompilerActionContext.class);
|
||||||
this.plugin = plugin;
|
|
||||||
|
|
||||||
clipboardProvider = new DecompilerClipboardProvider(plugin, this);
|
this.plugin = plugin;
|
||||||
|
this.clipboardProvider = new DecompilerClipboardProvider(plugin, this);
|
||||||
|
|
||||||
setConnected(isConnected);
|
setConnected(isConnected);
|
||||||
|
|
||||||
decompilerOptions = new DecompileOptions();
|
decompilerOptions = new DecompileOptions();
|
||||||
|
@ -137,10 +138,17 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||||
decompilerPanel.setHighlightController(highlightController);
|
decompilerPanel.setHighlightController(highlightController);
|
||||||
decorationPanel = new DecoratorPanel(decompilerPanel, isConnected);
|
decorationPanel = new DecoratorPanel(decompilerPanel, isConnected);
|
||||||
|
|
||||||
|
if (!isConnected) {
|
||||||
|
setTransient();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setDefaultKeyBinding(
|
||||||
|
new KeyBindingData(KeyEvent.VK_E, DockingUtils.CONTROL_KEY_MODIFIER_MASK));
|
||||||
|
}
|
||||||
|
|
||||||
|
setIcon(C_SOURCE_ICON, isConnected);
|
||||||
setTitle("Decompile");
|
setTitle("Decompile");
|
||||||
setIcon(C_SOURCE_ICON, true);
|
|
||||||
setDefaultKeyBinding(
|
|
||||||
new KeyBindingData(KeyEvent.VK_E, DockingUtils.CONTROL_KEY_MODIFIER_MASK));
|
|
||||||
setWindowMenuGroup("Decompile");
|
setWindowMenuGroup("Decompile");
|
||||||
setDefaultWindowPosition(WindowPosition.RIGHT);
|
setDefaultWindowPosition(WindowPosition.RIGHT);
|
||||||
createActions(isConnected);
|
createActions(isConnected);
|
||||||
|
@ -155,6 +163,13 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Component Provider methods
|
// Component Provider methods
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSnapshot() {
|
||||||
|
// we are a snapshot when we are 'disconnected'
|
||||||
|
return !isConnected();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void closeComponent() {
|
public void closeComponent() {
|
||||||
controller.clear();
|
controller.clear();
|
||||||
|
|
|
@ -48,8 +48,7 @@ import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
import ghidra.program.model.symbol.*;
|
import ghidra.program.model.symbol.*;
|
||||||
import ghidra.program.util.*;
|
import ghidra.program.util.*;
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.*;
|
||||||
import ghidra.util.SystemUtilities;
|
|
||||||
import ghidra.util.datastruct.WeakDataStructureFactory;
|
import ghidra.util.datastruct.WeakDataStructureFactory;
|
||||||
import ghidra.util.datastruct.WeakSet;
|
import ghidra.util.datastruct.WeakSet;
|
||||||
import ghidra.util.exception.AssertException;
|
import ghidra.util.exception.AssertException;
|
||||||
|
@ -104,13 +103,16 @@ public class FGProvider extends VisualGraphComponentProvider<FGVertex, FGEdge, F
|
||||||
controller = new FGController(this, plugin);
|
controller = new FGController(this, plugin);
|
||||||
|
|
||||||
setConnected(isConnected);
|
setConnected(isConnected);
|
||||||
|
if (!isConnected) {
|
||||||
|
setTransient();
|
||||||
|
}
|
||||||
|
|
||||||
decorationPanel = new DecoratorPanel(controller.getViewComponent(), isConnected);
|
decorationPanel = new DecoratorPanel(controller.getViewComponent(), isConnected);
|
||||||
setWindowMenuGroup(FunctionGraphPlugin.FUNCTION_GRAPH_NAME);
|
setWindowMenuGroup(FunctionGraphPlugin.FUNCTION_GRAPH_NAME);
|
||||||
setWindowGroup(FunctionGraphPlugin.FUNCTION_GRAPH_NAME);
|
setWindowGroup(FunctionGraphPlugin.FUNCTION_GRAPH_NAME);
|
||||||
setDefaultWindowPosition(WindowPosition.WINDOW);
|
setDefaultWindowPosition(WindowPosition.WINDOW);
|
||||||
|
|
||||||
setIcon(FunctionGraphPlugin.ICON, true);
|
setIcon(FunctionGraphPlugin.ICON, isConnected);
|
||||||
setHelpLocation(new HelpLocation("FunctionGraphPlugin", "FunctionGraphPlugin"));
|
setHelpLocation(new HelpLocation("FunctionGraphPlugin", "FunctionGraphPlugin"));
|
||||||
|
|
||||||
addToTool();
|
addToTool();
|
||||||
|
@ -129,6 +131,12 @@ public class FGProvider extends VisualGraphComponentProvider<FGVertex, FGEdge, F
|
||||||
setClipboardService(service);
|
setClipboardService(service);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSnapshot() {
|
||||||
|
// we are a snapshot when we are 'disconnected'
|
||||||
|
return !isConnected();
|
||||||
|
}
|
||||||
|
|
||||||
public void setClipboardService(ClipboardService service) {
|
public void setClipboardService(ClipboardService service) {
|
||||||
clipboardService = service;
|
clipboardService = service;
|
||||||
if (clipboardService != null) {
|
if (clipboardService != null) {
|
||||||
|
@ -204,7 +212,7 @@ public class FGProvider extends VisualGraphComponentProvider<FGVertex, FGEdge, F
|
||||||
|
|
||||||
void cloneWindow() {
|
void cloneWindow() {
|
||||||
FGProvider newProvider = plugin.createNewDisconnectedProvider();
|
FGProvider newProvider = plugin.createNewDisconnectedProvider();
|
||||||
SystemUtilities.runSwingLater(() -> {
|
Swing.runLater(() -> {
|
||||||
newProvider.doSetProgram(currentProgram);
|
newProvider.doSetProgram(currentProgram);
|
||||||
|
|
||||||
FGData currentData = controller.getFunctionGraphData();
|
FGData currentData = controller.getFunctionGraphData();
|
||||||
|
|
|
@ -326,7 +326,7 @@ class ComponentNode extends Node {
|
||||||
DockingTabRenderer tabRenderer) {
|
DockingTabRenderer tabRenderer) {
|
||||||
|
|
||||||
final ComponentProvider provider = placeholder.getProvider();
|
final ComponentProvider provider = placeholder.getProvider();
|
||||||
if (!provider.isTransient()) {
|
if (!provider.isTransient() || provider.isSnapshot()) {
|
||||||
return; // don't muck with the title of 'real' providers--only transients, like search
|
return; // don't muck with the title of 'real' providers--only transients, like search
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -449,6 +449,16 @@ public class ComponentPlaceholder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void removeAllActions() {
|
||||||
|
if (comp != null) {
|
||||||
|
for (DockingActionIf action : actions) {
|
||||||
|
comp.actionRemoved(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
actions.clear();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes an action from this component
|
* Removes an action from this component
|
||||||
* @param action the action to be removed.
|
* @param action the action to be removed.
|
||||||
|
|
|
@ -406,6 +406,10 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
|
||||||
this.helpLocation = helpLocation;
|
this.helpLocation = helpLocation;
|
||||||
HelpService helpService = DockingWindowManager.getHelpService();
|
HelpService helpService = DockingWindowManager.getHelpService();
|
||||||
helpService.registerHelp(this, helpLocation);
|
helpService.registerHelp(this, helpLocation);
|
||||||
|
|
||||||
|
if (showProviderAction != null) {
|
||||||
|
showProviderAction.setHelpLocation(helpLocation);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -564,7 +568,17 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
|
||||||
* @return true if transient
|
* @return true if transient
|
||||||
*/
|
*/
|
||||||
public boolean isTransient() {
|
public boolean isTransient() {
|
||||||
return isTransient;
|
return isTransient || isSnapshot();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A special marker that indicates this provider is a snapshot of a primary provider,
|
||||||
|
* somewhat like a picture of the primary provider.
|
||||||
|
*
|
||||||
|
* @return true if a snapshot
|
||||||
|
*/
|
||||||
|
public boolean isSnapshot() {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -766,13 +780,17 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
|
||||||
setToolBarData(new ToolBarData(icon, TOOLBAR_GROUP));
|
setToolBarData(new ToolBarData(icon, TOOLBAR_GROUP));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (defaultKeyBindingData != null) {
|
if (supportsKeyBindings && defaultKeyBindingData != null) {
|
||||||
// this action itself is not 'key binding managed', but the system *will* use
|
// this action itself is not 'key binding managed', but the system *will* use
|
||||||
// any key binding value we set when connecting 'shared' actions
|
// any key binding value we set when connecting 'shared' actions
|
||||||
setKeyBindingData(defaultKeyBindingData);
|
setKeyBindingData(defaultKeyBindingData);
|
||||||
}
|
}
|
||||||
|
|
||||||
setDescription("Display " + name);
|
setDescription("Display " + name);
|
||||||
|
HelpLocation providerHelp = ComponentProvider.this.getHelpLocation();
|
||||||
|
if (providerHelp != null) {
|
||||||
|
setHelpLocation(providerHelp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -26,6 +26,7 @@ import java.util.Map.Entry;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
|
import org.apache.commons.collections4.map.LazyMap;
|
||||||
import org.jdom.Element;
|
import org.jdom.Element;
|
||||||
|
|
||||||
import docking.action.DockingActionIf;
|
import docking.action.DockingActionIf;
|
||||||
|
@ -996,11 +997,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||||
}
|
}
|
||||||
|
|
||||||
private void disposePlaceholder(ComponentPlaceholder placeholder, boolean keepAround) {
|
private void disposePlaceholder(ComponentPlaceholder placeholder, boolean keepAround) {
|
||||||
Iterator<DockingActionIf> iter = placeholder.getActions();
|
placeholder.removeAllActions();
|
||||||
while (iter.hasNext()) {
|
|
||||||
DockingActionIf action = iter.next();
|
|
||||||
placeholder.removeAction(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
ComponentNode node = placeholder.getNode();
|
ComponentNode node = placeholder.getNode();
|
||||||
if (node == null) {
|
if (node == null) {
|
||||||
|
@ -1092,8 +1089,10 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||||
|
|
||||||
tool.getToolActions().removeActions(DOCKING_WINDOWS_OWNER);
|
tool.getToolActions().removeActions(DOCKING_WINDOWS_OWNER);
|
||||||
|
|
||||||
Map<String, List<ComponentPlaceholder>> permanentMap = new HashMap<>();
|
Map<String, List<ComponentPlaceholder>> permanentMap =
|
||||||
Map<String, List<ComponentPlaceholder>> transientMap = new HashMap<>();
|
LazyMap.lazyMap(new HashMap<>(), menuName -> new ArrayList<>());
|
||||||
|
Map<String, List<ComponentPlaceholder>> transientMap =
|
||||||
|
LazyMap.lazyMap(new HashMap<>(), menuName -> new ArrayList<>());
|
||||||
|
|
||||||
Map<ComponentProvider, ComponentPlaceholder> map =
|
Map<ComponentProvider, ComponentPlaceholder> map =
|
||||||
placeholderManager.getActiveProvidersToPlaceholders();
|
placeholderManager.getActiveProvidersToPlaceholders();
|
||||||
|
@ -1103,18 +1102,18 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||||
ComponentPlaceholder placeholder = entry.getValue();
|
ComponentPlaceholder placeholder = entry.getValue();
|
||||||
|
|
||||||
String subMenuName = provider.getWindowSubMenuName();
|
String subMenuName = provider.getWindowSubMenuName();
|
||||||
if (provider.isTransient()) {
|
if (provider.isTransient() && !provider.isSnapshot()) {
|
||||||
addToMap(transientMap, subMenuName, placeholder);
|
transientMap.get(subMenuName).add(placeholder);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
addToMap(permanentMap, subMenuName, placeholder);
|
permanentMap.get(subMenuName).add(placeholder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
promoteSingleMenuGroups(permanentMap);
|
promoteSingleMenuGroups(permanentMap);
|
||||||
promoteSingleMenuGroups(transientMap);
|
promoteSingleMenuGroups(transientMap);
|
||||||
|
|
||||||
createActions(transientMap, true);
|
createActions(transientMap);
|
||||||
createActions(permanentMap, false);
|
createActions(permanentMap);
|
||||||
createWindowActions();
|
createWindowActions();
|
||||||
|
|
||||||
actionToGuiMapper.update();
|
actionToGuiMapper.update();
|
||||||
|
@ -1144,14 +1143,17 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createActions(Map<String, List<ComponentPlaceholder>> map, boolean isTransient) {
|
private void createActions(Map<String, List<ComponentPlaceholder>> map) {
|
||||||
List<ShowComponentAction> actionList = new ArrayList<>();
|
List<ShowComponentAction> actionList = new ArrayList<>();
|
||||||
for (String subMenuName : map.keySet()) {
|
for (String subMenuName : map.keySet()) {
|
||||||
List<ComponentPlaceholder> placeholders = map.get(subMenuName);
|
List<ComponentPlaceholder> placeholders = map.get(subMenuName);
|
||||||
for (ComponentPlaceholder placeholder : placeholders) {
|
for (ComponentPlaceholder placeholder : placeholders) {
|
||||||
|
ComponentProvider provider = placeholder.getProvider();
|
||||||
|
boolean isTransient = provider.isTransient();
|
||||||
actionList.add(
|
actionList.add(
|
||||||
new ShowComponentAction(this, placeholder, subMenuName, isTransient));
|
new ShowComponentAction(this, placeholder, subMenuName, isTransient));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subMenuName != null) {
|
if (subMenuName != null) {
|
||||||
// add an 'add all' action for the sub-menu
|
// add an 'add all' action for the sub-menu
|
||||||
actionList.add(new ShowAllComponentsAction(this, placeholders, subMenuName));
|
actionList.add(new ShowAllComponentsAction(this, placeholders, subMenuName));
|
||||||
|
@ -1165,28 +1167,21 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void promoteSingleMenuGroups(Map<String, List<ComponentPlaceholder>> map) {
|
private void promoteSingleMenuGroups(Map<String, List<ComponentPlaceholder>> lazyMap) {
|
||||||
List<String> lists = new ArrayList<>(map.keySet());
|
List<String> lists = new ArrayList<>(lazyMap.keySet());
|
||||||
for (String key : lists) {
|
for (String key : lists) {
|
||||||
List<ComponentPlaceholder> list = map.get(key);
|
if (key == null) {
|
||||||
if (key != null && list.size() == 1) {
|
continue;
|
||||||
addToMap(map, null, list.get(0));
|
}
|
||||||
map.remove(key);
|
|
||||||
|
List<ComponentPlaceholder> list = lazyMap.get(key);
|
||||||
|
if (list.size() == 1) {
|
||||||
|
lazyMap.get(null /*submenu name*/).add(list.get(0));
|
||||||
|
lazyMap.remove(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addToMap(Map<String, List<ComponentPlaceholder>> map, String menuGroup,
|
|
||||||
ComponentPlaceholder placeholder) {
|
|
||||||
|
|
||||||
List<ComponentPlaceholder> list = map.get(menuGroup);
|
|
||||||
if (list == null) {
|
|
||||||
list = new ArrayList<>();
|
|
||||||
map.put(menuGroup, list);
|
|
||||||
}
|
|
||||||
list.add(placeholder);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createWindowActions() {
|
private void createWindowActions() {
|
||||||
List<DetachedWindowNode> windows = root.getDetachedWindows();
|
List<DetachedWindowNode> windows = root.getDetachedWindows();
|
||||||
List<ShowWindowAction> actions = new ArrayList<>();
|
List<ShowWindowAction> actions = new ArrayList<>();
|
||||||
|
|
|
@ -52,8 +52,7 @@ public class KeyBindingAction extends DockingAction {
|
||||||
if (!action.getKeyBindingType().supportsKeyBindings()) {
|
if (!action.getKeyBindingType().supportsKeyBindings()) {
|
||||||
Component parent = windowManager.getActiveComponent();
|
Component parent = windowManager.getActiveComponent();
|
||||||
Msg.showInfo(getClass(), parent, "Unable to Set Keybinding",
|
Msg.showInfo(getClass(), parent, "Unable to Set Keybinding",
|
||||||
"Action \"" + getActionName(action) + "\" is not keybinding managed and thus a " +
|
"Action \"" + getActionName(action) + "\" does not support key bindings");
|
||||||
"keybinding cannot be set.");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -446,9 +446,17 @@ public class KeyBindingUtils {
|
||||||
public static void assertSameDefaultKeyBindings(DockingActionIf newAction,
|
public static void assertSameDefaultKeyBindings(DockingActionIf newAction,
|
||||||
Collection<DockingActionIf> existingActions) {
|
Collection<DockingActionIf> existingActions) {
|
||||||
|
|
||||||
|
if (!newAction.getKeyBindingType().supportsKeyBindings()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
KeyBindingData newDefaultBinding = newAction.getDefaultKeyBindingData();
|
KeyBindingData newDefaultBinding = newAction.getDefaultKeyBindingData();
|
||||||
KeyStroke defaultKs = getKeyStroke(newDefaultBinding);
|
KeyStroke defaultKs = getKeyStroke(newDefaultBinding);
|
||||||
for (DockingActionIf action : existingActions) {
|
for (DockingActionIf action : existingActions) {
|
||||||
|
if (!action.getKeyBindingType().supportsKeyBindings()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
KeyBindingData existingDefaultBinding = action.getDefaultKeyBindingData();
|
KeyBindingData existingDefaultBinding = action.getDefaultKeyBindingData();
|
||||||
KeyStroke existingKs = getKeyStroke(existingDefaultBinding);
|
KeyStroke existingKs = getKeyStroke(existingDefaultBinding);
|
||||||
if (!Objects.equals(defaultKs, existingKs)) {
|
if (!Objects.equals(defaultKs, existingKs)) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue