mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
GT-3044 - Table Actions - test and review fixes
This commit is contained in:
parent
28f6500039
commit
e9c0921cfe
36 changed files with 920 additions and 748 deletions
|
@ -25,7 +25,11 @@ import docking.action.DockingActionIf;
|
|||
* {@link DockingActionIf}s for them to decide if they are enabled for a given user action. User
|
||||
* actions are toolbar button presses, menu bar item presses and popup menu item presses. As
|
||||
* the user changes focus in the system, all actions are queried with the current context. Thus,
|
||||
* toolbar buttons and menu items will enable and disable as the user interacts with the system.
|
||||
* <b>toolbar buttons and menu items will enable and disable as the user interacts with the system.
|
||||
* Further, popup menu items will not be added to popup menus when they report false for
|
||||
* {@link DockingActionIf#isAddToPopup(ActionContext)}; they will appear in the popup, but be
|
||||
* disabled if they report <tt>true</tt> for the above call, but <tt>false</tt> for
|
||||
* {@link DockingActionIf#isEnabledForContext(ActionContext)}.</b>
|
||||
* When the user executes an action, the current context will be passed to the backing
|
||||
* {@link DockingActionIf}. Ultimately, context serves to control action enablement and to
|
||||
* allow plugins to share state with actions without having to store that state information
|
||||
|
|
|
@ -126,6 +126,10 @@ public class ActionToGuiMapper {
|
|||
menuAndToolBarManager.contextChanged(placeHolder);
|
||||
}
|
||||
|
||||
PopupActionManager getPopupActionManager() {
|
||||
return popupActionManager;
|
||||
}
|
||||
|
||||
public MenuGroupMap getMenuGroupMap() {
|
||||
return menuGroupMap;
|
||||
}
|
||||
|
|
|
@ -396,19 +396,35 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
|
|||
return createContext(c, null);
|
||||
}
|
||||
|
||||
// TODO
|
||||
/**
|
||||
* A default method for creating an action context for this provider
|
||||
* @return the new context
|
||||
*/
|
||||
protected ActionContext createContext() {
|
||||
return new ActionContext(this);
|
||||
}
|
||||
|
||||
// TODO
|
||||
protected ActionContext createContext(Object payload) {
|
||||
return new ActionContext(this).setContextObject(payload);
|
||||
/**
|
||||
* A default method for creating an action context for this provider, using the given
|
||||
* {@link ActionContext#getContextObject() context object}
|
||||
*
|
||||
* @param contextObject the provider-specific context object
|
||||
* @return the new context
|
||||
*/
|
||||
protected ActionContext createContext(Object contextObject) {
|
||||
return new ActionContext(this).setContextObject(contextObject);
|
||||
}
|
||||
|
||||
// TODO
|
||||
protected ActionContext createContext(Component source, Object payload) {
|
||||
return new ActionContext(this, source).setContextObject(payload);
|
||||
/**
|
||||
* A default method for creating an action context for this provider, using the given
|
||||
* {@link ActionContext#getContextObject() context object} and component
|
||||
*
|
||||
* @param sourceComponent the component that is the target of the context being created
|
||||
* @param contextObject the provider-specific context object
|
||||
* @return the new context
|
||||
*/
|
||||
protected ActionContext createContext(Component sourceComponent, Object contextObject) {
|
||||
return new ActionContext(this, sourceComponent).setContextObject(contextObject);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -31,6 +31,7 @@ import docking.action.DockingActionIf;
|
|||
import docking.actions.ActionAdapter;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
import docking.event.mouse.GMouseListenerAdapter;
|
||||
import docking.help.HelpService;
|
||||
import docking.menu.DockingToolbarButton;
|
||||
import docking.util.AnimationUtils;
|
||||
import docking.widgets.label.GDHtmlLabel;
|
||||
|
@ -990,6 +991,15 @@ public class DialogComponentProvider
|
|||
DockingWindowManager.setHelpLocation(rootPanel, helpLocation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the help location for this dialog
|
||||
* @return the help location
|
||||
*/
|
||||
public HelpLocation getHelpLocatdion() {
|
||||
HelpService helpService = DockingWindowManager.getHelpService();
|
||||
return helpService.getHelpLocation(rootPanel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the button to make "Default" when the dialog is shown. If no default button is
|
||||
* desired, then pass <tt>null</tt> as the <tt>button</tt> value.
|
||||
|
@ -1139,6 +1149,10 @@ public class DialogComponentProvider
|
|||
return new ActionContext(null, c);
|
||||
}
|
||||
|
||||
Component sourceComponent = event.getComponent();
|
||||
if (sourceComponent != null) {
|
||||
c = sourceComponent;
|
||||
}
|
||||
return new ActionContext(null, c).setSourceObject(event.getSource());
|
||||
}
|
||||
|
||||
|
|
|
@ -88,43 +88,14 @@ public class DialogComponentProviderPopupActionManager {
|
|||
private void populatePopupMenuActions(DockingWindowManager dwm, MenuManager menuMgr,
|
||||
ActionContext actionContext) {
|
||||
|
||||
Iterator<DockingActionIf> iter = popupActions.iterator();
|
||||
while (iter.hasNext()) {
|
||||
DockingActionIf action = iter.next();
|
||||
MenuData popupMenuData = action.getPopupMenuData();
|
||||
if (popupMenuData != null && action.isValidContext(actionContext) &&
|
||||
action.isAddToPopup(actionContext)) {
|
||||
|
||||
action.setEnabled(action.isEnabledForContext(actionContext));
|
||||
menuMgr.addAction(action);
|
||||
}
|
||||
}
|
||||
|
||||
Object source = actionContext.getSourceObject();
|
||||
if (source instanceof DockingActionProviderIf) {
|
||||
DockingActionProviderIf actionProvider = (DockingActionProviderIf) source;
|
||||
List<DockingActionIf> dockingActions = actionProvider.getDockingActions();
|
||||
for (DockingActionIf action : dockingActions) {
|
||||
MenuData popupMenuData = action.getPopupMenuData();
|
||||
if (popupMenuData != null && action.isValidContext(actionContext) &&
|
||||
action.isAddToPopup(actionContext)) {
|
||||
action.setEnabled(action.isEnabledForContext(actionContext));
|
||||
menuMgr.addAction(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
// This is a bit of a kludge, but allows us to get generic actions, like 'copy' for
|
||||
// tables. This can go away if we ever convert DialogComponentProviders to use the
|
||||
// primary action system (this was something we were going to do once). If that happens,
|
||||
// then this entire class goes away.
|
||||
ActionToGuiMapper actionManager = dwm.getActionToGuiMapper();
|
||||
PopupActionManager toolPopupManager = actionManager.getPopupActionManager();
|
||||
Iterator<DockingActionIf> localActions = popupActions.iterator();
|
||||
toolPopupManager.populatePopupMenuActions(localActions, actionContext, menuMgr);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
|
|
|
@ -23,6 +23,8 @@ import java.util.*;
|
|||
|
||||
import javax.swing.JPopupMenu;
|
||||
|
||||
import org.apache.commons.collections4.IteratorUtils;
|
||||
|
||||
import docking.action.*;
|
||||
import docking.menu.*;
|
||||
|
||||
|
@ -66,6 +68,7 @@ public class PopupActionManager implements PropertyChangeListener {
|
|||
}
|
||||
|
||||
void popupMenu(ComponentPlaceholder info, MouseEvent e) {
|
||||
|
||||
if (e.isConsumed()) {
|
||||
return;
|
||||
}
|
||||
|
@ -78,24 +81,38 @@ public class PopupActionManager implements PropertyChangeListener {
|
|||
actionContext.setSourceObject(e.getSource());
|
||||
actionContext.setMouseEvent(e);
|
||||
|
||||
MenuHandler popupMenuHandler = new PopupMenuHandler(windowManager, actionContext);
|
||||
Iterator<DockingActionIf> localActions = info.getActions();
|
||||
JPopupMenu popupMenu = createPopupMenu(localActions, actionContext);
|
||||
if (popupMenu == null) {
|
||||
return; // no matching actions
|
||||
}
|
||||
|
||||
Component c = (Component) e.getSource();
|
||||
popupMenu.show(c, e.getX(), e.getY());
|
||||
}
|
||||
|
||||
JPopupMenu createPopupMenu(Iterator<DockingActionIf> localActions, ActionContext context) {
|
||||
|
||||
if (localActions == null) {
|
||||
localActions = IteratorUtils.emptyIterator();
|
||||
}
|
||||
|
||||
MenuHandler popupMenuHandler = new PopupMenuHandler(windowManager, context);
|
||||
MenuManager menuMgr =
|
||||
new MenuManager("Popup", '\0', null, true, popupMenuHandler, menuGroupMap);
|
||||
populatePopupMenuActions(info, actionContext, menuMgr);
|
||||
populatePopupMenuActions(localActions, context, menuMgr);
|
||||
if (menuMgr.isEmpty()) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Popup menu if items are available
|
||||
JPopupMenu popupMenu = menuMgr.getPopupMenu();
|
||||
Component c = (Component) e.getSource();
|
||||
popupMenu.addPopupMenuListener(popupMenuHandler);
|
||||
popupMenu.show(c, e.getX(), e.getY());
|
||||
return popupMenu;
|
||||
}
|
||||
|
||||
private void populatePopupMenuActions(ComponentPlaceholder info, ActionContext actionContext,
|
||||
MenuManager menuMgr) {
|
||||
void populatePopupMenuActions(Iterator<DockingActionIf> localActions,
|
||||
ActionContext actionContext, MenuManager menuMgr) {
|
||||
|
||||
// Unregistered actions are those used by special-needs components, on-the-fly
|
||||
addUnregisteredActions(actionContext, menuMgr);
|
||||
|
@ -129,9 +146,8 @@ public class PopupActionManager implements PropertyChangeListener {
|
|||
}
|
||||
|
||||
// Include local actions for focused component
|
||||
iter = info.getActions();
|
||||
while (iter.hasNext()) {
|
||||
DockingActionIf action = iter.next();
|
||||
while (localActions.hasNext()) {
|
||||
DockingActionIf action = localActions.next();
|
||||
if (action.getPopupMenuData() != null && action.isValidContext(actionContext) &&
|
||||
action.isAddToPopup(actionContext)) {
|
||||
action.setEnabled(action.isEnabledForContext(actionContext));
|
||||
|
|
|
@ -91,7 +91,4 @@ public interface DockingToolActions {
|
|||
* @return the actions
|
||||
*/
|
||||
public Set<DockingActionIf> getAllActions();
|
||||
|
||||
// TODO
|
||||
public boolean containsAction(DockingActionIf action);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/* ###
|
||||
* 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 docking.DockingTool;
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.tool.ToolConstants;
|
||||
import docking.widgets.table.GTable;
|
||||
|
||||
/**
|
||||
* A place used to hold {@link DockingActionIf}s that are meant to be used by components. Some
|
||||
* components do not have access to the tool that is required to register their actions. This
|
||||
* class helps those components by enabling the installation of shared actions for those
|
||||
* components.
|
||||
*/
|
||||
public class SharedActionRegistry {
|
||||
|
||||
/**
|
||||
* Install all known shared actions into the given tool
|
||||
* @param tool the tool
|
||||
* @param toolActions the tool action manager
|
||||
*/
|
||||
public static void installSharedActions(DockingTool tool, ToolActions toolActions) {
|
||||
GTable.createSharedActions(tool, toolActions, ToolConstants.TOOL_OWNER);
|
||||
}
|
||||
}
|
|
@ -89,10 +89,16 @@ public class SharedStubKeyBindingAction extends DockingAction implements Options
|
|||
@Override
|
||||
public String getOwnerDescription() {
|
||||
List<String> owners = getDistinctOwners();
|
||||
Collections.sort(owners);
|
||||
if (owners.size() == 1) {
|
||||
return owners.get(0);
|
||||
}
|
||||
|
||||
boolean hasTool = owners.remove(ToolConstants.TOOL_OWNER);
|
||||
Collections.sort(owners);
|
||||
if (hasTool) {
|
||||
owners.add(0, ToolConstants.TOOL_OWNER);
|
||||
}
|
||||
|
||||
return StringUtils.join(owners, ", ");
|
||||
}
|
||||
|
||||
|
|
|
@ -71,6 +71,7 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
|
|||
this.keyBindingOptions = tool.getOptions(DockingToolConstants.KEY_BINDINGS);
|
||||
|
||||
createReservedKeyBindings();
|
||||
SharedActionRegistry.installSharedActions(tool, this);
|
||||
}
|
||||
|
||||
private void createReservedKeyBindings() {
|
||||
|
@ -160,12 +161,7 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
|
|||
|
||||
SharedStubKeyBindingAction newStub =
|
||||
new SharedStubKeyBindingAction(name, keyBindingOptions);
|
||||
newStub.addPropertyChangeListener(this);
|
||||
keyBindingOptions.registerOption(newStub.getFullName(), OptionType.KEYSTROKE_TYPE,
|
||||
defaultKeyStroke, null, null);
|
||||
|
||||
keyBindingsManager.addAction(provider, newStub);
|
||||
|
||||
registerStub(newStub, defaultKeyStroke);
|
||||
return newStub;
|
||||
});
|
||||
|
||||
|
@ -177,6 +173,13 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
|
|||
}
|
||||
}
|
||||
|
||||
private void registerStub(SharedStubKeyBindingAction stub, KeyStroke defaultKeyStroke) {
|
||||
stub.addPropertyChangeListener(this);
|
||||
keyBindingOptions.registerOption(stub.getFullName(), OptionType.KEYSTROKE_TYPE,
|
||||
defaultKeyStroke, null, null);
|
||||
keyBindingsManager.addAction(null, stub);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given action from the tool
|
||||
* @param action the action to be removed.
|
||||
|
@ -405,11 +408,6 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAction(DockingActionIf action) {
|
||||
return getActionStorage(action).contains(action);
|
||||
}
|
||||
|
||||
public Action getAction(KeyStroke ks) {
|
||||
return keyBindingsManager.getDockingKeyAction(ks);
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import javax.swing.table.*;
|
|||
import docking.*;
|
||||
import docking.action.*;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
import docking.actions.ToolActions;
|
||||
import docking.widgets.OptionDialog;
|
||||
import docking.widgets.dialogs.SettingsDialog;
|
||||
import docking.widgets.filechooser.GhidraFileChooser;
|
||||
|
@ -71,6 +72,13 @@ import resources.ResourceManager;
|
|||
*/
|
||||
public class GTable extends JTable implements KeyStrokeConsumer {
|
||||
|
||||
private static final KeyStroke COPY_KEY_STROKE =
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_C, CONTROL_KEY_MODIFIER_MASK);
|
||||
private static final KeyStroke COPY_COLUMN_KEY_STROKE =
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_C, CONTROL_KEY_MODIFIER_MASK | SHIFT_DOWN_MASK);
|
||||
private static final KeyStroke SELECT_ALL_KEY_STROKE =
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_A, CONTROL_KEY_MODIFIER_MASK);
|
||||
|
||||
private static final String LAST_EXPORT_FILE = "LAST_EXPORT_DIR";
|
||||
|
||||
private int userDefinedRowHeight;
|
||||
|
@ -97,14 +105,6 @@ public class GTable extends JTable implements KeyStrokeConsumer {
|
|||
/** A flag to signal that a copy operation is being performed. */
|
||||
private boolean copying;
|
||||
|
||||
private static final String actionMenuGroup = "zzzTableGroup";
|
||||
private DockingAction copyAction;
|
||||
private DockingAction copyColumnsAction;
|
||||
private DockingAction copyCurrentColumnAction;
|
||||
private DockingAction selectAllAction;
|
||||
private DockingAction exportAction;
|
||||
private DockingAction exportColumnsAction;
|
||||
|
||||
private SelectionManager selectionManager;
|
||||
private Integer visibleRowCount;
|
||||
|
||||
|
@ -116,11 +116,11 @@ public class GTable extends JTable implements KeyStrokeConsumer {
|
|||
private final Map<Integer, GTableCellRenderingData> columnRenderingDataMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Constructs a new GTable.
|
||||
* Constructs a new GTable
|
||||
*/
|
||||
public GTable() {
|
||||
super();
|
||||
init(false);
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -128,44 +128,8 @@ public class GTable extends JTable implements KeyStrokeConsumer {
|
|||
* @param dm the table model
|
||||
*/
|
||||
public GTable(TableModel dm) {
|
||||
this(dm, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new GTable using the specified table model.
|
||||
* If <code>allowAutoEdit</code> is true, then automatic editing is enabled.
|
||||
* Auto-editing implies that typing in an editable cell will automatically
|
||||
* force the cell into edit mode.
|
||||
* If <code>allowAutoEdit</code> is false, then <code>F2</code> must be hit before editing may commence.
|
||||
* @param dm the table model
|
||||
* @param allowAutoEdit true if auto-editing is allowed
|
||||
*
|
||||
*/
|
||||
public GTable(TableModel dm, boolean allowAutoEdit) {
|
||||
super(dm);
|
||||
init(allowAutoEdit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a <code>GTable</code> to display the values of the given 2d array of data.
|
||||
* <p>
|
||||
* @param rowData the array of data to display in the table.
|
||||
* @param columnNames an array of names to use for the column names.
|
||||
*/
|
||||
public GTable(Object[][] rowData, Object[] columnNames) {
|
||||
this(rowData, columnNames, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a <code>GTable</code> to display the values of the given 2d array of data.
|
||||
* <p>
|
||||
* @param rowData the array of data to display in the table.
|
||||
* @param columnNames an array of names to use for the column names.
|
||||
* @param allowAutoEdit true if auto-editing is allowed
|
||||
*/
|
||||
public GTable(Object[][] rowData, Object[] columnNames, boolean allowAutoEdit) {
|
||||
super(rowData, columnNames);
|
||||
init(allowAutoEdit);
|
||||
init();
|
||||
}
|
||||
|
||||
public void setVisibleRowCount(int visibleRowCount) {
|
||||
|
@ -510,30 +474,42 @@ public class GTable extends JTable implements KeyStrokeConsumer {
|
|||
return autoLookupKeyStrokeConsumer.isKeyConsumed(keyStroke);
|
||||
}
|
||||
|
||||
private void init(boolean allowAutoEdit) {
|
||||
/**
|
||||
* Enables or disables auto-edit. When enabled, the user can start typing to trigger an
|
||||
* edit of an editable table cell.
|
||||
*
|
||||
* @param allowAutoEdit true for auto-editing
|
||||
*/
|
||||
public void setAutoEditEnabled(boolean allowAutoEdit) {
|
||||
putClientProperty("JTable.autoStartsEdit", allowAutoEdit);
|
||||
}
|
||||
|
||||
private void installEditKeyBinding() {
|
||||
AbstractAction action = new AbstractAction("StartEdit") {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent ev) {
|
||||
int row = getSelectedRow();
|
||||
int col = getSelectedColumn();
|
||||
if (col == -1) {
|
||||
Toolkit.getDefaultToolkit().beep();
|
||||
}
|
||||
KeyEvent evt = new KeyEvent(GTable.this, 0, 0, 0, KeyEvent.VK_UNDEFINED,
|
||||
KeyEvent.CHAR_UNDEFINED);
|
||||
editCellAt(row, col, evt);
|
||||
}
|
||||
};
|
||||
|
||||
KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0);
|
||||
KeyBindingUtils.registerAction(this, ks, action, JComponent.WHEN_FOCUSED);
|
||||
}
|
||||
|
||||
private void init() {
|
||||
ToolTipManager.sharedInstance().unregisterComponent(this);
|
||||
ToolTipManager.sharedInstance().registerComponent(this);
|
||||
setTableHeader(new GTableHeader(this));
|
||||
if (!allowAutoEdit) {
|
||||
putClientProperty("JTable.autoStartsEdit", Boolean.FALSE);
|
||||
|
||||
AbstractAction action = new AbstractAction("StartEdit") {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent ev) {
|
||||
int row = getSelectedRow();
|
||||
int col = getSelectedColumn();
|
||||
if (col == -1) {
|
||||
Toolkit.getDefaultToolkit().beep();
|
||||
}
|
||||
KeyEvent evt = new KeyEvent(GTable.this, 0, 0, 0, KeyEvent.VK_UNDEFINED,
|
||||
KeyEvent.CHAR_UNDEFINED);
|
||||
editCellAt(row, col, evt);
|
||||
}
|
||||
};
|
||||
|
||||
KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0);
|
||||
KeyBindingUtils.registerAction(this, ks, action, JComponent.WHEN_FOCUSED);
|
||||
}
|
||||
setAutoEditEnabled(false); // clients can turn this on as needed
|
||||
installEditKeyBinding();
|
||||
|
||||
initDefaultRenderers();
|
||||
|
||||
|
@ -558,27 +534,26 @@ public class GTable extends JTable implements KeyStrokeConsumer {
|
|||
}
|
||||
});
|
||||
|
||||
removeActionKeyStrokes();
|
||||
|
||||
// updating the row height requires the 'isInitialized' to be set, so do it first
|
||||
isInitialized = true;
|
||||
initializeRowHeight();
|
||||
|
||||
DockingWindowManager.registerComponentLoadedListener(this, (dwm, provider) -> {
|
||||
DockingTool tool = dwm.getTool();
|
||||
regiserActions(tool, provider);
|
||||
});
|
||||
}
|
||||
|
||||
private void regiserActions(DockingTool tool, ComponentProvider provider) {
|
||||
|
||||
tool.setMenuGroup(new String[] { "Copy" }, actionMenuGroup, "1");
|
||||
tool.setMenuGroup(new String[] { "Export" }, actionMenuGroup, "2");
|
||||
tool.setMenuGroup(new String[] { "Select All" }, actionMenuGroup, "3");
|
||||
|
||||
String owner = getClass().getSimpleName();
|
||||
if (provider != null) {
|
||||
owner = provider.getOwner();
|
||||
}
|
||||
installTableActions(tool, owner);
|
||||
private void removeActionKeyStrokes() {
|
||||
//
|
||||
// We remove these keybindings as we have replaced Java's version with our own. To be
|
||||
// thorough, we should really clear all table keybindings, which would ensure that any
|
||||
// user-provided key stroke would not get blocked by the table. At the time of writing,
|
||||
// there are alternate key bindings for copy that do not use this table's copy action.
|
||||
// Also, there are many other built-in keybindings for table navigation, which we do not
|
||||
// wish to override. For now, just clear these. We can clear others if they become
|
||||
// a problem.
|
||||
//
|
||||
KeyBindingUtils.clearKeyBinding(this, COPY_KEY_STROKE);
|
||||
KeyBindingUtils.clearKeyBinding(this, COPY_COLUMN_KEY_STROKE);
|
||||
KeyBindingUtils.clearKeyBinding(this, SELECT_ALL_KEY_STROKE);
|
||||
}
|
||||
|
||||
private void initializeHeader(JTableHeader header) {
|
||||
|
@ -1155,6 +1130,47 @@ public class GTable extends JTable implements KeyStrokeConsumer {
|
|||
return converted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maintain a {@link docking.widgets.table.GTableCellRenderingData} object
|
||||
* associated with each column that maintains some state and references to
|
||||
* useful data. These objects are created as needed, stored by the table for
|
||||
* convenient re-use and to prevent per-cell creation, and cleared when columns
|
||||
* are removed from the table.
|
||||
* <p>
|
||||
* Row and cell state is cleared before returning to the caller to ensure
|
||||
* consistent state; when the client is done rendering a cell, row and cell
|
||||
* state should also be cleared to minimize references.
|
||||
*
|
||||
* @param viewColumn
|
||||
* The columns' view index
|
||||
* @return Data specific to the column. Row state is cleared before returning.
|
||||
*/
|
||||
GTableCellRenderingData getRenderingData(int viewColumn) {
|
||||
|
||||
int modelColumn = convertColumnIndexToModel(viewColumn);
|
||||
|
||||
GTableCellRenderingData renderData = columnRenderingDataMap.get(modelColumn);
|
||||
|
||||
if (renderData == null) {
|
||||
Settings settings = SettingsImpl.NO_SETTINGS;
|
||||
ConfigurableColumnTableModel configurableModel = getConfigurableColumnTableModel();
|
||||
if (configurableModel != null) {
|
||||
settings = configurableModel.getColumnSettings(modelColumn);
|
||||
}
|
||||
|
||||
renderData = new GTableCellRenderingData(this, viewColumn, settings);
|
||||
columnRenderingDataMap.put(modelColumn, renderData);
|
||||
}
|
||||
|
||||
renderData.resetRowData();
|
||||
return renderData;
|
||||
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Actions
|
||||
//==================================================================================================
|
||||
|
||||
/**
|
||||
* A method that subclasses can override to signal that they wish not to have this table's
|
||||
* built-in popup actions. Subclasses will almost never need to override this method.
|
||||
|
@ -1165,198 +1181,6 @@ public class GTable extends JTable implements KeyStrokeConsumer {
|
|||
return true;
|
||||
}
|
||||
|
||||
private void installTableActions(DockingTool tool, String owner) {
|
||||
|
||||
int subGroupIndex = 1; // order by insertion
|
||||
copyAction = new GTableAction("Table Data Copy", owner) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
copying = true;
|
||||
Action builtinCopyAction = TransferHandler.getCopyAction();
|
||||
|
||||
try {
|
||||
builtinCopyAction.actionPerformed(new ActionEvent(GTable.this, 0, "copy"));
|
||||
}
|
||||
finally {
|
||||
copying = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
//@formatter:off
|
||||
copyAction.setPopupMenuData(new MenuData(
|
||||
new String[] { "Copy", "Copy" },
|
||||
ResourceManager.loadImage("images/page_white_copy.png"),
|
||||
actionMenuGroup, NO_MNEMONIC,
|
||||
Integer.toString(subGroupIndex++)
|
||||
)
|
||||
);
|
||||
copyAction.setKeyBindingData(new KeyBindingData(
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_C,
|
||||
CONTROL_KEY_MODIFIER_MASK)
|
||||
)
|
||||
);
|
||||
copyAction.setHelpLocation(new HelpLocation("Tables", "Copy"));
|
||||
//@formatter:on
|
||||
|
||||
copyCurrentColumnAction = new GTableAction("Table Data Copy Current Column", owner) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
|
||||
int column = getSelectedColumn();
|
||||
MouseEvent event = context.getMouseEvent();
|
||||
if (event != null) {
|
||||
column = columnAtPoint(event.getPoint());
|
||||
}
|
||||
|
||||
if (column < 0) {
|
||||
Msg.debug(this, "Copy failed--no column selected");
|
||||
return;
|
||||
}
|
||||
|
||||
copyColumns(column);
|
||||
}
|
||||
};
|
||||
//@formatter:off
|
||||
copyCurrentColumnAction.setPopupMenuData(new MenuData(
|
||||
new String[] { "Copy",
|
||||
"Copy Current Column" },
|
||||
ResourceManager.loadImage("images/page_white_copy.png"),
|
||||
actionMenuGroup,
|
||||
NO_MNEMONIC,
|
||||
Integer.toString(subGroupIndex++)
|
||||
)
|
||||
);
|
||||
copyCurrentColumnAction.setKeyBindingData(new KeyBindingData(
|
||||
KeyStroke.getKeyStroke(
|
||||
KeyEvent.VK_C, CONTROL_KEY_MODIFIER_MASK | SHIFT_DOWN_MASK)
|
||||
)
|
||||
);
|
||||
copyCurrentColumnAction.setHelpLocation(new HelpLocation("Tables", "Copy_Current_Column"));
|
||||
//@formatter:on
|
||||
|
||||
copyColumnsAction = new GTableAction("Table Data Copy by Columns", owner) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
int[] userColumns = promptUserForColumns();
|
||||
if (userColumns == null) {
|
||||
return; // cancelled
|
||||
}
|
||||
|
||||
copyColumns(userColumns);
|
||||
}
|
||||
};
|
||||
//@formatter:off
|
||||
copyColumnsAction.setPopupMenuData(new MenuData(
|
||||
new String[] { "Copy", "Copy Columns..." },
|
||||
ResourceManager.loadImage("images/page_white_copy.png"),
|
||||
actionMenuGroup,
|
||||
NO_MNEMONIC,
|
||||
Integer.toString(subGroupIndex++)
|
||||
)
|
||||
);
|
||||
copyColumnsAction.setHelpLocation(new HelpLocation("Tables", "Copy_Columns"));
|
||||
//@formatter:on
|
||||
|
||||
exportAction = new GTableAction("Table Data CSV Export", owner) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
File file = chooseExportFile();
|
||||
if (file != null) {
|
||||
GTableToCSV.writeCSV(file, GTable.this);
|
||||
}
|
||||
}
|
||||
};
|
||||
//@formatter:off
|
||||
exportAction.setPopupMenuData(new MenuData(
|
||||
new String[] { "Export", GTableToCSV.TITLE + "..." },
|
||||
ResourceManager.loadImage("images/application-vnd.oasis.opendocument.spreadsheet-template.png"),
|
||||
actionMenuGroup,
|
||||
NO_MNEMONIC,
|
||||
Integer.toString(subGroupIndex++)
|
||||
)
|
||||
);
|
||||
exportAction.setHelpLocation(new HelpLocation("Tables", "ExportCSV"));
|
||||
//@formatter:on
|
||||
|
||||
exportColumnsAction = new GTableAction("Table Data CSV Export (by Columns)", owner) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
int[] userColumns = promptUserForColumns();
|
||||
if (userColumns == null) {
|
||||
return; // cancelled
|
||||
}
|
||||
|
||||
File file = chooseExportFile();
|
||||
if (file == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<Integer> columnList = new ArrayList<>();
|
||||
for (int userColumn : userColumns) {
|
||||
columnList.add(userColumn);
|
||||
}
|
||||
GTableToCSV.writeCSVUsingColunns(file, GTable.this, columnList);
|
||||
}
|
||||
};
|
||||
//@formatter:off
|
||||
exportColumnsAction.setPopupMenuData(new MenuData(
|
||||
new String[] { "Export", "Export Columns to CSV..." },
|
||||
ResourceManager.loadImage("images/application-vnd.oasis.opendocument.spreadsheet-template.png"),
|
||||
actionMenuGroup,
|
||||
NO_MNEMONIC,
|
||||
Integer.toString(subGroupIndex++)
|
||||
)
|
||||
);
|
||||
exportColumnsAction.setHelpLocation(new HelpLocation("Tables", "ExportCSV_Columns"));
|
||||
//@formatter:on
|
||||
|
||||
selectAllAction = new GTableAction("Table Select All", owner) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
selectAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
if (!super.isEnabledForContext(context)) {
|
||||
return false;
|
||||
}
|
||||
return getSelectionModel().getSelectionMode() != ListSelectionModel.SINGLE_SELECTION;
|
||||
}
|
||||
};
|
||||
//@formatter:off
|
||||
selectAllAction.setPopupMenuData(new MenuData(
|
||||
new String[] { "Select All" },
|
||||
null /*icon*/,
|
||||
actionMenuGroup,
|
||||
NO_MNEMONIC,
|
||||
Integer.toString(subGroupIndex++)
|
||||
)
|
||||
);
|
||||
selectAllAction.setKeyBindingData(new KeyBindingData(
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_A,
|
||||
CONTROL_KEY_MODIFIER_MASK)
|
||||
)
|
||||
);
|
||||
selectAllAction.setHelpLocation(new HelpLocation("Tables", "SelectAll"));
|
||||
//@formatter:on
|
||||
|
||||
// remove any conflicting key bindings that Java has installed on this component
|
||||
KeyBindingUtils.clearKeyBinding(this, copyAction);
|
||||
KeyBindingUtils.clearKeyBinding(this, copyCurrentColumnAction);
|
||||
KeyBindingUtils.clearKeyBinding(this, copyColumnsAction);
|
||||
KeyBindingUtils.clearKeyBinding(this, selectAllAction);
|
||||
KeyBindingUtils.clearKeyBinding(this, exportAction);
|
||||
KeyBindingUtils.clearKeyBinding(this, exportColumnsAction);
|
||||
|
||||
tool.addAction(copyAction);
|
||||
tool.addAction(copyCurrentColumnAction);
|
||||
tool.addAction(copyColumnsAction);
|
||||
tool.addAction(selectAllAction);
|
||||
tool.addAction(exportAction);
|
||||
tool.addAction(exportColumnsAction);
|
||||
}
|
||||
|
||||
private void copyColumns(int... copyColumns) {
|
||||
|
||||
int[] originalColumns = new int[0];
|
||||
|
@ -1433,43 +1257,216 @@ public class GTable extends JTable implements KeyStrokeConsumer {
|
|||
Preferences.store();
|
||||
}
|
||||
|
||||
/**
|
||||
* Maintain a {@link docking.widgets.table.GTableCellRenderingData} object
|
||||
* associated with each column that maintains some state and references to
|
||||
* useful data. These objects are created as needed, stored by the table for
|
||||
* convenient re-use and to prevent per-cell creation, and cleared when columns
|
||||
* are removed from the table.
|
||||
* <p>
|
||||
* Row and cell state is cleared before returning to the caller to ensure
|
||||
* consistent state; when the client is done rendering a cell, row and cell
|
||||
* state should also be cleared to minimize references.
|
||||
*
|
||||
* @param viewColumn
|
||||
* The columns' view index
|
||||
* @return Data specific to the column. Row state is cleared before returning.
|
||||
*/
|
||||
GTableCellRenderingData getRenderingData(int viewColumn) {
|
||||
private void doCopy() {
|
||||
copying = true;
|
||||
Action builtinCopyAction = TransferHandler.getCopyAction();
|
||||
|
||||
int modelColumn = convertColumnIndexToModel(viewColumn);
|
||||
try {
|
||||
builtinCopyAction.actionPerformed(new ActionEvent(GTable.this, 0, "copy"));
|
||||
}
|
||||
finally {
|
||||
copying = false;
|
||||
}
|
||||
}
|
||||
|
||||
GTableCellRenderingData renderData = columnRenderingDataMap.get(modelColumn);
|
||||
|
||||
if (renderData == null) {
|
||||
Settings settings = SettingsImpl.NO_SETTINGS;
|
||||
ConfigurableColumnTableModel configurableModel = getConfigurableColumnTableModel();
|
||||
if (configurableModel != null) {
|
||||
settings = configurableModel.getColumnSettings(modelColumn);
|
||||
}
|
||||
|
||||
renderData = new GTableCellRenderingData(this, viewColumn, settings);
|
||||
columnRenderingDataMap.put(modelColumn, renderData);
|
||||
private void doCopyCurrentColumn(MouseEvent event) {
|
||||
int column = getSelectedColumn();
|
||||
if (event != null) {
|
||||
column = columnAtPoint(event.getPoint());
|
||||
}
|
||||
|
||||
renderData.resetRowData();
|
||||
return renderData;
|
||||
if (column < 0) {
|
||||
Msg.debug(this, "Copy failed--no column selected");
|
||||
return;
|
||||
}
|
||||
|
||||
copyColumns(column);
|
||||
}
|
||||
|
||||
private void doCopyColumns() {
|
||||
int[] userColumns = promptUserForColumns();
|
||||
if (userColumns == null) {
|
||||
return; // cancelled
|
||||
}
|
||||
|
||||
copyColumns(userColumns);
|
||||
}
|
||||
|
||||
private void doExport() {
|
||||
File file = chooseExportFile();
|
||||
if (file != null) {
|
||||
GTableToCSV.writeCSV(file, GTable.this);
|
||||
}
|
||||
}
|
||||
|
||||
private void doExportColumns() {
|
||||
int[] userColumns = promptUserForColumns();
|
||||
if (userColumns == null) {
|
||||
return; // cancelled
|
||||
}
|
||||
|
||||
File file = chooseExportFile();
|
||||
if (file == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<Integer> columnList = new ArrayList<>();
|
||||
for (int userColumn : userColumns) {
|
||||
columnList.add(userColumn);
|
||||
}
|
||||
GTableToCSV.writeCSVUsingColunns(file, GTable.this, columnList);
|
||||
}
|
||||
|
||||
public static void createSharedActions(DockingTool tool, ToolActions toolActions,
|
||||
String owner) {
|
||||
|
||||
String actionMenuGroup = "zzzTableGroup";
|
||||
tool.setMenuGroup(new String[] { "Copy" }, actionMenuGroup, "1");
|
||||
tool.setMenuGroup(new String[] { "Export" }, actionMenuGroup, "2");
|
||||
tool.setMenuGroup(new String[] { "Select All" }, actionMenuGroup, "3");
|
||||
|
||||
int subGroupIndex = 1; // order by insertion
|
||||
GTableAction copyAction = new GTableAction("Table Data Copy", owner) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
GTable gTable = (GTable) context.getSourceComponent();
|
||||
gTable.doCopy();
|
||||
}
|
||||
};
|
||||
//@formatter:off
|
||||
copyAction.setPopupMenuData(new MenuData(
|
||||
new String[] { "Copy", "Copy" },
|
||||
ResourceManager.loadImage("images/page_white_copy.png"),
|
||||
actionMenuGroup, NO_MNEMONIC,
|
||||
Integer.toString(subGroupIndex++)
|
||||
)
|
||||
);
|
||||
copyAction.setKeyBindingData(new KeyBindingData(COPY_KEY_STROKE));
|
||||
copyAction.setHelpLocation(new HelpLocation("Tables", "Copy"));
|
||||
//@formatter:on
|
||||
|
||||
GTableAction copyCurrentColumnAction =
|
||||
new GTableAction("Table Data Copy Current Column", owner) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
GTable gTable = (GTable) context.getSourceComponent();
|
||||
gTable.doCopyCurrentColumn(context.getMouseEvent());
|
||||
}
|
||||
};
|
||||
//@formatter:off
|
||||
copyCurrentColumnAction.setPopupMenuData(new MenuData(
|
||||
new String[] { "Copy",
|
||||
"Copy Current Column" },
|
||||
ResourceManager.loadImage("images/page_white_copy.png"),
|
||||
actionMenuGroup,
|
||||
NO_MNEMONIC,
|
||||
Integer.toString(subGroupIndex++)
|
||||
)
|
||||
);
|
||||
copyCurrentColumnAction.setKeyBindingData(new KeyBindingData(COPY_COLUMN_KEY_STROKE));
|
||||
copyCurrentColumnAction.setHelpLocation(new HelpLocation("Tables", "Copy_Current_Column"));
|
||||
//@formatter:on
|
||||
|
||||
GTableAction copyColumnsAction = new GTableAction("Table Data Copy by Columns", owner) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
GTable gTable = (GTable) context.getSourceComponent();
|
||||
gTable.doCopyColumns();
|
||||
}
|
||||
};
|
||||
//@formatter:off
|
||||
copyColumnsAction.setPopupMenuData(new MenuData(
|
||||
new String[] { "Copy", "Copy Columns..." },
|
||||
ResourceManager.loadImage("images/page_white_copy.png"),
|
||||
actionMenuGroup,
|
||||
NO_MNEMONIC,
|
||||
Integer.toString(subGroupIndex++)
|
||||
)
|
||||
);
|
||||
copyColumnsAction.setHelpLocation(new HelpLocation("Tables", "Copy_Columns"));
|
||||
//@formatter:on
|
||||
|
||||
GTableAction exportAction = new GTableAction("Table Data CSV Export", owner) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
GTable gTable = (GTable) context.getSourceComponent();
|
||||
gTable.doExport();
|
||||
}
|
||||
};
|
||||
//@formatter:off
|
||||
exportAction.setPopupMenuData(new MenuData(
|
||||
new String[] { "Export", GTableToCSV.TITLE + "..." },
|
||||
ResourceManager.loadImage("images/application-vnd.oasis.opendocument.spreadsheet-template.png"),
|
||||
actionMenuGroup,
|
||||
NO_MNEMONIC,
|
||||
Integer.toString(subGroupIndex++)
|
||||
)
|
||||
);
|
||||
exportAction.setHelpLocation(new HelpLocation("Tables", "ExportCSV"));
|
||||
//@formatter:on
|
||||
|
||||
GTableAction exportColumnsAction =
|
||||
new GTableAction("Table Data CSV Export (by Columns)", owner) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
GTable gTable = (GTable) context.getSourceComponent();
|
||||
gTable.doExportColumns();
|
||||
}
|
||||
};
|
||||
//@formatter:off
|
||||
exportColumnsAction.setPopupMenuData(new MenuData(
|
||||
new String[] { "Export", "Export Columns to CSV..." },
|
||||
ResourceManager.loadImage("images/application-vnd.oasis.opendocument.spreadsheet-template.png"),
|
||||
actionMenuGroup,
|
||||
NO_MNEMONIC,
|
||||
Integer.toString(subGroupIndex++)
|
||||
)
|
||||
);
|
||||
exportColumnsAction.setHelpLocation(new HelpLocation("Tables", "ExportCSV_Columns"));
|
||||
//@formatter:on
|
||||
|
||||
GTableAction selectAllAction = new GTableAction("Table Select All", owner) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
GTable gTable = (GTable) context.getSourceComponent();
|
||||
gTable.selectAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
if (!super.isEnabledForContext(context)) {
|
||||
return false;
|
||||
}
|
||||
GTable gTable = (GTable) context.getSourceComponent();
|
||||
int mode = gTable.getSelectionModel().getSelectionMode();
|
||||
return mode != ListSelectionModel.SINGLE_SELECTION;
|
||||
}
|
||||
};
|
||||
//@formatter:off
|
||||
selectAllAction.setPopupMenuData(new MenuData(
|
||||
new String[] { "Select All" },
|
||||
null /*icon*/,
|
||||
actionMenuGroup,
|
||||
NO_MNEMONIC,
|
||||
Integer.toString(subGroupIndex++)
|
||||
)
|
||||
);
|
||||
selectAllAction.setKeyBindingData(new KeyBindingData(SELECT_ALL_KEY_STROKE));
|
||||
selectAllAction.setHelpLocation(new HelpLocation("Tables", "SelectAll"));
|
||||
//@formatter:on
|
||||
|
||||
toolActions.addGlobalAction(copyAction);
|
||||
toolActions.addGlobalAction(copyColumnsAction);
|
||||
toolActions.addGlobalAction(copyCurrentColumnAction);
|
||||
toolActions.addGlobalAction(exportAction);
|
||||
toolActions.addGlobalAction(exportColumnsAction);
|
||||
toolActions.addGlobalAction(selectAllAction);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
||||
private class MyTableColumnModelListener implements TableColumnModelListener {
|
||||
@Override
|
||||
public void columnSelectionChanged(ListSelectionEvent e) {
|
||||
|
@ -1499,17 +1496,25 @@ public class GTable extends JTable implements KeyStrokeConsumer {
|
|||
}
|
||||
}
|
||||
|
||||
private abstract class GTableAction extends DockingAction {
|
||||
private abstract static class GTableAction extends DockingAction {
|
||||
|
||||
GTableAction(String name, String owner) {
|
||||
super(name, owner, KeyBindingType.SHARED);
|
||||
super(name, owner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAddToPopup(ActionContext context) {
|
||||
if (!isEnabledForContext(context)) {
|
||||
return false;
|
||||
}
|
||||
GTable gTable = (GTable) context.getSourceComponent();
|
||||
return gTable.supportsPopupActions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
Component sourceComponent = context.getSourceComponent();
|
||||
return sourceComponent == GTable.this;
|
||||
return sourceComponent instanceof GTable;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue