GT-2960 - Docking Actions - Consolidated the multiple methods of adding

dynamic popup actions
This commit is contained in:
dragonmacher 2019-07-29 18:20:00 -04:00
parent 785eb5a2a5
commit ab2a390fca
37 changed files with 375 additions and 341 deletions

View file

@ -21,6 +21,7 @@ import java.util.*;
import javax.swing.JFrame;
import docking.action.DockingActionIf;
import docking.actions.PopupActionProvider;
import docking.actions.ToolActions;
import ghidra.framework.options.ToolOptions;
import ghidra.util.Swing;
@ -123,6 +124,16 @@ public abstract class AbstractDockingTool implements DockingTool {
return toolActions.getAllActions();
}
@Override
public void addPopupActionProvider(PopupActionProvider provider) {
winMgr.addPopupActionProvider(provider);
}
@Override
public void removePopupActionProvider(PopupActionProvider provider) {
winMgr.removePopupActionProvider(provider);
}
@Override
public Set<DockingActionIf> getDockingActionsByOwnerName(String owner) {
return toolActions.getActions(owner);

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,7 +15,12 @@
*/
package docking;
import java.awt.Component;
/**
* A listener interface to know when a component has been
* made {@link Component#isDisplayable() displayable}
*/
public interface ComponentLoadedListener {
public void componentLoaded(DockingWindowManager windowManager);
}

View file

@ -32,7 +32,7 @@ import docking.actions.ActionAdapter;
import docking.actions.KeyBindingUtils;
import docking.event.mouse.GMouseListenerAdapter;
import docking.menu.DockingToolbarButton;
import docking.util.*;
import docking.util.AnimationUtils;
import docking.widgets.label.GDHtmlLabel;
import ghidra.util.*;
import ghidra.util.exception.AssertException;
@ -43,9 +43,8 @@ import utility.function.Callback;
* Base class used for creating dialogs in Ghidra. Subclass this to create a dialog provider that has
* all the gui elements to appear in the dialog, then use tool.showDialog() to display your dialog.
*/
public class DialogComponentProvider
implements TaskListener, StatusListener, ActionContextProvider {
implements ActionContextProvider, StatusListener, TaskListener {
private static final Color WARNING_COLOR = new Color(0xff9900);
@ -1369,5 +1368,4 @@ public class DialogComponentProvider
}
}
}

View file

@ -73,7 +73,7 @@ public class DialogComponentProviderPopupActionManager {
MenuGroupMap menuGroupMap = actionManager.getMenuGroupMap();
MenuManager menuMgr =
new MenuManager("Popup", '\0', null, true, popupMenuHandler, menuGroupMap);
populatePopupMenuActions(actionContext, menuMgr);
populatePopupMenuActions(dwm, menuMgr, actionContext);
if (menuMgr.isEmpty()) {
return;
}
@ -85,7 +85,8 @@ public class DialogComponentProviderPopupActionManager {
popupMenu.show(c, e.getX(), e.getY());
}
private void populatePopupMenuActions(ActionContext actionContext, MenuManager menuMgr) {
private void populatePopupMenuActions(DockingWindowManager dwm, MenuManager menuMgr,
ActionContext actionContext) {
Iterator<DockingActionIf> iter = popupActions.iterator();
while (iter.hasNext()) {
@ -112,6 +113,18 @@ public class DialogComponentProviderPopupActionManager {
}
}
}
List<DockingActionIf> tempActions = dwm.getTemporaryPopupActions(actionContext);
if (tempActions != null) {
for (DockingActionIf action : tempActions) {
MenuData popupMenuData = action.getPopupMenuData();
if (popupMenuData != null && action.isValidContext(actionContext) &&
action.isAddToPopup(actionContext)) {
action.setEnabled(action.isEnabledForContext(actionContext));
menuMgr.addAction(action);
}
}
}
}
//==================================================================================================

View file

@ -1,44 +0,0 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* 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;
import java.util.List;
import docking.action.DockingActionIf;
/**
* Listener interface for the object to be notified when the user closes the
* docking windows manager or initiates a popup menu.
*/
public interface DockWinListener {
/**
* Notification triggered when the user presses the "x" button in the main tool frame.
* Typical reaction is to dispose the dockingWindowManger and/or exit.
*/
void close();
/**
* Provides notification when a popup menu is about to be displayed
* and permits a list of temporary actions to be returned. Only
* those actions which have a suitable popup menu path will be
* considered.
* @param context the ActionContext
* @return list of temporary actions.
*/
List<DockingActionIf> getPopupActions(ActionContext context);
}

View file

@ -22,6 +22,7 @@ import javax.swing.ImageIcon;
import docking.action.DockingActionIf;
import docking.actions.DockingToolActions;
import docking.actions.PopupActionProvider;
import ghidra.framework.options.ToolOptions;
/**
@ -129,6 +130,19 @@ public interface DockingTool {
*/
public void removeLocalAction(ComponentProvider componentProvider, DockingActionIf action);
/**
* Adds the given popup action provider to this tool. This provider will be called each
* time the popup menu is about to be shown.
* @param provider the provider
*/
public void addPopupActionProvider(PopupActionProvider provider);
/**
* Removes the given popup action provider
* @param provider the provider
*/
public void removePopupActionProvider(PopupActionProvider provider);
/**
* Return a set of all actions in the tool.
*
@ -273,4 +287,12 @@ public interface DockingTool {
* @return the action manager
*/
public DockingToolActions getToolActions();
/**
* Suggests the tool to attempt to close(). This will be as though the user
* selected the close menu option on the tool or hit the closeWindow x button in
* the upper corner (Windows systems).
*/
public void close();
}

View file

@ -30,8 +30,7 @@ import org.apache.commons.collections4.map.LazyMap;
import org.jdom.Element;
import docking.action.DockingActionIf;
import docking.actions.DockingToolActions;
import docking.actions.ToolActions;
import docking.actions.*;
import docking.help.HelpService;
import generic.util.WindowUtilities;
import ghidra.framework.OperatingSystem;
@ -87,9 +86,10 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
private Map<String, ComponentProvider> providerNameCache = new HashMap<>();
private Map<String, PreferenceState> preferenceStateMap = new HashMap<>();
private DockWinListener docListener;
private ActionToGuiMapper actionToGuiMapper;
private WeakSet<PopupActionProvider> popupActionProviders =
WeakDataStructureFactory.createSingleThreadAccessWeakSet();
private WeakSet<DockingContextListener> contextListeners =
WeakDataStructureFactory.createSingleThreadAccessWeakSet();
@ -108,10 +108,9 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
* Constructs a new DockingWindowManager
* @param tool the tool
* @param images the images to use for windows in this window manager
* @param docListener the listener to be notified when the user closes the manager
*/
public DockingWindowManager(DockingTool tool, List<Image> images, DockWinListener docListener) {
this(tool, images, docListener, false, true, true, null);
public DockingWindowManager(DockingTool tool, List<Image> images) {
this(tool, images, false, true, true, null);
}
/**
@ -119,20 +118,18 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
*
* @param tool the tool
* @param images the list of icons to set on the window
* @param docListener the listener to be notified when the user closes the manager
* @param modal if true then the root window will be a modal dialog instead of a frame
* @param isDocking true for normal operation, false to suppress docking support(removes
* component headers and window menu)
* @param hasStatusBar if true a status bar will be created for the main window
* @param factory the drop target factory
*/
public DockingWindowManager(DockingTool tool, List<Image> images, DockWinListener docListener,
boolean modal, boolean isDocking, boolean hasStatusBar, DropTargetFactory factory) {
public DockingWindowManager(DockingTool tool, List<Image> images, boolean modal,
boolean isDocking, boolean hasStatusBar, DropTargetFactory factory) {
KeyBindingOverrideKeyEventDispatcher.install();
this.tool = tool;
this.docListener = docListener;
this.isDocking = isDocking;
this.hasStatusBar = hasStatusBar;
if (images == null) {
@ -174,10 +171,6 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
return helpService;
}
List<DockingActionIf> getTemporaryPopupActions(ActionContext context) {
return docListener.getPopupActions(context);
}
private static synchronized void addInstance(DockingWindowManager winMgr) {
instanceList.add(winMgr);
}
@ -1084,7 +1077,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
* the main window frame.
*/
void close() {
docListener.close();
tool.close();
}
boolean isDocking() {
@ -2099,6 +2092,44 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
actionToGuiMapper.contextChanged(placeholder);
}
/**
* Adds the given popup action provider to this tool. This provider will be called each
* time the popup menu is about to be shown.
* @param provider the provider
*/
public void addPopupActionProvider(PopupActionProvider provider) {
popupActionProviders.add(provider);
}
/**
* Removes the given popup action provider
* @param provider the provider
*/
public void removePopupActionProvider(PopupActionProvider provider) {
popupActionProviders.remove(provider);
}
/**
* Returns a list of temporary popup actions to be returned. Only those actions which have
* a suitable popup menu path will be considered. This mechanism allows clients to
* add transient actions to be added to the tool without the accompanying management overhead.
*
* @param context the ActionContext
* @return list of temporary actions
* @see #addPopupActionProvider(PopupActionProvider)
*/
List<DockingActionIf> getTemporaryPopupActions(ActionContext context) {
List<DockingActionIf> actionList = new ArrayList<>();
for (PopupActionProvider pl : popupActionProviders) {
List<DockingActionIf> actions = pl.getPopupActions(context);
if (actions != null) {
actionList.addAll(actions);
}
}
return actionList;
}
public void addContextListener(DockingContextListener listener) {
contextListeners.add(listener);
}
@ -2122,12 +2153,9 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
* @param component the component that will be parented in a docking window system.
* @param listener the listener to be notified the component was parented.
*/
public static void registerComponentLoadedListener(final Component component,
final ComponentLoadedListener listener) {
public static void registerComponentLoadedListener(Component component,
ComponentLoadedListener listener) {
// We want to load our state after the column model is loaded. We are using this
// listener to know when the table has been added to the component hierarchy, as its
// model has been loaded by then.
component.addHierarchyListener(new HierarchyListener() {
@Override
public void hierarchyChanged(HierarchyEvent e) {

View file

@ -17,6 +17,8 @@ package docking.action;
import java.util.List;
import docking.DockingTool;
/**
* An interface for objects (really Components) to implement that signals they provide actions
* for the Docking environment. This interface will be called when the implementor is the source
@ -25,8 +27,12 @@ import java.util.List;
* As an example, a JTable that wishes to provide popup menu actions can implement this interface.
* When the user right-clicks on said table, then Docking system will ask this object for its
* actions. Further, in this example, the actions given will be inserted into the popup menu
* that is shown.
* that is shown.
*
* @deprecated use {@link DockingTool}
*/
// Note: this API is not likely used by forward-facing clients and can be removed in the next release
@Deprecated(since = "9.1", forRemoval = true)
public interface DockingActionProviderIf {
/**

View file

@ -0,0 +1,47 @@
/* ###
* 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.actions;
import java.util.List;
import docking.ActionContext;
import docking.DockingTool;
import docking.action.DockingActionIf;
/**
* Provides notification when the popup action menu is displayed. This interface allows
* temporary/transient actions (those not registered with the tool via
* {@link DockingTool#addAction(DockingActionIf)}) to be used in the popup context menu.
*
* <p>
* Most clients will register actions directly with the tool. However, clients that have numerous
* actions that vary greatly with the context can use this method to only create those actions
* on demand as the popup is about to be shown, and only if their context is active. This
* mechanism can reduce the tool's action management overhead.
*/
public interface PopupActionProvider {
/**
* Provides notification that the popup menu is about to be displayed and allows a set of
* temporary actions to be included in the popup menu. Actions returned will be
* included in the menu if they have a valid popup menu path and respond true to the
* {@link DockingActionIf#isValidContext(ActionContext)} call.
*
* @param context the ActionContext
* @return list of temporary popup actions; return null if there are no popup actions
*/
public List<DockingActionIf> getPopupActions(ActionContext context);
}

View file

@ -32,6 +32,7 @@ import javax.swing.table.*;
import docking.*;
import docking.action.*;
import docking.actions.KeyBindingUtils;
import docking.actions.PopupActionProvider;
import docking.widgets.OptionDialog;
import docking.widgets.dialogs.SettingsDialog;
import docking.widgets.filechooser.GhidraFileChooser;
@ -69,7 +70,7 @@ import resources.ResourceManager;
*
* @see GTableFilterPanel
*/
public class GTable extends JTable implements KeyStrokeConsumer, DockingActionProviderIf {
public class GTable extends JTable implements KeyStrokeConsumer, PopupActionProvider {
private static final String LAST_EXPORT_FILE = "LAST_EXPORT_DIR";
@ -510,20 +511,8 @@ public class GTable extends JTable implements KeyStrokeConsumer, DockingActionPr
}
@Override
public List<DockingActionIf> getDockingActions() {
return getDefaultDockingActions();
}
public List<DockingActionIf> getPopupActions(ActionContext context) {
/**
* Returns the default actions of this table. Normally, the Docking Windows systems uses
* {@link #getDockingActions()} to get the correct actions to show. However,
* there are some cases where clients override what appears when you click on a table (such
* as in {@link DialogComponentProvider}s. For those clients that are creating their own
* action building, they need a way to get the default actions, hence this method.
*
* @return the default actions
*/
public List<DockingActionIf> getDefaultDockingActions() {
// we want these top-level groups to all appear together, with no separator
DockingWindowManager dwm = DockingWindowManager.getInstance(this);
dwm.setMenuGroup(new String[] { "Copy" }, actionMenuGroup, "1");
@ -590,6 +579,10 @@ public class GTable extends JTable implements KeyStrokeConsumer, DockingActionPr
createPopupActions();
initializeRowHeight();
DockingWindowManager.registerComponentLoadedListener(this, dwm -> {
dwm.getTool().addPopupActionProvider(this);
});
}
private void initializeHeader(JTableHeader header) {
@ -1490,5 +1483,4 @@ public class GTable extends JTable implements KeyStrokeConsumer, DockingActionPr
// ignored
}
}
}