mirror of
https://github.com/NationalSecurityAgency/ghidra
synced 2024-10-13 21:52:59 +00:00
Merge remote-tracking branch 'origin/GT-3044-dragonmacher-table-popup-cleanup'
This commit is contained in:
commit
351cf56e6e
|
@ -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,26 +15,37 @@
|
|||
*/
|
||||
package ghidra.app.context;
|
||||
|
||||
import ghidra.program.model.listing.Program;
|
||||
import java.awt.Component;
|
||||
import java.awt.KeyboardFocusManager;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.ComponentProvider;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
public class ProgramActionContext extends ActionContext {
|
||||
protected final Program program;
|
||||
|
||||
|
||||
public ProgramActionContext(ComponentProvider provider, Program program) {
|
||||
this(provider, program, null);
|
||||
}
|
||||
|
||||
public ProgramActionContext(ComponentProvider provider, Program program, Object contextObject) {
|
||||
super(provider, contextObject);
|
||||
this.program = program;
|
||||
|
||||
public ProgramActionContext(ComponentProvider provider, Program program,
|
||||
Component sourceComponent) {
|
||||
this(provider, program, sourceComponent, sourceComponent);
|
||||
}
|
||||
|
||||
public ProgramActionContext(ComponentProvider provider, Program program,
|
||||
Component sourceComponent, Object contextObject) {
|
||||
super(provider, contextObject, sourceComponent);
|
||||
this.program = program;
|
||||
|
||||
// the case where the first constructor is called, which does not specify the component
|
||||
if (sourceComponent == null) {
|
||||
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
|
||||
setSourceObject(kfm.getFocusOwner());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the program.
|
||||
*/
|
||||
public Program getProgram() {
|
||||
return program;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package ghidra.app.context;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
|
@ -27,14 +28,9 @@ public class ProgramSymbolActionContext extends ProgramActionContext {
|
|||
|
||||
private final long[] symbolIDs;
|
||||
|
||||
public ProgramSymbolActionContext(ComponentProvider provider, Program program,
|
||||
long[] symbolIDs) {
|
||||
this(provider, program, symbolIDs, null);
|
||||
}
|
||||
|
||||
public ProgramSymbolActionContext(ComponentProvider provider, Program program, long[] symbolIDs,
|
||||
Object contextObj) {
|
||||
super(provider, program, contextObj);
|
||||
Component sourceComponent) {
|
||||
super(provider, program, sourceComponent);
|
||||
this.symbolIDs = symbolIDs;
|
||||
}
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@ class MergeManagerProvider extends ComponentProviderAdapter {
|
|||
if (event != null && event.getSource() instanceof FieldHeaderComp) {
|
||||
FieldHeaderComp comp = (FieldHeaderComp) event.getSource();
|
||||
FieldHeaderLocation fieldHeaderLocation = comp.getFieldHeaderLocation(event.getPoint());
|
||||
return new ActionContext(this, fieldHeaderLocation);
|
||||
return createContext(fieldHeaderLocation);
|
||||
|
||||
}
|
||||
if (mergeManager instanceof ProgramMultiUserMergeManager) {
|
||||
|
|
|
@ -47,7 +47,7 @@ public class ListingMergePanelProvider extends ComponentProviderAdapter
|
|||
@Override
|
||||
public ActionContext getActionContext(MouseEvent event) {
|
||||
Object obj = mergePanel.getActionContext(event);
|
||||
return new ActionContext(this, obj);
|
||||
return createContext(obj);
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
|
@ -55,7 +55,7 @@ public class ListingMergePanelProvider extends ComponentProviderAdapter
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<DockingActionIf> getPopupActions(DockingTool tool, ActionContext context) {
|
||||
public List<DockingActionIf> getPopupActions(DockingTool dt, ActionContext context) {
|
||||
ListingPanel resultPanel = mergePanel.getResultPanel();
|
||||
if (resultPanel != null) {
|
||||
return resultPanel.getHeaderActions(getName());
|
||||
|
|
|
@ -105,7 +105,7 @@ public class BookmarkProvider extends ComponentProviderAdapter {
|
|||
if (program == null) {
|
||||
return null;
|
||||
}
|
||||
return new ProgramActionContext(this, program);
|
||||
return new ProgramActionContext(this, program, bookmarkTable);
|
||||
}
|
||||
|
||||
void setGoToService(GoToService goToService) {
|
||||
|
|
|
@ -269,17 +269,17 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
|||
FieldHeader headerPanel = listingPanel.getFieldHeader();
|
||||
if (headerPanel != null && source instanceof FieldHeaderComp) {
|
||||
FieldHeaderLocation fhLoc = headerPanel.getFieldHeaderLocation(event.getPoint());
|
||||
return new ActionContext(this, fhLoc);
|
||||
return createContext(fhLoc);
|
||||
}
|
||||
|
||||
if (otherPanel != null && otherPanel.isAncestorOf((Component) source)) {
|
||||
Object obj = getContextForMarginPanels(otherPanel, event);
|
||||
if (obj != null) {
|
||||
return new ActionContext(this, obj);
|
||||
return createContext(obj);
|
||||
}
|
||||
return new OtherPanelContext(this, program);
|
||||
}
|
||||
return new ActionContext(this, getContextForMarginPanels(listingPanel, event));
|
||||
return createContext(getContextForMarginPanels(listingPanel, event));
|
||||
}
|
||||
|
||||
private Object getContextForMarginPanels(ListingPanel lp, MouseEvent event) {
|
||||
|
@ -955,11 +955,11 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<DockingActionIf> getPopupActions(DockingTool tool, ActionContext context) {
|
||||
public List<DockingActionIf> getPopupActions(DockingTool dt, ActionContext context) {
|
||||
if (context.getComponentProvider() == this) {
|
||||
return listingPanel.getHeaderActions(getName());
|
||||
}
|
||||
return new ArrayList<>();
|
||||
return null;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.datamgr;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Point;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
@ -333,7 +334,8 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
|||
|
||||
Object source = event.getSource();
|
||||
if (source instanceof JTextField || source instanceof JTextPane) {
|
||||
return new ActionContext(this, source, source);
|
||||
Component component = (Component) source;
|
||||
return new ActionContext(this, source, component);
|
||||
}
|
||||
|
||||
Point point = event.getPoint();
|
||||
|
|
|
@ -30,58 +30,62 @@ import ghidra.util.exception.NoValueException;
|
|||
import ghidra.util.table.GhidraTable;
|
||||
|
||||
public class SizeAlignmentPanel extends JPanel {
|
||||
|
||||
|
||||
GhidraTable table;
|
||||
DataOrganizationImpl dataOrganization;
|
||||
|
||||
public SizeAlignmentPanel() {
|
||||
super(new BorderLayout());
|
||||
TableModel tableModel = new SizeAlignmentTableModel();
|
||||
table = new GhidraTable(tableModel, true);
|
||||
JScrollPane sp = new JScrollPane(table);
|
||||
table.setPreferredScrollableViewportSize(new Dimension(200, 80));
|
||||
table = new GhidraTable(tableModel);
|
||||
table.setAutoEditEnabled(true);
|
||||
JScrollPane sp = new JScrollPane(table);
|
||||
table.setPreferredScrollableViewportSize(new Dimension(200, 80));
|
||||
add(sp, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
public void setOrganization(DataOrganizationImpl dataOrganization) {
|
||||
this.dataOrganization = dataOrganization;
|
||||
((SizeAlignmentTableModel)table.getModel()).fireTableDataChanged();
|
||||
((SizeAlignmentTableModel) table.getModel()).fireTableDataChanged();
|
||||
}
|
||||
|
||||
|
||||
class SizeAlignmentTableModel extends AbstractTableModel {
|
||||
|
||||
private final String[] columnNames = new String[] {"Size", "Alignment"};
|
||||
|
||||
private final String[] columnNames = new String[] { "Size", "Alignment" };
|
||||
private final int SIZE_COLUMN = 0;
|
||||
private final int ALIGNMENT_COLUMN = 1;
|
||||
|
||||
|
||||
SizeAlignmentTableModel() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTableModelListener(TableModelListener l) {
|
||||
public void addTableModelListener(TableModelListener l) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getColumnClass(int columnIndex) {
|
||||
public Class<?> getColumnClass(int columnIndex) {
|
||||
return Integer.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return columnNames.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int columnIndex) {
|
||||
public String getColumnName(int columnIndex) {
|
||||
return columnNames[columnIndex];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return dataOrganization.getSizeAlignmentCount() + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||
int[] sizes = dataOrganization.getSizes();
|
||||
if (rowIndex < sizes.length) {
|
||||
|
@ -92,7 +96,8 @@ public class SizeAlignmentPanel extends JPanel {
|
|||
else if (columnIndex == ALIGNMENT_COLUMN) {
|
||||
try {
|
||||
return dataOrganization.getSizeAlignment(size);
|
||||
} catch (NoValueException e) {
|
||||
}
|
||||
catch (NoValueException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -101,7 +106,7 @@ public class SizeAlignmentPanel extends JPanel {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isCellEditable(int rowIndex, int columnIndex) {
|
||||
public boolean isCellEditable(int rowIndex, int columnIndex) {
|
||||
if (rowIndex == dataOrganization.getSizeAlignmentCount()) {
|
||||
return columnIndex == SIZE_COLUMN;
|
||||
}
|
||||
|
@ -109,30 +114,31 @@ public class SizeAlignmentPanel extends JPanel {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void removeTableModelListener(TableModelListener l) {
|
||||
public void removeTableModelListener(TableModelListener l) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueAt(Object value, int rowIndex, int columnIndex) {
|
||||
public void setValueAt(Object value, int rowIndex, int columnIndex) {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
int[] sizes = dataOrganization.getSizes();
|
||||
if (rowIndex < sizes.length) {
|
||||
int alignment = ((Integer)value).intValue();
|
||||
int alignment = ((Integer) value).intValue();
|
||||
int size = sizes[rowIndex];
|
||||
dataOrganization.setSizeAlignment(size, alignment);
|
||||
}
|
||||
if (rowIndex == sizes.length) {
|
||||
int size = ((Integer)value).intValue();
|
||||
int size = ((Integer) value).intValue();
|
||||
// Check that we don't already have this size.
|
||||
try {
|
||||
dataOrganization.getSizeAlignment(size);
|
||||
setStatusMessage("An alignment is already defined for a size of "+size+".");
|
||||
setStatusMessage("An alignment is already defined for a size of " + size + ".");
|
||||
return;
|
||||
} catch (NoValueException e) {
|
||||
}
|
||||
catch (NoValueException e) {
|
||||
// Actually don't want to find a value so we can set one below.
|
||||
}
|
||||
int alignment = size; // Set the alignment to match the size initially.
|
||||
|
|
|
@ -113,25 +113,22 @@ class EnumEditorPanel extends JPanel {
|
|||
|
||||
// invoke later because the key press on the table causes the selection
|
||||
// to change
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (table.isEditing()) {
|
||||
return; // don't change the selection if a new edit is in progress
|
||||
}
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
try {
|
||||
if (table.isEditing()) {
|
||||
return; // don't change the selection if a new edit is in progress
|
||||
}
|
||||
|
||||
int row = tableModel.getRow(name);
|
||||
if (row >= 0 && row < tableModel.getRowCount()) {
|
||||
table.setRowSelectionInterval(row, row);
|
||||
Rectangle rect = table.getCellRect(row, 0, false);
|
||||
table.scrollRectToVisible(rect);
|
||||
}
|
||||
}
|
||||
catch (NoSuchElementException e) {
|
||||
// ignore
|
||||
int row = tableModel.getRow(name);
|
||||
if (row >= 0 && row < tableModel.getRowCount()) {
|
||||
table.setRowSelectionInterval(row, row);
|
||||
Rectangle rect = table.getCellRect(row, 0, false);
|
||||
table.scrollRectToVisible(rect);
|
||||
}
|
||||
}
|
||||
catch (NoSuchElementException e) {
|
||||
// ignore
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -232,8 +229,8 @@ class EnumEditorPanel extends JPanel {
|
|||
void deleteSelectedEntries() {
|
||||
EnumDataType enuum = getEnum();
|
||||
int[] rows = getSelectedRows();
|
||||
for (int i = 0; i < rows.length; i++) {
|
||||
String name = tableModel.getNameAt(rows[i]);
|
||||
for (int row : rows) {
|
||||
String name = tableModel.getNameAt(row);
|
||||
enuum.remove(name);
|
||||
}
|
||||
tableModel.setEnum(enuum, true);
|
||||
|
@ -262,15 +259,12 @@ class EnumEditorPanel extends JPanel {
|
|||
"All possible Enum values have already been used");
|
||||
return;
|
||||
}
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
table.setRowSelectionInterval(newRow, newRow);
|
||||
table.editCellAt(newRow, EnumTableModel.NAME_COL);
|
||||
Rectangle r = table.getCellRect(newRow, 0, true);
|
||||
table.scrollRectToVisible(r);
|
||||
provider.stateChanged(null);
|
||||
}
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
table.setRowSelectionInterval(newRow, newRow);
|
||||
table.editCellAt(newRow, EnumTableModel.NAME_COL);
|
||||
Rectangle r = table.getCellRect(newRow, 0, true);
|
||||
table.scrollRectToVisible(r);
|
||||
provider.stateChanged(null);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -418,18 +412,15 @@ class EnumEditorPanel extends JPanel {
|
|||
|
||||
sizeComboBox = new GhidraComboBox(new Integer[] { 1, 2, 4, 8 });
|
||||
sizeComboBox.setName("Size");
|
||||
sizeComboBox.addItemListener(new ItemListener() {
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
Integer length = (Integer) sizeComboBox.getSelectedItem();
|
||||
if (!validateNewLength(length)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setStatusMessage("");
|
||||
tableModel.setLength(length);
|
||||
provider.stateChanged(null);
|
||||
sizeComboBox.addItemListener(e -> {
|
||||
Integer length = (Integer) sizeComboBox.getSelectedItem();
|
||||
if (!validateNewLength(length)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setStatusMessage("");
|
||||
tableModel.setLength(length);
|
||||
provider.stateChanged(null);
|
||||
});
|
||||
|
||||
JLabel label = new GLabel("Category:", SwingConstants.RIGHT);
|
||||
|
@ -465,13 +456,10 @@ class EnumEditorPanel extends JPanel {
|
|||
}
|
||||
|
||||
private void vetoSizeChange(final int newLength, final int currentLength, final long badValue) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
setStatusMessage("Enum size of " + newLength + " cannot contain the value " + "0x" +
|
||||
Long.toHexString(badValue));
|
||||
sizeComboBox.setSelectedItem(new Integer(currentLength));
|
||||
}
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
setStatusMessage("Enum size of " + newLength + " cannot contain the value " + "0x" +
|
||||
Long.toHexString(badValue));
|
||||
sizeComboBox.setSelectedItem(new Integer(currentLength));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -495,12 +483,9 @@ class EnumEditorPanel extends JPanel {
|
|||
}
|
||||
|
||||
private void focus(final JTextField field) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
field.requestFocusInWindow();
|
||||
field.selectAll();
|
||||
}
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
field.requestFocusInWindow();
|
||||
field.selectAll();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -510,7 +495,8 @@ class EnumEditorPanel extends JPanel {
|
|||
|
||||
private class EnumTable extends GhidraTable {
|
||||
EnumTable(TableModel model) {
|
||||
super(model, true);
|
||||
super(model);
|
||||
setAutoEditEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -534,12 +520,7 @@ class EnumEditorPanel extends JPanel {
|
|||
public EnumCellEditor(JTextField textField) {
|
||||
super(textField);
|
||||
textField.addKeyListener(editingKeyListener);
|
||||
textField.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
table.editingStopped(null);
|
||||
}
|
||||
});
|
||||
textField.addActionListener(e -> table.editingStopped(null));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -148,9 +148,8 @@ public class AutoTableDisassemblerPlugin extends ProgramPlugin implements Domain
|
|||
};
|
||||
findTableAction.setHelpLocation(
|
||||
new HelpLocation(HelpTopics.SEARCH, findTableAction.getName()));
|
||||
findTableAction.setMenuBarData(
|
||||
new MenuData(new String[] { ToolConstants.MENU_SEARCH, "For Address Tables..." }, null,
|
||||
"search for"));
|
||||
findTableAction.setMenuBarData(new MenuData(
|
||||
new String[] { ToolConstants.MENU_SEARCH, "For Address Tables" }, null, "search for"));
|
||||
findTableAction.setDescription(getPluginDescription().getDescription());
|
||||
|
||||
tool.addAction(findTableAction);
|
||||
|
|
|
@ -18,7 +18,6 @@ package ghidra.app.plugin.core.editor;
|
|||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.io.*;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
@ -91,10 +90,10 @@ public class TextEditorComponentProvider extends ComponentProviderAdapter {
|
|||
return textarea.getText();
|
||||
}
|
||||
|
||||
private void initialize(TextEditorManagerPlugin plugin, String textContents) {
|
||||
this.plugin = plugin;
|
||||
private void initialize(TextEditorManagerPlugin p, String textContents) {
|
||||
this.plugin = p;
|
||||
|
||||
setHelpLocation(new HelpLocation(plugin.getName(), plugin.getName()));
|
||||
setHelpLocation(new HelpLocation(p.getName(), p.getName()));
|
||||
|
||||
title = textFileName + (isReadOnly() ? " (Read-Only) " : "");
|
||||
setTitle(title);
|
||||
|
@ -106,7 +105,7 @@ public class TextEditorComponentProvider extends ComponentProviderAdapter {
|
|||
|
||||
addToTool();
|
||||
setVisible(true);
|
||||
plugin.getTool().setStatusInfo("Press F1 for help.");
|
||||
p.getTool().setStatusInfo("Press F1 for help.");
|
||||
|
||||
createActions();
|
||||
}
|
||||
|
@ -278,8 +277,7 @@ public class TextEditorComponentProvider extends ComponentProviderAdapter {
|
|||
|
||||
ActionContextProvider acp = e -> {
|
||||
ComponentProvider p = TextEditorComponentProvider.this;
|
||||
Object context = TextEditorComponentProvider.this;
|
||||
return new ActionContext(p, context);
|
||||
return new ActionContext(p);
|
||||
};
|
||||
|
||||
KeyBindingUtils.registerAction(textarea, saveAction, acp);
|
||||
|
@ -374,11 +372,6 @@ public class TextEditorComponentProvider extends ComponentProviderAdapter {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionContext getActionContext(MouseEvent event) {
|
||||
return new ActionContext(this, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getComponent() {
|
||||
return scrollpane;
|
||||
|
@ -387,9 +380,10 @@ public class TextEditorComponentProvider extends ComponentProviderAdapter {
|
|||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
||||
/**
|
||||
* Special JTextArea that knows how to properly handle it's key events.
|
||||
* @see {@link #processKeyBinding(KeyStroke, KeyEvent, int, boolean)}
|
||||
* @see #processKeyBinding(KeyStroke, KeyEvent, int, boolean)
|
||||
*/
|
||||
private class KeyMasterTextArea extends JTextArea {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
@ -455,7 +449,7 @@ public class TextEditorComponentProvider extends ComponentProviderAdapter {
|
|||
return true;
|
||||
}
|
||||
|
||||
return SwingUtilities.notifyAction(action, ks, e, this, e.getModifiers());
|
||||
return SwingUtilities.notifyAction(action, ks, e, this, e.getModifiersEx());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -71,11 +71,7 @@ public class FunctionWindowProvider extends ComponentProviderAdapter {
|
|||
|
||||
@Override
|
||||
public ActionContext getActionContext(MouseEvent event) {
|
||||
if (event != null && event.getSource() == functionTable) {
|
||||
return new ActionContext(this, functionTable);
|
||||
}
|
||||
|
||||
return null;
|
||||
return new ActionContext(this, functionTable);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -251,7 +251,7 @@ public class InstructionSearchPlugin extends ProgramPlugin {
|
|||
};
|
||||
searchAction.setHelpLocation(new HelpLocation("Search", "Instruction_Pattern_Search"));
|
||||
searchAction.setMenuBarData(
|
||||
new MenuData(new String[] { ToolConstants.MENU_SEARCH, "For Instruction Patterns..." },
|
||||
new MenuData(new String[] { ToolConstants.MENU_SEARCH, "For Instruction Patterns" },
|
||||
null, "search for"));
|
||||
searchAction.setDescription("Construct searches using selected instructions");
|
||||
tool.addAction(searchAction);
|
||||
|
|
|
@ -21,6 +21,7 @@ import javax.swing.JToolBar;
|
|||
import javax.swing.table.TableCellRenderer;
|
||||
|
||||
import docking.widgets.table.GTable;
|
||||
import ghidra.app.plugin.core.instructionsearch.InstructionSearchPlugin;
|
||||
import ghidra.app.plugin.core.instructionsearch.model.*;
|
||||
import ghidra.util.table.GhidraTable;
|
||||
|
||||
|
@ -85,6 +86,10 @@ public abstract class AbstractInstructionTable extends GhidraTable {
|
|||
this.setRowHeight(this.getRowHeight() + CELL_HEIGHT_PADDING);
|
||||
}
|
||||
|
||||
InstructionSearchPlugin getPlugin() {
|
||||
return dialog.getPlugin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data object at the given cell location. We need to check
|
||||
* first to make sure the row/col values map to a valid cell.
|
||||
|
|
|
@ -504,13 +504,12 @@ public class InstructionSearchDialog extends DialogComponentProvider implements
|
|||
SystemUtilities.runSwingLater(runnable);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param addr
|
||||
*/
|
||||
private void goToLocation(Address addr) {
|
||||
GoToService gs = plugin.getTool().getService(GoToService.class);
|
||||
gs.goTo(addr);
|
||||
}
|
||||
|
||||
public InstructionSearchPlugin getPlugin() {
|
||||
return plugin;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,8 +21,7 @@ import java.util.List;
|
|||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.*;
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.DockingWindowManager;
|
||||
import docking.widgets.EmptyBorderButton;
|
||||
import ghidra.app.plugin.core.instructionsearch.InstructionSearchPlugin;
|
||||
import ghidra.app.plugin.core.instructionsearch.model.*;
|
||||
|
@ -95,13 +94,9 @@ public class InstructionTable extends AbstractInstructionTable {
|
|||
dialog.getSearchData().registerForGuiUpdates(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override from abstract class so we can remove the select and copy options
|
||||
* (which is all of them).
|
||||
*/
|
||||
@Override
|
||||
public List<DockingActionIf> getPopupActions(DockingTool tool, ActionContext context) {
|
||||
return null;
|
||||
protected boolean supportsPopupActions() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public InsertBytesWidget getInsertBytesWidget() {
|
||||
|
|
|
@ -25,14 +25,17 @@ import java.util.*;
|
|||
import javax.swing.*;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
|
||||
import docking.*;
|
||||
import docking.action.*;
|
||||
import docking.ActionContext;
|
||||
import docking.EmptyBorderToggleButton;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.MenuData;
|
||||
import docking.dnd.GClipboard;
|
||||
import docking.widgets.EmptyBorderButton;
|
||||
import ghidra.app.plugin.core.instructionsearch.InstructionSearchPlugin;
|
||||
import ghidra.app.plugin.core.instructionsearch.model.*;
|
||||
import ghidra.app.plugin.core.instructionsearch.ui.SelectionModeWidget.InputMode;
|
||||
import ghidra.app.plugin.core.instructionsearch.util.InstructionSearchUtils;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.task.*;
|
||||
|
@ -78,6 +81,7 @@ public class PreviewTable extends AbstractInstructionTable {
|
|||
*
|
||||
* @param numColumns the number of columns in the table
|
||||
* @param plugin the parent plugin
|
||||
* @param dialog the search dialog
|
||||
*/
|
||||
public PreviewTable(int numColumns, InstructionSearchPlugin plugin,
|
||||
InstructionSearchDialog dialog) {
|
||||
|
@ -131,22 +135,6 @@ public class PreviewTable extends AbstractInstructionTable {
|
|||
refreshView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds custom context-sensitive menus to the table. This does NOT modify
|
||||
* any existing menus; it simply adds to them.
|
||||
*/
|
||||
@Override
|
||||
public List<DockingActionIf> getPopupActions(DockingTool tool, ActionContext context) {
|
||||
|
||||
// Invoke the base class method to add default menu options.
|
||||
List<DockingActionIf> list = super.getPopupActions(tool, context);
|
||||
|
||||
// And now add our own.
|
||||
addCustomMenuItems(list);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the contents of the preview table at the given row with the
|
||||
* given string.
|
||||
|
@ -373,14 +361,6 @@ public class PreviewTable extends AbstractInstructionTable {
|
|||
return binaryBtn;
|
||||
}
|
||||
|
||||
private void addCustomMenuItems(List<DockingActionIf> list) {
|
||||
DockingWindowManager dwm = DockingWindowManager.getInstance(this);
|
||||
dwm.setMenuGroup(new String[] { "Copy Special" }, actionMenuGroup, "1");
|
||||
list.add(copyNoSpacesAction);
|
||||
list.add(copyInstructionAction);
|
||||
list.add(copyInstructionWithCommentsAction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gathers the search strings for each instruction and returns them as a
|
||||
* single string.
|
||||
|
@ -462,6 +442,10 @@ public class PreviewTable extends AbstractInstructionTable {
|
|||
private void createContextMenuActions() {
|
||||
String owner = getClass().getSimpleName();
|
||||
|
||||
InstructionSearchPlugin plugin = getPlugin();
|
||||
PluginTool tool = plugin.getTool();
|
||||
tool.setMenuGroup(new String[] { "Copy Special" }, actionMenuGroup, "1");
|
||||
|
||||
createCopyNoSpacesAction(owner);
|
||||
copyNoSpacesAction.setPopupMenuData(
|
||||
new MenuData(new String[] { "Copy Special", "Selected instructions (no spaces)" },
|
||||
|
@ -479,6 +463,10 @@ public class PreviewTable extends AbstractInstructionTable {
|
|||
new MenuData(new String[] { "Copy Special", "Selected Instructions (with comments)" },
|
||||
ResourceManager.loadImage("images/page_white_copy.png"), actionMenuGroup,
|
||||
MenuData.NO_MNEMONIC, Integer.toString(1)));
|
||||
|
||||
dialog.addAction(copyNoSpacesAction);
|
||||
dialog.addAction(copyInstructionAction);
|
||||
dialog.addAction(copyInstructionWithCommentsAction);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -510,7 +498,14 @@ public class PreviewTable extends AbstractInstructionTable {
|
|||
Clipboard clip = GClipboard.getSystemClipboard();
|
||||
clip.setContents(sel, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
return context.getSourceComponent() == PreviewTable.this;
|
||||
}
|
||||
};
|
||||
|
||||
copyInstructionWithCommentsAction.setHelpLocation(dialog.getHelpLocatdion());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -531,7 +526,14 @@ public class PreviewTable extends AbstractInstructionTable {
|
|||
Clipboard clip = GClipboard.getSystemClipboard();
|
||||
clip.setContents(sel, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
return context.getSourceComponent() == PreviewTable.this;
|
||||
}
|
||||
};
|
||||
|
||||
copyInstructionAction.setHelpLocation(dialog.getHelpLocatdion());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -552,7 +554,14 @@ public class PreviewTable extends AbstractInstructionTable {
|
|||
Clipboard clip = GClipboard.getSystemClipboard();
|
||||
clip.setContents(sel, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
return context.getSourceComponent() == PreviewTable.this;
|
||||
}
|
||||
};
|
||||
|
||||
copyNoSpacesAction.setHelpLocation(dialog.getHelpLocatdion());
|
||||
}
|
||||
|
||||
private class BinaryAction extends AbstractAction {
|
||||
|
|
|
@ -652,7 +652,8 @@ class MemoryMapProvider extends ComponentProviderAdapter {
|
|||
|
||||
private class MemoryMapTable extends GhidraTable {
|
||||
MemoryMapTable(TableModel model) {
|
||||
super(model, true);
|
||||
super(model);
|
||||
setAutoEditEnabled(true);
|
||||
setActionsEnabled(true);
|
||||
setVisibleRowCount(10);
|
||||
}
|
||||
|
|
|
@ -15,6 +15,15 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.programtree;
|
||||
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import ghidra.app.events.ViewChangedPluginEvent;
|
||||
import ghidra.app.services.GoToService;
|
||||
import ghidra.app.services.ViewManagerService;
|
||||
|
@ -25,15 +34,6 @@ import ghidra.program.util.*;
|
|||
import ghidra.util.Msg;
|
||||
import ghidra.util.task.*;
|
||||
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
|
||||
import docking.action.DockingAction;
|
||||
|
||||
/**
|
||||
* Provides a view of the program tree.
|
||||
*/
|
||||
|
@ -51,9 +51,6 @@ class TreeViewProvider implements ViewProviderService {
|
|||
|
||||
private final static int DELAY = 500;
|
||||
|
||||
/**
|
||||
* Constructor for TreeViewProvider.
|
||||
*/
|
||||
public TreeViewProvider(String treeName, final ProgramTreePlugin plugin) {
|
||||
|
||||
treePanel = new ProgramTreePanel(treeName, plugin);
|
||||
|
@ -72,25 +69,16 @@ class TreeViewProvider implements ViewProviderService {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.app.plugin.core.programtree.ViewProviderService#getViewComponent()
|
||||
*/
|
||||
@Override
|
||||
public JComponent getViewComponent() {
|
||||
return treePanel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.app.plugin.core.programtree.ViewProviderService#getViewName()
|
||||
*/
|
||||
@Override
|
||||
public String getViewName() {
|
||||
return treePanel.getTreeName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.app.plugin.core.programtree.ViewProviderService#setHasFocus(JComponent, boolean)
|
||||
*/
|
||||
@Override
|
||||
public void setHasFocus(boolean hasFocus) {
|
||||
treePanel.setHasFocus(hasFocus);
|
||||
|
@ -107,33 +95,26 @@ class TreeViewProvider implements ViewProviderService {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.app.plugin.core.programtree.ViewProviderService#getActivePopupObject(MouseEvent)
|
||||
*/
|
||||
@Override
|
||||
public Object getActivePopupObject(MouseEvent event) {
|
||||
return treePanel.prepareSelectionForPopup(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.app.plugin.core.programtree.ViewProviderService#getActiveObject()
|
||||
*/
|
||||
@Override
|
||||
public ActionContext getActionContext(MouseEvent event) {
|
||||
return new ActionContext().setContextObject(getActivePopupObject(event));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getActiveObject() {
|
||||
return treePanel.getSelectedNode();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.app.plugin.core.programtree.ViewProviderService#getToolBarActions()
|
||||
*/
|
||||
@Override
|
||||
public DockingAction[] getToolBarActions() {
|
||||
return plugin.getToolBarActions();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.app.plugin.core.programtree.ViewProviderService#viewClosed()
|
||||
*/
|
||||
@Override
|
||||
public boolean viewClosed() {
|
||||
if (program == null) {
|
||||
|
@ -146,9 +127,6 @@ class TreeViewProvider implements ViewProviderService {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.app.plugin.core.programtree.ViewProviderService#viewDeleted()
|
||||
*/
|
||||
@Override
|
||||
public boolean viewDeleted() {
|
||||
if (program == null) {
|
||||
|
@ -161,9 +139,6 @@ class TreeViewProvider implements ViewProviderService {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.app.plugin.core.programtree.ViewProviderService#viewRenamed(String)
|
||||
*/
|
||||
@Override
|
||||
public boolean viewRenamed(String newName) {
|
||||
if (program == null) {
|
||||
|
@ -177,9 +152,6 @@ class TreeViewProvider implements ViewProviderService {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.app.plugin.core.programtree.ViewProviderService#addToView(ProgramLocation)
|
||||
*/
|
||||
@Override
|
||||
public AddressSetView addToView(ProgramLocation loc) {
|
||||
if (program != null && loc != null && loc.getAddress() != null) {
|
||||
|
@ -192,9 +164,6 @@ class TreeViewProvider implements ViewProviderService {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.app.plugin.core.programtree.ViewProviderService#getCurrentView()
|
||||
*/
|
||||
@Override
|
||||
public AddressSetView getCurrentView() {
|
||||
return view;
|
||||
|
@ -212,14 +181,14 @@ class TreeViewProvider implements ViewProviderService {
|
|||
ProgramModule root = program.getListing().getRootModule(group.getTreeName());
|
||||
ProgramModule[] parents = group.getParents();
|
||||
if (parents != null && parents.length > 0) {
|
||||
for (int i = 0; i < parents.length; i++) {
|
||||
LinkedList<String> myList = new LinkedList<String>(list);
|
||||
myList.addFirst(parents[i].getName());
|
||||
if (parents[i] == root) {
|
||||
for (ProgramModule parent : parents) {
|
||||
LinkedList<String> myList = new LinkedList<>(list);
|
||||
myList.addFirst(parent.getName());
|
||||
if (parent == root) {
|
||||
pathNameList.add(myList);
|
||||
}
|
||||
else {
|
||||
setAncestorList(parents[i], myList, pathNameList);
|
||||
setAncestorList(parent, myList, pathNameList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -278,15 +247,12 @@ class TreeViewProvider implements ViewProviderService {
|
|||
|
||||
/**
|
||||
* Set the tree selection.
|
||||
* @param groupPaths
|
||||
* @param paths the paths to select
|
||||
*/
|
||||
void setGroupSelection(GroupPath[] paths) {
|
||||
treePanel.setGroupSelection(paths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write group paths in the view.
|
||||
*/
|
||||
void writeDataState(SaveState saveState) {
|
||||
GroupView currentView = treePanel.getGroupView();
|
||||
String treeName = treePanel.getTreeName();
|
||||
|
@ -299,9 +265,6 @@ class TreeViewProvider implements ViewProviderService {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the state from save state object.
|
||||
*/
|
||||
void readDataState(SaveState saveState) {
|
||||
String treeName = treePanel.getTreeName();
|
||||
int numGroups = saveState.getInt(NUMBER_OF_GROUPS + treeName, 0);
|
||||
|
@ -329,9 +292,6 @@ class TreeViewProvider implements ViewProviderService {
|
|||
return treePanel.getDnDTree();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the address set currently being viewed.
|
||||
*/
|
||||
AddressSet getView() {
|
||||
if (program == null) {
|
||||
return new AddressSet();
|
||||
|
@ -342,8 +302,8 @@ class TreeViewProvider implements ViewProviderService {
|
|||
return set;
|
||||
}
|
||||
String treeName = treePanel.getTreeName();
|
||||
for (int i = 0; i < gp.length; i++) {
|
||||
Group group = gp[i].getGroup(program, treeName);
|
||||
for (GroupPath element : gp) {
|
||||
Group group = element.getGroup(program, treeName);
|
||||
if (group == null) {
|
||||
continue;
|
||||
}
|
||||
|
@ -365,8 +325,8 @@ class TreeViewProvider implements ViewProviderService {
|
|||
return;
|
||||
}
|
||||
view = getView();
|
||||
plugin.firePluginEvent(new ViewChangedPluginEvent(plugin.getName(),
|
||||
treePanel.getTreeName(), view));
|
||||
plugin.firePluginEvent(
|
||||
new ViewChangedPluginEvent(plugin.getName(), treePanel.getTreeName(), view));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -379,7 +339,7 @@ class TreeViewProvider implements ViewProviderService {
|
|||
if (fragment == null) {
|
||||
return;
|
||||
}
|
||||
LinkedList<String> list = new LinkedList<String>();
|
||||
LinkedList<String> list = new LinkedList<>();
|
||||
list.add(fragment.getName());
|
||||
Group group = fragment;
|
||||
while (group != null) {
|
||||
|
@ -408,8 +368,8 @@ class TreeViewProvider implements ViewProviderService {
|
|||
}
|
||||
else {
|
||||
Group[] groups = ((ProgramModule) group).getChildren();
|
||||
for (int i = 0; i < groups.length; i++) {
|
||||
getAddressSet(groups[i], set);
|
||||
for (Group group2 : groups) {
|
||||
getAddressSet(group2, set);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -439,11 +399,11 @@ class TreeViewProvider implements ViewProviderService {
|
|||
if (fragment == null) {
|
||||
return;
|
||||
}
|
||||
LinkedList<String> list = new LinkedList<String>();
|
||||
LinkedList<String> list = new LinkedList<>();
|
||||
list.add(fragment.getName());
|
||||
Group group = fragment;
|
||||
|
||||
ArrayList<LinkedList<String>> pathNameList = new ArrayList<LinkedList<String>>();
|
||||
ArrayList<LinkedList<String>> pathNameList = new ArrayList<>();
|
||||
// need GroupPath for all occurrences of fragment
|
||||
setAncestorList(group, list, pathNameList);
|
||||
|
||||
|
|
|
@ -196,11 +196,13 @@ public class ViewManagerComponentProvider extends ComponentProviderAdapter
|
|||
if (currentProgram == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (event != null) {
|
||||
return new ProgramActionContext(this, currentProgram, getActivePopupObject(event));
|
||||
return new ProgramActionContext(this, currentProgram, viewPanel,
|
||||
getActivePopupObject(event));
|
||||
}
|
||||
|
||||
return new ProgramActionContext(this, currentProgram, getFocusedContext());
|
||||
return new ProgramActionContext(this, currentProgram, viewPanel, getFocusedContext());
|
||||
}
|
||||
|
||||
private Object getFocusedContext() {
|
||||
|
|
|
@ -15,14 +15,14 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.programtree;
|
||||
|
||||
import ghidra.app.services.ViewService;
|
||||
import ghidra.framework.plugintool.ServiceInfo;
|
||||
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import ghidra.app.services.ViewService;
|
||||
import ghidra.framework.plugintool.ServiceInfo;
|
||||
|
||||
/**
|
||||
* Define methods for notification of which service becomes active;
|
||||
|
@ -30,55 +30,62 @@ import docking.action.DockingAction;
|
|||
*/
|
||||
@ServiceInfo(description = "Provide a view that is managed by the ViewManagerService")
|
||||
public interface ViewProviderService extends ViewService {
|
||||
|
||||
/**
|
||||
* Get the viewer component.
|
||||
*/
|
||||
public JComponent getViewComponent();
|
||||
|
||||
/**
|
||||
* Get the name of this view.
|
||||
*/
|
||||
public String getViewName();
|
||||
/**
|
||||
* Get the viewer component.
|
||||
*/
|
||||
public JComponent getViewComponent();
|
||||
|
||||
/**
|
||||
* Set whether or not the component that is showing has focus.
|
||||
/**
|
||||
* Get the name of this view.
|
||||
*/
|
||||
public String getViewName();
|
||||
|
||||
/**
|
||||
* Set whether or not the component that is showing has focus.
|
||||
* @param hasFocus true if the component has focus
|
||||
*/
|
||||
public void setHasFocus(boolean hasFocus);
|
||||
*/
|
||||
public void setHasFocus(boolean hasFocus);
|
||||
|
||||
/**
|
||||
* Return the object under the mouse location for the popup
|
||||
* @param event mouse event generated when the right mouse button is pressed
|
||||
*/
|
||||
public Object getActivePopupObject(MouseEvent event);
|
||||
|
||||
/**
|
||||
* Get the actions that would go on a tool bar.
|
||||
*/
|
||||
public DockingAction[] getToolBarActions();
|
||||
|
||||
/**
|
||||
* Notification that this view is closed.
|
||||
* @return true if the view can be closed
|
||||
*/
|
||||
public boolean viewClosed();
|
||||
/**
|
||||
* Return the object under the mouse location for the popup
|
||||
* @param event mouse event generated when the right mouse button is pressed
|
||||
*/
|
||||
public Object getActivePopupObject(MouseEvent event);
|
||||
|
||||
/**
|
||||
* Returns the current action context for this view service
|
||||
* @param event the mouse event
|
||||
* @return the context
|
||||
*/
|
||||
public ActionContext getActionContext(MouseEvent event);
|
||||
|
||||
/**
|
||||
* Get the actions that would go on a tool bar.
|
||||
*/
|
||||
public DockingAction[] getToolBarActions();
|
||||
|
||||
/**
|
||||
* Notification that this view is closed.
|
||||
* @return true if the view can be closed
|
||||
*/
|
||||
public boolean viewClosed();
|
||||
|
||||
/**
|
||||
* Notification that this view should be deleted
|
||||
* @return true if the view can be deleted
|
||||
*/
|
||||
public boolean viewDeleted();
|
||||
|
||||
|
||||
/**
|
||||
* Notification that this view should be renamed to newName.
|
||||
* @return true if the rename is allowed
|
||||
*/
|
||||
public boolean viewRenamed(String newName);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the context for the current selection.
|
||||
*/
|
||||
public Object getActiveObject();
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -651,7 +651,7 @@ public class GhidraScriptEditorComponentProvider extends ComponentProvider {
|
|||
|
||||
@Override
|
||||
public ActionContext getActionContext(MouseEvent event) {
|
||||
return new ActionContext(this, this);
|
||||
return createContext(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,12 +15,9 @@
|
|||
*/
|
||||
package ghidra.app.plugin.debug;
|
||||
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import db.DBHandle;
|
||||
import docking.ActionContext;
|
||||
import docking.WindowPosition;
|
||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
|
@ -77,11 +74,6 @@ public class DbViewerProvider extends ComponentProviderAdapter {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionContext getActionContext(MouseEvent event) {
|
||||
return new ActionContext(this, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getComponent() {
|
||||
if (comp == null) {
|
||||
|
|
|
@ -219,7 +219,7 @@ public class PropertyManagerProvider extends ComponentProviderAdapter {
|
|||
Rectangle rowBounds =
|
||||
table.getCellRect(row, PropertyManagerTableModel.PROPERTY_NAME_COLUMN, true);
|
||||
if (rowBounds.contains(event.getPoint())) {
|
||||
return new ActionContext(this, rowBounds);
|
||||
return createContext(rowBounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,8 +24,7 @@ import javax.swing.*;
|
|||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.ComponentProvider;
|
||||
import docking.*;
|
||||
import docking.action.*;
|
||||
import docking.help.Help;
|
||||
import docking.help.HelpService;
|
||||
|
@ -700,7 +699,8 @@ public class ListingCodeComparisonPanel
|
|||
NextDiffAction() {
|
||||
super("Dual Listing Go To Next Area Marker", owner);
|
||||
setEnabled(true);
|
||||
setKeyBindingData(new KeyBindingData('N', InputEvent.CTRL_MASK | InputEvent.ALT_MASK));
|
||||
setKeyBindingData(new KeyBindingData('N',
|
||||
DockingUtils.CONTROL_KEY_MODIFIER_MASK | InputEvent.ALT_DOWN_MASK));
|
||||
setDescription("Go to the next highlighted area.");
|
||||
setPopupMenuData(new MenuData(new String[] { "Go To Next Highlighted Area" },
|
||||
NEXT_DIFF_ICON, DIFF_NAVIGATE_GROUP));
|
||||
|
@ -738,7 +738,8 @@ public class ListingCodeComparisonPanel
|
|||
PreviousDiffAction() {
|
||||
super("Dual Listing Go To Previous Area Marker", owner);
|
||||
setEnabled(true);
|
||||
setKeyBindingData(new KeyBindingData('P', InputEvent.CTRL_MASK | InputEvent.ALT_MASK));
|
||||
setKeyBindingData(new KeyBindingData('P',
|
||||
DockingUtils.CONTROL_KEY_MODIFIER_MASK | InputEvent.ALT_DOWN_MASK));
|
||||
setDescription("Go to the previous highlighted area.");
|
||||
setPopupMenuData(new MenuData(new String[] { "Go To Previous Highlighted Area" },
|
||||
PREVIOUS_DIFF_ICON, DIFF_NAVIGATE_GROUP));
|
||||
|
@ -796,9 +797,6 @@ public class ListingCodeComparisonPanel
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the listings are showing the entire program.
|
||||
*/
|
||||
public boolean isEntireListingShowing() {
|
||||
return isShowingEntireListing;
|
||||
}
|
||||
|
@ -857,9 +855,6 @@ public class ListingCodeComparisonPanel
|
|||
toggleHeaderAction.setSelected(show);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the listings are being displayed side by side.
|
||||
*/
|
||||
public boolean isSideBySide() {
|
||||
return isSideBySide;
|
||||
}
|
||||
|
@ -2024,18 +2019,18 @@ public class ListingCodeComparisonPanel
|
|||
|
||||
Object leftMarginContext = getContextForMarginPanels(leftPanel, event);
|
||||
if (leftMarginContext != null) {
|
||||
return new ActionContext(provider, leftMarginContext);
|
||||
return new ActionContext(provider).setContextObject(leftMarginContext);
|
||||
}
|
||||
Object rightMarginContext = getContextForMarginPanels(rightPanel, event);
|
||||
if (rightMarginContext != null) {
|
||||
return new ActionContext(provider, rightMarginContext);
|
||||
return new ActionContext(provider).setContextObject(rightMarginContext);
|
||||
}
|
||||
|
||||
Object source = event.getSource();
|
||||
if (source instanceof FieldHeaderComp) {
|
||||
FieldHeaderLocation fieldHeaderLocation =
|
||||
leftPanel.getFieldHeader().getFieldHeaderLocation(event.getPoint());
|
||||
return new ActionContext(provider, fieldHeaderLocation);
|
||||
return new ActionContext(provider).setContextObject(fieldHeaderLocation);
|
||||
}
|
||||
|
||||
Navigatable focusedNavigatable = dualListingPanel.getFocusedNavigatable();
|
||||
|
@ -2043,7 +2038,7 @@ public class ListingCodeComparisonPanel
|
|||
new DualListingActionContext(provider, focusedNavigatable);
|
||||
myActionContext.setContextObject(this);
|
||||
myActionContext.setCodeComparisonPanel(this);
|
||||
myActionContext.setSource(source);
|
||||
myActionContext.setSourceObject(source);
|
||||
return myActionContext;
|
||||
}
|
||||
|
||||
|
@ -2607,13 +2602,13 @@ public class ListingCodeComparisonPanel
|
|||
// Are we on a marker margin of the left listing? Return that margin's context.
|
||||
Object sourceMarginContextObject = getContextObjectForMarginPanels(sourcePanel, event);
|
||||
if (sourceMarginContextObject != null) {
|
||||
return new ActionContext(provider, sourceMarginContextObject);
|
||||
return new ActionContext(provider).setContextObject(sourceMarginContextObject);
|
||||
}
|
||||
// Are we on a marker margin of the right listing? Return that margin's context.
|
||||
Object destinationMarginContextObject =
|
||||
getContextObjectForMarginPanels(destinationPanel, event);
|
||||
if (destinationMarginContextObject != null) {
|
||||
return new ActionContext(provider, destinationMarginContextObject);
|
||||
return new ActionContext(provider).setContextObject(destinationMarginContextObject);
|
||||
}
|
||||
|
||||
// If the action is on the Field Header of the left listing panel return an
|
||||
|
@ -2621,7 +2616,7 @@ public class ListingCodeComparisonPanel
|
|||
if (sourceComponent instanceof FieldHeaderComp) {
|
||||
FieldHeaderLocation fieldHeaderLocation =
|
||||
sourcePanel.getFieldHeader().getFieldHeaderLocation(event.getPoint());
|
||||
return new ActionContext(provider, fieldHeaderLocation);
|
||||
return new ActionContext(provider).setContextObject(fieldHeaderLocation);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -259,7 +259,7 @@ public class OpenVersionedFileDialog extends DataTreeDialog {
|
|||
return context;
|
||||
}
|
||||
|
||||
ActionContext actionContext = new ActionContext(null, event.getSource(), this);
|
||||
ActionContext actionContext = new ActionContext(null, this, event.getComponent());
|
||||
actionContext.setMouseEvent(event);
|
||||
|
||||
return actionContext;
|
||||
|
|
|
@ -35,7 +35,7 @@ public class FSBActionContext extends ActionContext {
|
|||
* @param gTree {@link FileSystemBrowserPlugin} provider tree.
|
||||
*/
|
||||
public FSBActionContext(ComponentProvider provider, Object contextObject, GTree gTree) {
|
||||
super(provider, contextObject);
|
||||
super(provider, contextObject, gTree);
|
||||
this.gTree = gTree;
|
||||
}
|
||||
|
||||
|
|
|
@ -51,66 +51,10 @@ public class GhidraTable extends GTable {
|
|||
|
||||
public GhidraTable() {
|
||||
super();
|
||||
|
||||
}
|
||||
|
||||
public GhidraTable(TableModel model) {
|
||||
super(model);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new GhidraTable 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 GhidraTable(TableModel dm, boolean allowAutoEdit) {
|
||||
super(dm, allowAutoEdit);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a <code>GhidraTable</code> to display the values in the two dimensional array,
|
||||
* <code>rowData</code>, with column names, <code>columnNames</code>.
|
||||
* <code>rowData</code> is an array of rows, so the value of the cell at row 1,
|
||||
* column 5 can be obtained with the following code:
|
||||
* <p>
|
||||
* <pre> rowData[1][5]; </pre>
|
||||
* <p>
|
||||
* All rows must be of the same length as <code>columnNames</code>.
|
||||
* <p>
|
||||
* @param rowData the data for the new table
|
||||
* @param columnNames names of each column
|
||||
*/
|
||||
public GhidraTable(Object[][] rowData, Object[] columnNames) {
|
||||
super(rowData, columnNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a <code>GhidraTable</code> to display the values in the two dimensional array,
|
||||
* <code>rowData</code>, with column names, <code>columnNames</code>.
|
||||
* <code>rowData</code> is an array of rows, so the value of the cell at row 1,
|
||||
* column 5 can be obtained with the following code:
|
||||
* <p>
|
||||
* <pre> rowData[1][5]; </pre>
|
||||
* <p>
|
||||
* All rows must be of the same length as <code>columnNames</code>.
|
||||
* <p>
|
||||
* 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 rowData the data for the new table
|
||||
* @param columnNames names of each column
|
||||
* @param allowAutoEdit true if auto-editing is allowed
|
||||
*/
|
||||
public GhidraTable(Object[][] rowData, Object[] columnNames, boolean allowAutoEdit) {
|
||||
super(rowData, columnNames, allowAutoEdit);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package ghidra.util.table.actions;
|
||||
|
||||
import java.awt.Component;
|
||||
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import docking.ActionContext;
|
||||
|
@ -36,10 +38,9 @@ import resources.Icons;
|
|||
*/
|
||||
public class MakeProgramSelectionAction extends DockingAction {
|
||||
|
||||
private GhidraTable table;
|
||||
|
||||
// we will have one of these fields be non-null after construction
|
||||
private Plugin plugin;
|
||||
private GhidraTable table;
|
||||
|
||||
/**
|
||||
* Special constructor for clients that do not have a plugin. Clients using this
|
||||
|
@ -92,8 +93,8 @@ public class MakeProgramSelectionAction extends DockingAction {
|
|||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
|
||||
Object contextObject = context.getContextObject();
|
||||
if (contextObject != table) {
|
||||
Component component = context.getSourceComponent();
|
||||
if (component != table) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,8 +33,7 @@ import ghidra.framework.options.ToolOptions;
|
|||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||
import ghidra.test.TestEnv;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.SpyErrorLogger;
|
||||
import ghidra.util.*;
|
||||
import resources.Icons;
|
||||
import resources.ResourceManager;
|
||||
|
||||
|
@ -335,8 +334,11 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio
|
|||
KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, DockingUtils.CONTROL_KEY_MODIFIER_MASK);
|
||||
setKeyBindingViaF4Dialog_FromCloseButton(controlEsc);
|
||||
|
||||
// Note: there may be a test focus issue here. If this test fails sporadically due to
|
||||
// how the action context is generated (it depends on focus). It is only useful to fail
|
||||
// here in development mode.
|
||||
pressKey(controlEsc);
|
||||
assertProviderIsHidden();
|
||||
assertProviderIsHidden_InNonBatchMode();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -461,9 +463,18 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio
|
|||
// runSwing(() -> tool.isActive(provider)));
|
||||
}
|
||||
|
||||
private void assertProviderIsHidden() {
|
||||
assertFalse("The test provider is showing, but should be hidden",
|
||||
runSwing(() -> tool.isVisible(provider)));
|
||||
private void assertProviderIsHidden_InNonBatchMode() {
|
||||
|
||||
boolean isVisible = runSwing(() -> tool.isVisible(provider));
|
||||
if (!SystemUtilities.isInTestingBatchMode()) {
|
||||
assertFalse("The test provider is showing, but should be hidden", isVisible);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isVisible) {
|
||||
Msg.error(this,
|
||||
"Provider should not be visible after pressing the 'close provider' key bindings");
|
||||
}
|
||||
}
|
||||
|
||||
private void assertNoToolbarAction() {
|
||||
|
|
|
@ -22,7 +22,6 @@ import java.util.Set;
|
|||
|
||||
import org.junit.*;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingActionIf;
|
||||
import ghidra.app.plugin.core.programtree.ProgramTreePlugin;
|
||||
import ghidra.app.plugin.core.programtree.ViewProviderService;
|
||||
|
@ -99,7 +98,7 @@ public class ModuleAlgorithmPluginTest extends AbstractGhidraHeadedIntegrationTe
|
|||
|
||||
getContextObject(vps);
|
||||
|
||||
performAction(CollectionUtils.any(actions), new ActionContext(null, context), true);
|
||||
performAction(CollectionUtils.any(actions), createContext(context), true);
|
||||
|
||||
waitForTasks();
|
||||
program.flushEvents();
|
||||
|
|
|
@ -433,7 +433,7 @@ public class BookmarkPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
Address address = bookmark.getAddress();
|
||||
DeleteBookmarkAction action = new DeleteBookmarkAction(plugin, bookmark, true);
|
||||
MarkerLocation markerLocation = new MarkerLocation(null, address, 0, 0);
|
||||
performAction(action, new ActionContext(null, markerLocation), true);
|
||||
performAction(action, createContext(markerLocation), true);
|
||||
list = getBookmarks(program.getBookmarkManager());
|
||||
assertFalse(list.contains(bookmark));
|
||||
|
||||
|
@ -441,7 +441,7 @@ public class BookmarkPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
address = bookmark.getAddress();
|
||||
action = new DeleteBookmarkAction(plugin, bookmark, false);
|
||||
markerLocation = new MarkerLocation(null, address, 0, 0);
|
||||
performAction(action, new ActionContext(null, markerLocation), true);
|
||||
performAction(action, createContext(markerLocation), true);
|
||||
list = getBookmarks(program.getBookmarkManager());
|
||||
assertFalse(list.contains(bookmark));
|
||||
}
|
||||
|
@ -543,8 +543,8 @@ public class BookmarkPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
CreateStructureCmd cmd = new CreateStructureCmd(addr("0100b6db"), 20);
|
||||
applyCmd(program, cmd);
|
||||
|
||||
List<DockingActionIf> actions = runSwing(() -> plugin.getPopupActions(
|
||||
null, new ActionContext(null, new MarkerLocation(null, addr("0100b6db"), 0, 0))));
|
||||
List<DockingActionIf> actions = runSwing(() -> plugin.getPopupActions(null,
|
||||
createContext(new MarkerLocation(null, addr("0100b6db"), 0, 0))));
|
||||
assertEquals(10, actions.size());
|
||||
}
|
||||
|
||||
|
|
|
@ -131,7 +131,7 @@ public class HeaderActionsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
DockingAction headerAction = getHeaderAction("Remove All Fields");
|
||||
FieldHeaderLocation loc = new FieldHeaderLocation(functionFormat, factorys[0], 0, 0);
|
||||
ActionContext context = new ActionContext(provider, loc);
|
||||
ActionContext context = createContext(provider, loc);
|
||||
performAction(headerAction, context, false);
|
||||
pressContinueOnResetFormatDialog("Remove All Fields?");
|
||||
|
||||
|
@ -151,7 +151,7 @@ public class HeaderActionsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
selectHeaderField(factories[0]);
|
||||
FieldHeaderLocation loc = new FieldHeaderLocation(functionFormat, factories[0], 0, 0);
|
||||
ActionContext context = new ActionContext(provider, loc);
|
||||
ActionContext context = createContext(provider, loc);
|
||||
|
||||
DockingAction headerAction = getHeaderAction("Add Spacer Field");
|
||||
performAction(headerAction, context, true);
|
||||
|
@ -173,7 +173,7 @@ public class HeaderActionsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
selectHeaderField(factories[0]);
|
||||
|
||||
FieldHeaderLocation loc = new FieldHeaderLocation(functionFormat, factories[0], 0, 0);
|
||||
ActionContext context = new ActionContext(provider, loc);
|
||||
ActionContext context = createContext(provider, loc);
|
||||
|
||||
DockingAction headerAction = getHeaderAction("SetTextAction");
|
||||
performAction(headerAction, context, false);
|
||||
|
@ -200,7 +200,7 @@ public class HeaderActionsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
assertTrue(factories[1].isEnabled());
|
||||
|
||||
FieldHeaderLocation loc = new FieldHeaderLocation(functionFormat, factories[1], 0, 0);
|
||||
ActionContext context = new ActionContext(provider, loc);
|
||||
ActionContext context = createContext(provider, loc);
|
||||
|
||||
DockingAction headerAction = getHeaderAction("Disable Field");
|
||||
performAction(headerAction, context, true);
|
||||
|
@ -222,7 +222,7 @@ public class HeaderActionsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
assertTrue(factories[1] instanceof FunctionSignatureFieldFactory);
|
||||
|
||||
FieldHeaderLocation loc = new FieldHeaderLocation(functionFormat, factories[1], 0, 1);
|
||||
ActionContext context = new ActionContext(provider, loc);
|
||||
ActionContext context = createContext(provider, loc);
|
||||
|
||||
DockingAction headerAction = getHeaderAction("Remove Field");
|
||||
performAction(headerAction, context, true);
|
||||
|
@ -242,7 +242,7 @@ public class HeaderActionsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
functionFormat.removeAllFactories();
|
||||
|
||||
FieldHeaderLocation loc = new FieldHeaderLocation(functionFormat, factories[1], 0, 1);
|
||||
ActionContext context = new ActionContext(provider, loc);
|
||||
ActionContext context = createContext(provider, loc);
|
||||
|
||||
DockingAction headerAction = getHeaderAction("Add All Field");
|
||||
performAction(headerAction, context, true);
|
||||
|
@ -272,8 +272,7 @@ public class HeaderActionsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
}
|
||||
|
||||
private void enterTextIntoDialog(String input) {
|
||||
InputDialog dialog =
|
||||
waitForDialogComponent(null, InputDialog.class, DEFAULT_WINDOW_TIMEOUT);
|
||||
InputDialog dialog = waitForDialogComponent(InputDialog.class);
|
||||
assertNotNull("Never found the spacer text input dialog", dialog);
|
||||
JTextComponent textField =
|
||||
(JTextComponent) findComponentByName(dialog, "input.dialog.text.field.0");
|
||||
|
@ -291,7 +290,7 @@ public class HeaderActionsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
}
|
||||
|
||||
private void pressContinueOnResetFormatDialog(String title) {
|
||||
Window window = waitForWindow(title, DEFAULT_WINDOW_TIMEOUT);
|
||||
Window window = waitForWindow(title);
|
||||
assertNotNull("Never found the dialog: " + title, window);
|
||||
pressButtonByText(window, "Continue");
|
||||
waitForSwing();
|
||||
|
|
|
@ -275,14 +275,14 @@ public class HeaderTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
public void testInsertDeleteRow() {
|
||||
FieldFormatModel model = header.getHeaderTab().getModel();
|
||||
InsertRowAction act = new InsertRowAction("Test", header);
|
||||
act.isEnabledForContext(
|
||||
new ActionContext(null, new FieldHeaderLocation(model, null, 0, 0)));
|
||||
act.isEnabledForContext(new ActionContext(cb.getProvider()).setContextObject(
|
||||
new FieldHeaderLocation(model, null, 0, 0)));
|
||||
performAction(act, true);
|
||||
assertEquals(8, model.getNumRows());
|
||||
assertEquals(0, model.getNumFactorys(0));
|
||||
RemoveRowAction act2 = new RemoveRowAction("Test", header);
|
||||
act2.isEnabledForContext(
|
||||
new ActionContext(null, new FieldHeaderLocation(model, null, 0, 0)));
|
||||
act2.isEnabledForContext(new ActionContext(cb.getProvider()).setContextObject(
|
||||
new FieldHeaderLocation(model, null, 0, 0)));
|
||||
performAction(act2, true);
|
||||
assertEquals(7, model.getNumRows());
|
||||
assertEquals(2, model.getNumFactorys(0));
|
||||
|
@ -294,8 +294,8 @@ public class HeaderTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
ListingField bf = cb.getCurrentField();
|
||||
int startX = bf.getStartX();
|
||||
InsertRowAction act = new InsertRowAction("Test", header);
|
||||
act.isEnabledForContext(
|
||||
new ActionContext(null, new FieldHeaderLocation(model, null, 0, 0)));
|
||||
act.isEnabledForContext(new ActionContext(cb.getProvider()).setContextObject(
|
||||
new FieldHeaderLocation(model, null, 0, 0)));
|
||||
performAction(act, true);
|
||||
int width = bf.getWidth();
|
||||
int dragX = startX + width / 2;
|
||||
|
|
|
@ -27,6 +27,7 @@ import javax.swing.table.TableModel;
|
|||
|
||||
import org.junit.*;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingActionIf;
|
||||
import ghidra.app.cmd.memory.*;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||
|
@ -100,11 +101,12 @@ public class MemoryMapPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
for (DockingActionIf action : actions) {
|
||||
String name = action.getName();
|
||||
if (name.equals("Add Block") || name.equals("Set Image Base") ||
|
||||
name.equals("Memory Map") || name.equals("Close Window")) {
|
||||
assertTrue(action.isEnabledForContext(provider.getActionContext(null)));
|
||||
name.equals("Memory Map") || name.equals("Close Window") ||
|
||||
name.contains("Table")) {
|
||||
assertActionEnabled(action, getActionContext(), true);
|
||||
}
|
||||
else {
|
||||
assertFalse(action.isEnabledForContext(provider.getActionContext(null)));
|
||||
assertActionEnabled(action, getActionContext(), false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,10 +123,26 @@ public class MemoryMapPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
if (name.equals("Memory Map") || name.equals("Close Window")) {
|
||||
continue;
|
||||
}
|
||||
assertFalse(action.isEnabledForContext(provider.getActionContext(null)));
|
||||
assertActionEnabled(action, getActionContext(), false);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertActionEnabled(DockingActionIf action, ActionContext context,
|
||||
boolean shouldBeEnabled) {
|
||||
|
||||
String text = shouldBeEnabled ? "should be enabled" : "should be disabled";
|
||||
assertEquals("Action " + text + ", but is not: '" + action.getFullName() + "'",
|
||||
shouldBeEnabled, action.isEnabledForContext(context));
|
||||
}
|
||||
|
||||
private ActionContext getActionContext() {
|
||||
ActionContext context = provider.getActionContext(null);
|
||||
if (context == null) {
|
||||
return new ActionContext();
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBlockNameChanged() throws Exception {
|
||||
MemoryBlock block = memory.getBlock(program.getMinAddress());
|
||||
|
|
|
@ -109,21 +109,21 @@ public class AutoRenamePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
ViewProviderService vps = vmService.getCurrentViewProvider();
|
||||
|
||||
Object context = vps.getActivePopupObject(null);
|
||||
assertTrue(!renameAction.isEnabledForContext(new ActionContext(null, context)));
|
||||
assertTrue(!labelAction.isEnabledForContext(new ActionContext(null, context)));
|
||||
|
||||
assertTrue(!renameAction.isEnabledForContext(createContext(context)));
|
||||
assertTrue(!labelAction.isEnabledForContext(createContext(context)));
|
||||
|
||||
gps = new GroupPath[1];
|
||||
gps[0] = new GroupPath(new String[] { root.getName(), "DLLs", "USER32.DLL" });
|
||||
setSelection(gps);
|
||||
|
||||
context = vps.getActivePopupObject(null);
|
||||
assertTrue(renameAction.isEnabledForContext(new ActionContext(null, context)));
|
||||
assertTrue(!labelAction.isEnabledForContext(new ActionContext(null, context)));
|
||||
assertTrue(renameAction.isEnabledForContext(createContext(context)));
|
||||
assertTrue(!labelAction.isEnabledForContext(createContext(context)));
|
||||
|
||||
// fire Label program location
|
||||
Address addr = getAddr(0x10033f6);
|
||||
LabelFieldLocation loc =
|
||||
new LabelFieldLocation(program, addr, "SUB_010033f6");
|
||||
LabelFieldLocation loc = new LabelFieldLocation(program, addr, "SUB_010033f6");
|
||||
tool.firePluginEvent(new ProgramLocationPluginEvent("test", loc, program));
|
||||
|
||||
ActionContext actionContext = cb.getProvider().getActionContext(null);
|
||||
|
@ -147,7 +147,7 @@ public class AutoRenamePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
ViewManagerService vmService = tool.getService(ViewManagerService.class);
|
||||
ViewProviderService vps = vmService.getCurrentViewProvider();
|
||||
Object context = vps.getActivePopupObject(null);
|
||||
performAction(renameAction, new ActionContext(null, context), true);
|
||||
performAction(renameAction, createContext(context), true);
|
||||
program.flushEvents();
|
||||
|
||||
assertNotNull(program.getListing().getFragment("Main Tree", s.getName()));
|
||||
|
@ -166,12 +166,8 @@ public class AutoRenamePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
new LabelFieldLocation(program, addr, null, "SUB_010033f6", null, 0, 0);
|
||||
cb.goTo(loc);
|
||||
|
||||
SwingUtilities.invokeAndWait(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
labelAction.actionPerformed(cb.getProvider().getActionContext(null));
|
||||
}
|
||||
});
|
||||
SwingUtilities.invokeAndWait(
|
||||
() -> labelAction.actionPerformed(cb.getProvider().getActionContext(null)));
|
||||
program.flushEvents();
|
||||
assertNull(program.getListing().getFragment("SUB_010033f6", origName));
|
||||
assertEquals("SUB_010033f6", frag.getName());
|
||||
|
@ -182,22 +178,12 @@ public class AutoRenamePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
}
|
||||
|
||||
private void setViewToMainTree() {
|
||||
runSwing(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
service.setViewedTree("Main Tree");
|
||||
}
|
||||
});
|
||||
runSwing(() -> service.setViewedTree("Main Tree"));
|
||||
}
|
||||
|
||||
private void setSelection(final GroupPath[] gps) {
|
||||
|
||||
runSwing(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
service.setGroupSelection(gps);
|
||||
}
|
||||
});
|
||||
runSwing(() -> service.setGroupSelection(gps));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ import java.util.*;
|
|||
|
||||
import org.junit.*;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingActionIf;
|
||||
import ghidra.app.plugin.core.programtree.ProgramTreePlugin;
|
||||
import ghidra.app.plugin.core.programtree.ViewProviderService;
|
||||
|
@ -113,7 +112,8 @@ public class ModuleSortPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
Object context = vps.getActivePopupObject(null);
|
||||
for (DockingActionIf action : actions) {
|
||||
assertTrue(action.isAddToPopup(new ActionContext(null, context)));
|
||||
|
||||
assertTrue(action.isAddToPopup(vps.getActionContext(null)));
|
||||
}
|
||||
|
||||
gps = new GroupPath[1];
|
||||
|
@ -122,7 +122,7 @@ public class ModuleSortPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
context = vps.getActivePopupObject(null);
|
||||
for (DockingActionIf action : actions) {
|
||||
assertTrue(!action.isAddToPopup(new ActionContext(null, context)));
|
||||
assertTrue(!action.isAddToPopup(vps.getActionContext(null)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -159,11 +159,10 @@ public class ModuleSortPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
ViewManagerService vmService = tool.getService(ViewManagerService.class);
|
||||
ViewProviderService vps = vmService.getCurrentViewProvider();
|
||||
|
||||
Object context = vps.getActivePopupObject(null);
|
||||
|
||||
for (DockingActionIf action : actions) {
|
||||
if (action.getName().indexOf("Name") > 0) {
|
||||
action.actionPerformed(new ActionContext(null, context));
|
||||
|
||||
action.actionPerformed(vps.getActionContext(null));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -205,7 +204,7 @@ public class ModuleSortPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
for (DockingActionIf action : actions) {
|
||||
if (action.getName().indexOf("Address") > 0) {
|
||||
action.actionPerformed(new ActionContext(null, context));
|
||||
action.actionPerformed(vps.getActionContext(null));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -232,7 +231,7 @@ public class ModuleSortPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
Object context = vps.getActivePopupObject(null);
|
||||
for (DockingActionIf action : actions) {
|
||||
assertTrue(!action.isAddToPopup(new ActionContext(null, context)));
|
||||
assertTrue(!action.isAddToPopup(vps.getActionContext(null)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -331,26 +331,6 @@ public class NextPrevAddressPluginTest extends AbstractGhidraHeadedIntegrationTe
|
|||
clickMouse(component, MouseEvent.BUTTON1, x, y, 1, 0);
|
||||
}
|
||||
|
||||
private JPopupMenu getPopupMenu(Container parent) {
|
||||
Component[] components = parent.getComponents();
|
||||
for (Component component : components) {
|
||||
if (component instanceof JPopupMenu) {
|
||||
return (JPopupMenu) component;
|
||||
}
|
||||
}
|
||||
|
||||
for (Component component : components) {
|
||||
if (component instanceof Container) {
|
||||
JPopupMenu popupMenu = getPopupMenu((Container) component);
|
||||
if (popupMenu != null) {
|
||||
return popupMenu;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
// let caution fly
|
||||
private JButton findButtonForAction(DockingWindowManager windowManager, DockingAction action) {
|
||||
|
|
|
@ -152,7 +152,7 @@ public class ProviderNavigationPluginTest extends AbstractProgramBasedTest {
|
|||
}
|
||||
|
||||
private void forceActivate(ComponentProvider provider) {
|
||||
ActionContext context = new ActionContext(provider, provider);
|
||||
ActionContext context = new ActionContext(provider);
|
||||
for (DockingContextListener l : testContextListeners) {
|
||||
l.contextChanged(context);
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ import javax.swing.table.*;
|
|||
import org.jdom.Element;
|
||||
import org.junit.*;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.*;
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.action.ToggleDockingAction;
|
||||
import docking.widgets.filter.*;
|
||||
|
@ -117,7 +117,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
@Test
|
||||
public void testNavigation() throws Exception {
|
||||
openProgram("notepad");
|
||||
openProgram("sample");
|
||||
int row = findRow("ghidra", "Global");
|
||||
|
||||
TableModel model = symbolTable.getModel();
|
||||
|
@ -128,7 +128,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
@Test
|
||||
public void testSortingLabelColumn() throws Exception {
|
||||
openProgram("notepad");
|
||||
openProgram("sample");
|
||||
|
||||
Rectangle rect = symbolTableHeader.getHeaderRect(SymbolTableModel.LABEL_COL);
|
||||
|
||||
|
@ -170,7 +170,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
// Note: this is somewhat of a tripwire test--it is designed to catch a major breakage
|
||||
// to the DynamicTableColumn discovery mechanism.
|
||||
//
|
||||
openProgram("notepad");
|
||||
openProgram("sample");
|
||||
|
||||
List<String> columnNames = new ArrayList<>();
|
||||
int columnCount = symbolModel.getColumnCount();
|
||||
|
@ -203,7 +203,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
@Test
|
||||
public void testSortingAddressColumn() throws Exception {
|
||||
openProgram("notepad");
|
||||
openProgram("sample");
|
||||
|
||||
Rectangle rect = symbolTableHeader.getHeaderRect(SymbolTableModel.LOCATION_COL);
|
||||
|
||||
|
@ -239,7 +239,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
@Test
|
||||
public void testSortingReferenceColumn() throws Exception {
|
||||
openProgram("notepad");
|
||||
openProgram("sample");
|
||||
|
||||
sortOnColumn(SymbolTableModel.REFS_COL);
|
||||
|
||||
|
@ -267,7 +267,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
@Test
|
||||
public void testFilter() throws Exception {
|
||||
openProgram("notepad");
|
||||
openProgram("sample");
|
||||
|
||||
performAction(setFilterAction, new ActionContext(), false);
|
||||
waitForSwing();
|
||||
|
@ -331,15 +331,6 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
assertEquals(2, symbolTable.getRowCount());
|
||||
}
|
||||
|
||||
private FilterDialog showFilterDialog() {
|
||||
|
||||
performAction(setFilterAction, false);
|
||||
|
||||
FilterDialog dialog = waitForDialogComponent(FilterDialog.class);
|
||||
assertNotNull(dialog);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilterPersistence() throws Exception {
|
||||
|
||||
|
@ -362,39 +353,9 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
assertEquals(savedXml, restoredXml);
|
||||
}
|
||||
|
||||
private void changeSomeFilterSettings(NewSymbolFilter filter) {
|
||||
//
|
||||
// Change different filter types and values. (This requires some guilty knowledge).
|
||||
//
|
||||
// Symbol type name and default state:
|
||||
//
|
||||
// Symbol Types:
|
||||
// Label filters: instruction (active), data (active), function (active)
|
||||
// Non-label filters: namespaces, classes, params, etc (all inactive)
|
||||
//
|
||||
// Advanced filters: externals, globals, entry points, locals, etc (all inactive)
|
||||
//
|
||||
// Symbol Source Types: user defined (active), imported (active),
|
||||
// default label (inactive), default function, analysis (active)
|
||||
//
|
||||
|
||||
boolean active = true;
|
||||
boolean inactive = false;
|
||||
filter.setFilter("User Defined", inactive);
|
||||
filter.setFilter("Default (Labels)", active);
|
||||
|
||||
filter.setFilter("Function Labels", inactive);
|
||||
|
||||
filter.setFilter("Local Variables", active);
|
||||
|
||||
filter.setFilter("Register Variables", active);
|
||||
filter.setFilter("Subroutines", active);
|
||||
filter.setFilter("Non-Primary Labels", active);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEditing() throws Exception {
|
||||
openProgram("notepad");
|
||||
openProgram("sample");
|
||||
|
||||
waitForNotBusy(symbolTable);
|
||||
|
||||
|
@ -421,7 +382,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
@Test
|
||||
public void testQuickLookup() throws Exception {
|
||||
openProgram("notepad");
|
||||
openProgram("sample");
|
||||
|
||||
int id = prog.startTransaction(testName.getMethodName());
|
||||
try {
|
||||
|
@ -440,7 +401,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
waitForNotBusy(symbolTable);
|
||||
|
||||
runSwing(() -> symbolTable.setRowSelectionInterval(0, 0));
|
||||
selectRow(0);
|
||||
|
||||
triggerAutoLookup("a");
|
||||
waitForNotBusy(symbolTable);
|
||||
|
@ -462,7 +423,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
assertEquals(findRow("abc1", "Global"), symbolTable.getSelectedRow());
|
||||
Thread.sleep(GTable.KEY_TIMEOUT);
|
||||
|
||||
runSwing(() -> symbolTable.setRowSelectionInterval(0, 0));
|
||||
selectRow(0);
|
||||
waitForSwing();
|
||||
triggerAutoLookup("abc12");
|
||||
waitForNotBusy(symbolTable);
|
||||
|
@ -471,7 +432,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
@Test
|
||||
public void testDeleting() throws Exception {
|
||||
openProgram("notepad");
|
||||
openProgram("sample");
|
||||
|
||||
int rowCount = symbolTable.getRowCount();
|
||||
assertTrue(!deleteAction.isEnabled());
|
||||
|
@ -501,8 +462,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
assertEquals(rowCount, symbolTable.getRowCount());
|
||||
|
||||
final int anotherLocal_RowIndex = findRow("AnotherLocal", "Global");
|
||||
runSwing(() -> symbolTable.setRowSelectionInterval(anotherLocal_RowIndex,
|
||||
anotherLocal_RowIndex));
|
||||
selectRow(anotherLocal_RowIndex);
|
||||
|
||||
int selectedRow = symbolTable.getSelectedRow();
|
||||
assertEquals("Row was not selected!", anotherLocal_RowIndex, selectedRow);
|
||||
|
@ -538,29 +498,37 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
setupSymbolTableFilterToShowParameters();
|
||||
|
||||
final int row = getRowForSymbol(param1Symbol);
|
||||
|
||||
// select that row
|
||||
runSwing(() -> symbolTable.setRowSelectionInterval(row, row));
|
||||
int row = getRowForSymbol(param1Symbol);
|
||||
selectRow(row);
|
||||
|
||||
// execute the delete action
|
||||
performAction(deleteAction, true);
|
||||
Assert.assertNotEquals(param1Symbol, getUniqueSymbol(prog, "param_1", function));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuiltInTableActionsAvailable() throws Exception {
|
||||
openProgram("sample");
|
||||
|
||||
int row = 0;
|
||||
selectRow(row);
|
||||
|
||||
JPopupMenu popup = triggerPopup(row);
|
||||
List<JMenuItem> popupItems = getPopupMenuItems(popup);
|
||||
assertMenuContains(popupItems, "Copy");
|
||||
assertMenuContains(popupItems, "Export");
|
||||
assertMenuContains(popupItems, "Select All");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMakeSelection() throws Exception {
|
||||
openProgram("notepad");
|
||||
openProgram("sample");
|
||||
|
||||
assertTrue(!makeSelectionAction.isEnabled());
|
||||
|
||||
final int row = findRow("ghidra", "Global");
|
||||
int rowCount = 3;
|
||||
runSwing(() -> {
|
||||
symbolTable.setRowSelectionInterval(row, row + 2);
|
||||
Rectangle rect = symbolTable.getCellRect(row + 2, 0, true);
|
||||
symbolTable.scrollRectToVisible(rect);
|
||||
});
|
||||
selectRow(row, row + 2);
|
||||
|
||||
assertTrue(makeSelectionAction.isEnabled());
|
||||
|
||||
|
@ -590,14 +558,11 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
@Test
|
||||
public void testSetAndClearPinnedAction() throws Exception {
|
||||
openProgram("notepad");
|
||||
openProgram("sample");
|
||||
|
||||
int row = findRow("ADVAPI32.dll_IsTextUnicode", "Global");
|
||||
selectRow(row, row + 2);
|
||||
|
||||
final int row = findRow("ADVAPI32.dll_IsTextUnicode", "Global");
|
||||
runSwing(() -> {
|
||||
symbolTable.setRowSelectionInterval(row, row + 2);
|
||||
Rectangle rect = symbolTable.getCellRect(row + 2, 0, true);
|
||||
symbolTable.scrollRectToVisible(rect);
|
||||
});
|
||||
ActionContext actionContext = provider.getActionContext(null);
|
||||
int[] selectedRows = symbolTable.getSelectedRows();
|
||||
assertEquals(3, selectedRows.length);
|
||||
|
@ -626,14 +591,11 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
@Test
|
||||
public void testSetPinnedActionNotEnabledForExternalSymbols() throws Exception {
|
||||
openProgram("notepad");
|
||||
openProgram("sample");
|
||||
|
||||
int row = findRow("CharLowerW", "USER32.dll");
|
||||
selectRow(row, row + 1);
|
||||
|
||||
final int row = findRow("CharLowerW", "USER32.dll");
|
||||
runSwing(() -> {
|
||||
symbolTable.setRowSelectionInterval(row, row + 1);
|
||||
Rectangle rect = symbolTable.getCellRect(row + 1, 0, true);
|
||||
symbolTable.scrollRectToVisible(rect);
|
||||
});
|
||||
ActionContext actionContext = provider.getActionContext(null);
|
||||
int[] selectedRows = symbolTable.getSelectedRows();
|
||||
|
||||
|
@ -648,7 +610,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
@Test
|
||||
public void testUpdateOnSymbolsAdded() throws Exception {
|
||||
openProgram("notepad");
|
||||
openProgram("sample");
|
||||
Address sample = prog.getMinAddress();
|
||||
SymbolTable st = prog.getSymbolTable();
|
||||
Symbol sym = null;
|
||||
|
@ -673,7 +635,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
@Test
|
||||
public void testSymbolsAddedWithFilterOn() throws Exception {
|
||||
openProgram("notepad");
|
||||
openProgram("sample");
|
||||
|
||||
final JTextField textField = getFilterTextField();
|
||||
final JCheckBox checkBox = findComponent(filterPanel, JCheckBox.class);
|
||||
|
@ -709,7 +671,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
@Test
|
||||
public void testDefaultFunctionToNamedFunctionWithFilterOn() throws Exception {
|
||||
openProgram("notepad");
|
||||
openProgram("sample");
|
||||
|
||||
performAction(setFilterAction, new ActionContext(), false);
|
||||
waitForSwing();
|
||||
|
@ -742,7 +704,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
@Test
|
||||
public void testUpdateOnSymbolsRemoved() throws Exception {
|
||||
openProgram("notepad");
|
||||
openProgram("sample");
|
||||
|
||||
SymbolTable st = prog.getSymbolTable();
|
||||
Symbol sym = getUniqueSymbol(prog, "entry");
|
||||
|
@ -765,7 +727,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
@Test
|
||||
public void testUpdateOnReferencesAdded() throws Exception {
|
||||
openProgram("notepad");
|
||||
openProgram("sample");
|
||||
Address sample = prog.getMinAddress();
|
||||
|
||||
Symbol s = getUniqueSymbol(prog, "entry");
|
||||
|
@ -796,7 +758,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
@Test
|
||||
public void testUpdateOnReferencesRemoved() throws Exception {
|
||||
openProgram("notepad");
|
||||
openProgram("sample");
|
||||
Address sample = prog.getMinAddress();
|
||||
|
||||
Symbol s = getUniqueSymbol(prog, "doStuff");
|
||||
|
@ -836,7 +798,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
@Test
|
||||
public void testUpdateOnProgramRestore() throws Exception {
|
||||
openProgram("notepad");
|
||||
openProgram("sample");
|
||||
|
||||
int id = prog.startTransaction(testName.getMethodName());
|
||||
try {
|
||||
|
@ -959,7 +921,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
@Test
|
||||
public void testReferences() throws Exception {
|
||||
openProgram("notepad");
|
||||
openProgram("sample");
|
||||
|
||||
showReferencesTable();
|
||||
|
||||
|
@ -1020,7 +982,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
@Test
|
||||
public void testFilterTextField() throws Exception {
|
||||
openProgram("notepad");
|
||||
openProgram("sample");
|
||||
|
||||
JTextField textField = getFilterTextField();
|
||||
|
||||
|
@ -1167,7 +1129,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
@Test
|
||||
public void testFilterTextFieldFindsAllMatches() throws Exception {
|
||||
openProgram("notepad");
|
||||
openProgram("sample");
|
||||
|
||||
JTextField textField = getFilterTextField();
|
||||
|
||||
|
@ -1214,6 +1176,86 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
// Helper methods
|
||||
//==================================================================================================
|
||||
|
||||
private void assertMenuContains(List<JMenuItem> popupItems, String string) {
|
||||
for (JMenuItem item : popupItems) {
|
||||
String text = item.getText();
|
||||
if (text.equals(string)) {
|
||||
return; // found it
|
||||
}
|
||||
}
|
||||
fail("'" + string + "' not in the popup menu!");
|
||||
}
|
||||
|
||||
private List<JMenuItem> getPopupMenuItems(JPopupMenu popup) {
|
||||
List<JMenuItem> list = new ArrayList<>();
|
||||
Component[] children = popup.getComponents();
|
||||
for (Component child : children) {
|
||||
if (child instanceof JMenuItem) {
|
||||
list.add((JMenuItem) child);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private JPopupMenu triggerPopup(int row) {
|
||||
DockingWindowManager dwm = DockingWindowManager.getInstance(symbolTable);
|
||||
ActionContext context = provider.getActionContext(null);
|
||||
JPopupMenu popup =
|
||||
runSwing(() -> DockingWindowManagerTestHelper.getPopupMenu(dwm, context));
|
||||
return popup;
|
||||
}
|
||||
|
||||
private void selectRow(int row) {
|
||||
selectRow(row, row);
|
||||
}
|
||||
|
||||
private void selectRow(int start, int end) {
|
||||
runSwing(() -> {
|
||||
symbolTable.setRowSelectionInterval(start, end);
|
||||
Rectangle rect = symbolTable.getCellRect(end, 0, true);
|
||||
symbolTable.scrollRectToVisible(rect);
|
||||
});
|
||||
}
|
||||
|
||||
private FilterDialog showFilterDialog() {
|
||||
|
||||
performAction(setFilterAction, false);
|
||||
|
||||
FilterDialog dialog = waitForDialogComponent(FilterDialog.class);
|
||||
assertNotNull(dialog);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
private void changeSomeFilterSettings(NewSymbolFilter filter) {
|
||||
//
|
||||
// Change different filter types and values. (This requires some guilty knowledge).
|
||||
//
|
||||
// Symbol type name and default state:
|
||||
//
|
||||
// Symbol Types:
|
||||
// Label filters: instruction (active), data (active), function (active)
|
||||
// Non-label filters: namespaces, classes, params, etc (all inactive)
|
||||
//
|
||||
// Advanced filters: externals, globals, entry points, locals, etc (all inactive)
|
||||
//
|
||||
// Symbol Source Types: user defined (active), imported (active),
|
||||
// default label (inactive), default function, analysis (active)
|
||||
//
|
||||
|
||||
boolean active = true;
|
||||
boolean inactive = false;
|
||||
filter.setFilter("User Defined", inactive);
|
||||
filter.setFilter("Default (Labels)", active);
|
||||
|
||||
filter.setFilter("Function Labels", inactive);
|
||||
|
||||
filter.setFilter("Local Variables", active);
|
||||
|
||||
filter.setFilter("Register Variables", active);
|
||||
filter.setFilter("Subroutines", active);
|
||||
filter.setFilter("Non-Primary Labels", active);
|
||||
}
|
||||
|
||||
private void triggerAutoLookup(String text) {
|
||||
|
||||
KeyListener listener = (KeyListener) getInstanceField("autoLookupListener", symbolTable);
|
||||
|
|
|
@ -29,6 +29,7 @@ import javax.swing.tree.TreePath;
|
|||
|
||||
import org.junit.*;
|
||||
|
||||
import docking.ComponentProvider;
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
import docking.options.editor.OptionsDialog;
|
||||
|
@ -86,6 +87,13 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
// Msg.debug(this, "Writing debug data to: " + file);
|
||||
// debug = new FileWriter(file);
|
||||
|
||||
// debug to the local console
|
||||
// debug = new PrintWriter(System.out);
|
||||
|
||||
setUpTool();
|
||||
}
|
||||
|
||||
private void setUpTool() throws Exception {
|
||||
debug("setUp()");
|
||||
|
||||
env = new TestEnv();
|
||||
|
@ -104,9 +112,21 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
tool.addPlugin(FunctionPlugin.class.getName());
|
||||
tool.addPlugin(EquateTablePlugin.class.getName());
|
||||
|
||||
// Unusual Code: Some actions don't get created until the table is shown (like GTable
|
||||
// actions). Show a provider that has a table so that the actions will get correctly
|
||||
// loaded into the key bindings panel
|
||||
showTableProvider();
|
||||
|
||||
debug("two");
|
||||
}
|
||||
|
||||
private void showTableProvider() {
|
||||
EquateTablePlugin eqp = env.getPlugin(EquateTablePlugin.class);
|
||||
ComponentProvider provider = (ComponentProvider) getInstanceField("provider", eqp);
|
||||
env.showTool();
|
||||
tool.showComponentProvider(provider, true);
|
||||
}
|
||||
|
||||
private void debug(String message) {
|
||||
if (debug == null) {
|
||||
return;
|
||||
|
@ -208,7 +228,7 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
ToolOptions originalOptions = importOptions(saveFile);
|
||||
|
||||
assertOptionsMatch(
|
||||
"The Options objects do not contain different data after " + "changes have been made.",
|
||||
"The Options objects do not contain different data after changes have been made.",
|
||||
toolKeyBindingOptions, originalOptions);
|
||||
|
||||
debug("c");
|
||||
|
@ -220,7 +240,7 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
// verify the changes are different than the original values
|
||||
assertOptionsDontMatch(
|
||||
"The Options objects do not contain different data after " + "changes have been made.",
|
||||
"The Options objects do not contain different data after changes have been made.",
|
||||
toolKeyBindingOptions, originalOptions);
|
||||
|
||||
debug("e");
|
||||
|
@ -319,6 +339,7 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
private void setKeyBindingsUpDialog() throws Exception {
|
||||
env.showTool();
|
||||
showTableProvider();
|
||||
setKeyBindingsUpDialog(tool);
|
||||
}
|
||||
|
||||
|
@ -531,13 +552,13 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
// compares the provided options with the mapping of property names to
|
||||
// keystrokes (the map is obtained from the key bindings panel after an
|
||||
// import is done).
|
||||
private boolean compareOptionsWithKeyStrokeMap(Options options,
|
||||
private boolean compareOptionsWithKeyStrokeMap(Options oldOptions,
|
||||
Map<String, KeyStroke> panelKeyStrokeMap) {
|
||||
List<String> propertyNames = options.getOptionNames();
|
||||
List<String> propertyNames = oldOptions.getOptionNames();
|
||||
for (String element : propertyNames) {
|
||||
boolean match = panelKeyStrokeMap.containsKey(element);
|
||||
|
||||
KeyStroke optionsKs = options.getKeyStroke(element, null);
|
||||
boolean match = panelKeyStrokeMap.containsKey(element);
|
||||
KeyStroke optionsKs = oldOptions.getKeyStroke(element, null);
|
||||
KeyStroke panelKs = panelKeyStrokeMap.get(element);
|
||||
|
||||
// if the value is null, then it would not have been placed into the options map
|
||||
|
|
|
@ -109,12 +109,22 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
public void testManagedKeyBindings() {
|
||||
Set<DockingActionIf> list = tool.getAllActions();
|
||||
for (DockingActionIf action : list) {
|
||||
if (action.getKeyBindingType().isManaged()) {
|
||||
assertTrue(actionInTable(action));
|
||||
if (!ignoreAction(action)) {
|
||||
boolean inTable = actionInKeyBindingsTable(action);
|
||||
assertTrue("Action should be in the key bindingds table: " + action.getFullName(),
|
||||
inTable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean ignoreAction(DockingActionIf action) {
|
||||
if (!action.getKeyBindingType().isManaged()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return action.getFullName().contains("Table Data");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEditKeyBinding() throws Exception {
|
||||
// find action that has a keystroke assigned
|
||||
|
@ -316,14 +326,14 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
}
|
||||
|
||||
private boolean supportsKeyBindings(DockingActionIf action) {
|
||||
return action.getKeyBindingType().isManaged();
|
||||
return ignoreAction(action);
|
||||
}
|
||||
|
||||
private DockingActionIf getKeyBindingPluginAction() {
|
||||
Set<DockingActionIf> list = tool.getAllActions();
|
||||
for (DockingActionIf action : list) {
|
||||
KeyStroke ks = action.getKeyBinding();
|
||||
if (action.getKeyBindingType().isManaged() && ks != null &&
|
||||
if (ignoreAction(action) && ks != null &&
|
||||
ks != KeyStroke.getKeyStroke(KeyEvent.VK_Z, 0)) {
|
||||
return action;
|
||||
}
|
||||
|
@ -331,7 +341,7 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
return null;
|
||||
}
|
||||
|
||||
private boolean actionInTable(DockingActionIf action) {
|
||||
private boolean actionInKeyBindingsTable(DockingActionIf action) {
|
||||
String actionName = action.getName();
|
||||
KeyStroke ks = action.getKeyBinding();
|
||||
|
||||
|
@ -373,15 +383,14 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
private void setUpDialog() throws Exception {
|
||||
runSwing(() -> {
|
||||
panel = new KeyBindingsPanel(tool, tool.getOptions(DockingToolConstants.KEY_BINDINGS));
|
||||
panel.setOptionsPropertyChangeListener(evt -> {
|
||||
// stub
|
||||
});
|
||||
|
||||
dialog = new JDialog(tool.getToolFrame(), "Test KeyBindings", false);
|
||||
dialog.getContentPane().add(panel);
|
||||
dialog.pack();
|
||||
dialog.setVisible(true);
|
||||
// set the dialog so that the panel can enable the apply button
|
||||
panel.setOptionsPropertyChangeListener(evt -> {
|
||||
// stub
|
||||
});
|
||||
});
|
||||
table = findComponent(panel, JTable.class);
|
||||
keyField = findComponent(panel, JTextField.class);
|
||||
|
@ -390,21 +399,26 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
model = table.getModel();
|
||||
}
|
||||
|
||||
// find 2 actions that do not have key bindings so that we can add and change the values
|
||||
private void grabActionsWithoutKeybinding() {
|
||||
Set<DockingActionIf> list = tool.getAllActions();
|
||||
for (DockingActionIf action : list) {
|
||||
if (!action.getKeyBindingType().isManaged()) {
|
||||
if (ignoreAction(action)) {
|
||||
continue;
|
||||
}
|
||||
if (action.getKeyBinding() != null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// good action
|
||||
if (action1 == null) {
|
||||
action1 = action;
|
||||
}
|
||||
else {
|
||||
|
||||
if (action.getName().equals(action1.getName())) {
|
||||
continue; // same name, different owners; these are 'shared' actions--ignore
|
||||
}
|
||||
|
||||
action2 = action;
|
||||
return; // grabbed all actions--we are done
|
||||
}
|
||||
|
|
|
@ -16,13 +16,15 @@
|
|||
package ghidra.bitpatterns.gui;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import org.apache.commons.collections4.list.LazyList;
|
||||
|
||||
import docking.widgets.label.GLabel;
|
||||
import docking.widgets.table.AbstractSortedTableModel;
|
||||
import docking.widgets.table.GTable;
|
||||
import docking.widgets.textfield.IntegerTextField;
|
||||
import ghidra.bitpatterns.info.FileBitPatternInfoReader;
|
||||
|
@ -34,8 +36,6 @@ import ghidra.util.layout.PairLayout;
|
|||
*/
|
||||
public class AlignmentPanelBuilder extends ContextRegisterFilterablePanelBuilder {
|
||||
private static final int DEFAULT_MODULUS = 16;
|
||||
private static final String[] alignmentTableColumnNames =
|
||||
{ "Modulus", "Number of Functions", "Percentage" };
|
||||
private static final String MODULUS_FIELD_TEXT = " Alignment Modulus ";
|
||||
private static final String RECOMPUTE_BUTTON_TEXT = "Compute Alignment Info";
|
||||
|
||||
|
@ -75,12 +75,7 @@ public class AlignmentPanelBuilder extends ContextRegisterFilterablePanelBuilder
|
|||
modulusPanel.add(modulusField.getComponent());
|
||||
|
||||
JButton recomputeButton = new JButton(RECOMPUTE_BUTTON_TEXT);
|
||||
recomputeButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
updateAlignmentPanel();
|
||||
}
|
||||
});
|
||||
recomputeButton.addActionListener(e -> updateAlignmentPanel());
|
||||
|
||||
getButtonPanel().add(recomputeButton);
|
||||
|
||||
|
@ -109,11 +104,13 @@ public class AlignmentPanelBuilder extends ContextRegisterFilterablePanelBuilder
|
|||
}
|
||||
|
||||
private GTable createAlignmentTable(List<Long> startingAddresses, int numFuncs) {
|
||||
String[][] modulusInfo = new String[modulus][3];
|
||||
|
||||
List<ModulusInfo> data = LazyList.lazyList(new ArrayList<>(), () -> new ModulusInfo());
|
||||
if (startingAddresses != null) {
|
||||
for (int i = 0; i < modulus; i++) {
|
||||
modulusInfo[i][0] = Long.toString(i);
|
||||
data.get(0).modulus = Long.toString(i);
|
||||
}
|
||||
|
||||
long[] countsAsLongs = new long[modulus];
|
||||
for (Long currentAddress : startingAddresses) {
|
||||
countsAsLongs[(int) (Long.remainderUnsigned(currentAddress, modulus))] =
|
||||
|
@ -121,11 +118,13 @@ public class AlignmentPanelBuilder extends ContextRegisterFilterablePanelBuilder
|
|||
}
|
||||
for (int i = 0; i < modulus; i++) {
|
||||
double percent = (100.0 * countsAsLongs[i]) / numFuncs;
|
||||
modulusInfo[i][2] = Double.toString(Math.round(percent));
|
||||
modulusInfo[i][1] = Long.toString(countsAsLongs[i]);
|
||||
data.get(i).counts = Long.toString(countsAsLongs[i]);
|
||||
data.get(i).percent = Double.toString(Math.round(percent));
|
||||
}
|
||||
}
|
||||
GTable table = new GTable(modulusInfo, alignmentTableColumnNames);
|
||||
|
||||
AlignmentTableModel model = new AlignmentTableModel(data);
|
||||
GTable table = new GTable(model);
|
||||
return table;
|
||||
}
|
||||
|
||||
|
@ -161,4 +160,69 @@ public class AlignmentPanelBuilder extends ContextRegisterFilterablePanelBuilder
|
|||
updateAlignmentPanel();
|
||||
}
|
||||
|
||||
private class ModulusInfo {
|
||||
|
||||
private String modulus;
|
||||
private String percent;
|
||||
private String counts;
|
||||
|
||||
ModulusInfo() {
|
||||
this.modulus = modulus;
|
||||
this.percent = percent;
|
||||
this.counts = counts;
|
||||
}
|
||||
}
|
||||
|
||||
private class AlignmentTableModel extends AbstractSortedTableModel<ModulusInfo> {
|
||||
|
||||
private final String[] columnNames = { "Modulus", "Number of Functions", "Percentage" };
|
||||
private List<ModulusInfo> data;
|
||||
|
||||
AlignmentTableModel(List<ModulusInfo> data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int column) {
|
||||
return columnNames[column];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getColumnClass(int columnIndex) {
|
||||
return String.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSortable(int columnIndex) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Function Start Alignment";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ModulusInfo> getModelData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getColumnValueForRow(ModulusInfo t, int columnIndex) {
|
||||
switch (columnIndex) {
|
||||
case 0:
|
||||
return t.modulus;
|
||||
case 1:
|
||||
return t.counts;
|
||||
case 2:
|
||||
return t.percent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -528,13 +528,11 @@ public abstract class DecompilerCodeComparisonPanel<T extends DualDecompilerFiel
|
|||
|
||||
@Override
|
||||
public ActionContext getActionContext(ComponentProvider provider, MouseEvent event) {
|
||||
Object sourceObject = null;
|
||||
if (event != null) {
|
||||
sourceObject = event.getSource();
|
||||
}
|
||||
|
||||
Component component = event == null ? null : event.getComponent();
|
||||
CDisplayPanel focusedDecompilerPanel = getFocusedDecompilerPanel();
|
||||
DualDecompilerActionContext dualDecompContext =
|
||||
new DualDecompilerActionContext(provider, focusedDecompilerPanel, sourceObject);
|
||||
new DualDecompilerActionContext(provider, focusedDecompilerPanel, component);
|
||||
dualDecompContext.setCodeComparisonPanel(this);
|
||||
return dualDecompContext;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ import ghidra.program.model.address.*;
|
|||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.pcode.*;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
public class DecompilerUtils {
|
||||
|
||||
|
@ -315,11 +314,6 @@ public class DecompilerUtils {
|
|||
int nchild = parentNode.numChildren();
|
||||
for (int i = 0; i < nchild; i++) {
|
||||
ClangNode node = parentNode.Child(i);
|
||||
|
||||
if (node instanceof ClangFuncProto) {
|
||||
Msg.debug(null, "");
|
||||
}
|
||||
|
||||
if (node.numChildren() > 0) {
|
||||
collectTokens(tokenList, node, addressSet);
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package ghidra.app.decompiler.component;
|
||||
|
||||
import java.awt.Component;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.ComponentProvider;
|
||||
import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
|
||||
|
@ -34,11 +36,11 @@ public class DualDecompilerActionContext extends ActionContext
|
|||
* Creates an action context for a dual decompiler panel.
|
||||
* @param provider the provider for this context
|
||||
* @param cPanel the decompiler panel associated with this context
|
||||
* @param sourceObject the source of the action
|
||||
* @param source the source of the action
|
||||
*/
|
||||
public DualDecompilerActionContext(ComponentProvider provider, CDisplayPanel cPanel,
|
||||
Object sourceObject) {
|
||||
super(provider, cPanel, sourceObject);
|
||||
Component source) {
|
||||
super(provider, cPanel, source);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -122,7 +122,7 @@ public class SetFormatDialogComponentProvider extends DialogComponentProvider {
|
|||
FieldHeader headerPanel = listingPanel.getFieldHeader();
|
||||
if (headerPanel != null && headerPanel.isAncestorOf(event.getComponent())) {
|
||||
FieldHeaderLocation fhLoc = headerPanel.getFieldHeaderLocation(event.getPoint());
|
||||
return new ActionContext(null, fhLoc);
|
||||
return new ActionContext().setContextObject(fhLoc);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -619,7 +619,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
|
|||
assertNotNull(action);
|
||||
|
||||
FieldHeaderLocation fhLoc = createFieldHeaderLocation(provider);
|
||||
ActionContext context = new ActionContext(null, fhLoc);
|
||||
ActionContext context = createContext(fhLoc);
|
||||
performAction(action, context, true);
|
||||
|
||||
waitForConditionWithoutFailing(() -> fieldIsVisible(provider, actionName));
|
||||
|
@ -723,7 +723,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
|
|||
assertNotNull(action);
|
||||
|
||||
FieldHeaderLocation fhLoc = createFieldHeaderLocation(provider);
|
||||
ActionContext context = new ActionContext(null, fhLoc);
|
||||
ActionContext context = createContext(fhLoc);
|
||||
performAction(action, context, false);
|
||||
|
||||
Window dialog = waitForWindow("Reset All Formats?");
|
||||
|
|
|
@ -29,6 +29,6 @@ public class FcgActionContext extends ActionContext {
|
|||
}
|
||||
|
||||
public FcgActionContext(FcgProvider provider, Object contextObject) {
|
||||
super(provider, contextObject);
|
||||
super(provider, contextObject, provider.getComponent());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -277,7 +277,7 @@ public class VTFunctionAssociationProvider extends ComponentProviderAdapter
|
|||
getExistingMatch(sourceFunction, destinationFunction));
|
||||
vtListingContext.setCodeComparisonPanel(dualListingPanel);
|
||||
vtListingContext.setContextObject(dualListingPanel);
|
||||
vtListingContext.setSource(source);
|
||||
vtListingContext.setSourceObject(source);
|
||||
return vtListingContext;
|
||||
}
|
||||
|
||||
|
|
|
@ -493,7 +493,7 @@ public class VTMarkupItemsTableProvider extends ComponentProviderAdapter
|
|||
new VTListingContext(this, vtListingNavigator);
|
||||
vtListingContext.setCodeComparisonPanel(dualListingPanel);
|
||||
vtListingContext.setContextObject(dualListingPanel);
|
||||
vtListingContext.setSource(source);
|
||||
vtListingContext.setSourceObject(source);
|
||||
return vtListingContext;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,48 +15,132 @@
|
|||
*/
|
||||
package docking;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
import docking.action.DockingActionIf;
|
||||
|
||||
/**
|
||||
* ComponentProviders are required to return Objects of this type in their getActionContext()
|
||||
* Action context is a class that contains state information that is given to
|
||||
* {@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,
|
||||
* <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
|
||||
* in class fields of the plugin.
|
||||
*
|
||||
* <p>ComponentProviders are required to return Objects of this type in their getActionContext()
|
||||
* methods. Generally, ComponentProviders have two ways to use this class. They can either create
|
||||
* an ActionContext instance and pass in a contextObject that will be useful to its actions or,
|
||||
* subclass the ActionContext object to include specific methods to provide the information that
|
||||
* actions will require.
|
||||
*
|
||||
* <p>The data contained by this class has meaning that can change relative to the code that
|
||||
* created it. The intended purpose for the fields of this class is as follows:
|
||||
* <ul>
|
||||
* <li><b>provider</b> - the component provider to which this context belongs; the provider that
|
||||
* contains the component that is the source of the user action
|
||||
* </li>
|
||||
* <li><b>contextObject</b> - client-defined data object. This allows clients to save any
|
||||
* information desired to be used when the action is performed.
|
||||
* </li>
|
||||
* <li><b>sourceObject</b> - when checking enablement, this is the item that was clicked or
|
||||
* activated; when performing an action this is either the active
|
||||
* object or the component that was clicked. This value may change
|
||||
* between the check for
|
||||
* {@link DockingActionIf#isEnabledForContext(ActionContext) enablement}
|
||||
* and {@link DockingActionIf#actionPerformed(ActionContext) execution}.
|
||||
* </li>
|
||||
* <li><b>sourceComponent</b> - this value is the component that is the source of the current
|
||||
* context. Whereas the <code>sourceObject</code> is the actual
|
||||
* clicked item, this value is the focused/active component and
|
||||
* will not change between
|
||||
* {@link DockingActionIf#isEnabledForContext(ActionContext) enablement}
|
||||
* and {@link DockingActionIf#actionPerformed(ActionContext) execution}.
|
||||
* </li>
|
||||
* <li><b>mouseEvent</b> - the mouse event that triggered the action; null if the action was
|
||||
* triggered by a key binding.
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Ultimately, clients can pass any values they wish for the fields of this class, even if
|
||||
* that changes the meaning of the fields outlined above.
|
||||
*/
|
||||
|
||||
public class ActionContext {
|
||||
private ComponentProvider provider;
|
||||
private MouseEvent mouseEvent;
|
||||
private Object contextObject;
|
||||
private Object sourceObject;
|
||||
private MouseEvent mouseEvent;
|
||||
|
||||
// Note: the setting of this object is delayed. This allows clients to build-up the state
|
||||
// of this context. This object will be set when getSourceComponent() is called if it
|
||||
// has not already been set.
|
||||
private Component sourceComponent;
|
||||
|
||||
public ActionContext() {
|
||||
this(null, null);
|
||||
}
|
||||
|
||||
public ActionContext(ComponentProvider cp) {
|
||||
this(cp, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic constructor for ActionContext
|
||||
* @param provider the ComponentProvider that generated this context.
|
||||
* @param contextObject an optional contextObject that the ComponentProvider can provide
|
||||
* to the action.
|
||||
* @param sourceComponent an optional source object; this is intended to be the component that
|
||||
* is the source of the context, usually the focused component
|
||||
*/
|
||||
public ActionContext(ComponentProvider provider, Object contextObject) {
|
||||
this.provider = provider;
|
||||
this.contextObject = contextObject;
|
||||
public ActionContext(ComponentProvider provider, Component sourceComponent) {
|
||||
this(provider, sourceComponent, sourceComponent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param provider the ComponentProvider that generated this context.
|
||||
* @param contextObject an optional contextObject that the ComponentProvider can provide
|
||||
* @param sourceObject an optional source object; this can be anything that actions wish to
|
||||
* later retrieve
|
||||
* @param contextObject an optional contextObject that the ComponentProvider can provide; this
|
||||
* can be anything that actions wish to later retrieve
|
||||
* @param sourceComponent an optional source object; this is intended to be the component that
|
||||
* is the source of the context, usually the focused component
|
||||
*/
|
||||
public ActionContext(ComponentProvider provider, Object contextObject, Object sourceObject) {
|
||||
this(provider, contextObject);
|
||||
this.sourceObject = sourceObject;
|
||||
public ActionContext(ComponentProvider provider, Object contextObject,
|
||||
Component sourceComponent) {
|
||||
this.provider = provider;
|
||||
this.contextObject = contextObject;
|
||||
this.sourceObject = sourceComponent;
|
||||
this.sourceComponent = sourceComponent;
|
||||
}
|
||||
|
||||
private void lazyDeriveSourceComponent() {
|
||||
|
||||
if (sourceComponent != null) {
|
||||
// Do not allow this to change once set. This prevents the value from getting changed
|
||||
// when the user clicks a menu item.
|
||||
return;
|
||||
}
|
||||
|
||||
// check this in order of preference
|
||||
if (sourceObject instanceof Component) {
|
||||
sourceComponent = (Component) sourceObject;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mouseEvent != null) {
|
||||
sourceComponent = mouseEvent.getComponent();
|
||||
return;
|
||||
}
|
||||
|
||||
if (contextObject instanceof Component) {
|
||||
sourceComponent = (Component) contextObject;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -83,9 +167,11 @@ public class ActionContext {
|
|||
* choosing that can be provided for later retrieval.
|
||||
*
|
||||
* @param contextObject Sets the context object for this context.
|
||||
* @return this context
|
||||
*/
|
||||
public void setContextObject(Object contextObject) {
|
||||
public ActionContext setContextObject(Object contextObject) {
|
||||
this.contextObject = contextObject;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -98,22 +184,28 @@ public class ActionContext {
|
|||
|
||||
/**
|
||||
* Sets the sourceObject for this ActionContext. This method is used internally by the
|
||||
* DockingWindowManager. ComponentProvider and action developers should
|
||||
* only use this method for testing.
|
||||
* DockingWindowManager. ComponentProvider and action developers should only use this
|
||||
* method for testing.
|
||||
*
|
||||
* @param sourceObject the source object
|
||||
* @return this context
|
||||
*/
|
||||
public void setSource(Object sourceObject) {
|
||||
public ActionContext setSourceObject(Object sourceObject) {
|
||||
this.sourceObject = sourceObject;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the context's mouse event. Contexts that are based upon key events will have no
|
||||
* mouse event.
|
||||
* mouse event. This method is really for the framework to use. Client calls to this
|
||||
* method will be overridden by the framework when menu items are clicked.
|
||||
*
|
||||
* @param e the event that triggered this context.
|
||||
* @return this context
|
||||
*/
|
||||
public void setMouseEvent(MouseEvent e) {
|
||||
public ActionContext setMouseEvent(MouseEvent e) {
|
||||
this.mouseEvent = e;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -126,6 +218,17 @@ public class ActionContext {
|
|||
return mouseEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the component that is the target of this context. This value should not change
|
||||
* whether the context is triggered by a key binding or mouse event.
|
||||
*
|
||||
* @return the component; may be null
|
||||
*/
|
||||
public Component getSourceComponent() {
|
||||
lazyDeriveSourceComponent();
|
||||
return sourceComponent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
|
@ -134,6 +237,7 @@ public class ActionContext {
|
|||
"\tprovider: " + provider + ",\n" +
|
||||
"\tcontextObject: " + contextObject + ",\n" +
|
||||
"\tsourceObject: " + sourceObject + ",\n" +
|
||||
"\tsourceComponent: " + sourceComponent + ",\n" +
|
||||
"\tmouseEvent: " + mouseEvent + "\n" +
|
||||
"}";
|
||||
//@formatter:on
|
||||
|
|
|
@ -126,6 +126,10 @@ public class ActionToGuiMapper {
|
|||
menuAndToolBarManager.contextChanged(placeHolder);
|
||||
}
|
||||
|
||||
PopupActionManager getPopupActionManager() {
|
||||
return popupActionManager;
|
||||
}
|
||||
|
||||
public MenuGroupMap getMenuGroupMap() {
|
||||
return menuGroupMap;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,8 @@ public interface ComponentLoadedListener {
|
|||
* Called when the component is made displayable
|
||||
*
|
||||
* @param windowManager the window manager associated with the loaded component
|
||||
* @param provider the provider that is the parent of the given component; null if this
|
||||
* component is not the child of a component provider
|
||||
*/
|
||||
public void componentLoaded(DockingWindowManager windowManager);
|
||||
public void componentLoaded(DockingWindowManager windowManager, ComponentProvider provider);
|
||||
}
|
||||
|
|
|
@ -387,7 +387,44 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
|
|||
*/
|
||||
@Override
|
||||
public ActionContext getActionContext(MouseEvent event) {
|
||||
return new ActionContext(this, getComponent());
|
||||
Component c = getComponent();
|
||||
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
|
||||
Component focusedComponent = kfm.getFocusOwner();
|
||||
if (focusedComponent != null && SwingUtilities.isDescendingFrom(focusedComponent, c)) {
|
||||
c = focusedComponent;
|
||||
}
|
||||
return createContext(c, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* A default method for creating an action context for this provider
|
||||
* @return the new context
|
||||
*/
|
||||
protected ActionContext createContext() {
|
||||
return new ActionContext(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
@ -1127,7 +1137,23 @@ public class DialogComponentProvider
|
|||
*/
|
||||
@Override
|
||||
public ActionContext getActionContext(MouseEvent event) {
|
||||
return new ActionContext(null, null);
|
||||
|
||||
Component c = getComponent();
|
||||
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
|
||||
Component focusedComponent = kfm.getFocusOwner();
|
||||
if (focusedComponent != null && SwingUtilities.isDescendingFrom(focusedComponent, c)) {
|
||||
c = focusedComponent;
|
||||
}
|
||||
|
||||
if (event == null) {
|
||||
return new ActionContext(null, c);
|
||||
}
|
||||
|
||||
Component sourceComponent = event.getComponent();
|
||||
if (sourceComponent != null) {
|
||||
c = sourceComponent;
|
||||
}
|
||||
return new ActionContext(null, c).setSourceObject(event.getSource());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -56,7 +56,7 @@ public class DialogComponentProviderPopupActionManager {
|
|||
// If the source is null, must set it or we won't have
|
||||
// any popups shown.
|
||||
if (actionContext.getSourceObject() == null) {
|
||||
actionContext.setSource(e.getSource());
|
||||
actionContext.setSourceObject(e.getSource());
|
||||
}
|
||||
|
||||
MenuHandler popupMenuHandler = new PopupMenuHandler(actionContext);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
|
@ -152,7 +123,7 @@ public class DialogComponentProviderPopupActionManager {
|
|||
public void processMenuAction(final DockingActionIf action, final ActionEvent event) {
|
||||
|
||||
DockingWindowManager.clearMouseOverHelp();
|
||||
actionContext.setSource(event.getSource());
|
||||
actionContext.setSourceObject(event.getSource());
|
||||
|
||||
// this gives the UI some time to repaint before executing the action
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
|
|
|
@ -214,6 +214,11 @@ public class DockingActionProxy
|
|||
dockingAction.setUnvalidatedKeyBindingData(newKeyBindingData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
dockingAction.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpInfo() {
|
||||
return dockingAction.getHelpInfo();
|
||||
|
|
|
@ -62,7 +62,7 @@ public abstract class DockingKeyBindingAction extends AbstractAction {
|
|||
tool.setStatusInfo("");
|
||||
ComponentProvider provider = tool.getActiveComponentProvider();
|
||||
ActionContext context = getLocalContext(provider);
|
||||
context.setSource(e.getSource());
|
||||
context.setSourceObject(e.getSource());
|
||||
docakbleAction.actionPerformed(context);
|
||||
}
|
||||
|
||||
|
|
|
@ -73,7 +73,9 @@ public interface DockingTool {
|
|||
public void addComponentProvider(ComponentProvider componentProvider, boolean show);
|
||||
|
||||
/**
|
||||
* Removes the given ComponentProvider from the tool
|
||||
* Removes the given ComponentProvider from the tool. When a provider has been removed
|
||||
* from the tool it is considered disposed and should not be reused.
|
||||
*
|
||||
* @param componentProvider the provider to remove from the tool
|
||||
*/
|
||||
public void removeComponentProvider(ComponentProvider componentProvider);
|
||||
|
@ -125,7 +127,8 @@ public interface DockingTool {
|
|||
public void addAction(DockingActionIf action);
|
||||
|
||||
/**
|
||||
* Removes the given action from the tool
|
||||
* Removes the given action from the tool. When an action is removed from the tool it will
|
||||
* be disposed and should not be reused.
|
||||
* @param action the action to be removed.
|
||||
*/
|
||||
public void removeAction(DockingActionIf action);
|
||||
|
|
|
@ -63,8 +63,13 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
private static DockingActionIf actionUnderMouse;
|
||||
private static Object objectUnderMouse;
|
||||
|
||||
public static final String TOOL_PREFERENCES_XML_NAME = "PREFERENCES";
|
||||
/**
|
||||
* The owner name for docking windows actions.
|
||||
* <p>Warning: Any action with this owner will get removed every time the 'Window' menu is
|
||||
* rebuilt, with the exception if reserved key bindings.
|
||||
*/
|
||||
public static final String DOCKING_WINDOWS_OWNER = "DockingWindows";
|
||||
public static final String TOOL_PREFERENCES_XML_NAME = "PREFERENCES";
|
||||
|
||||
/**
|
||||
* The helpService field should be set to the appropriate help service provider.
|
||||
|
@ -560,6 +565,25 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the component provider that is the conceptual parent of the given component. More
|
||||
* precisely, this will return the component provider whose
|
||||
* {@link ComponentProvider#getComponent() component} is the parent of the given component.
|
||||
*
|
||||
* @param component the component for which to find a provider
|
||||
* @return the provider; null if the component is not the child of a provider
|
||||
*/
|
||||
private ComponentProvider getComponentProvider(Component component) {
|
||||
Set<ComponentProvider> providers = placeholderManager.getActiveProviders();
|
||||
for (ComponentProvider provider : providers) {
|
||||
JComponent providerComponent = provider.getComponent();
|
||||
if (SwingUtilities.isDescendingFrom(component, providerComponent)) {
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
DockableComponent getDockableComponent(ComponentProvider provider) {
|
||||
ComponentPlaceholder placeholder = placeholderManager.getPlaceholder(provider);
|
||||
return placeholder.getComponent();
|
||||
|
@ -2165,7 +2189,8 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
component.removeHierarchyListener(this);
|
||||
DockingWindowManager dwm = getInstance(component);
|
||||
if (dwm != null) {
|
||||
listener.componentLoaded(dwm);
|
||||
ComponentProvider provider = dwm.getComponentProvider(component);
|
||||
listener.componentLoaded(dwm, provider);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -408,8 +408,9 @@ class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher {
|
|||
|
||||
// ...next see if there is a key binding for when the component is the child of the focus
|
||||
// owner
|
||||
return KeyBindingUtils.getAction(jComponent, keyStroke,
|
||||
action = KeyBindingUtils.getAction(jComponent, keyStroke,
|
||||
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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.
|
||||
|
@ -63,7 +62,7 @@ public class MenuBarMenuHandler extends MenuHandler {
|
|||
return; // context is not valid, nothing to do
|
||||
}
|
||||
|
||||
tempContext.setSource(event.getSource());
|
||||
tempContext.setSourceObject(event.getSource());
|
||||
final ActionContext finalContext = tempContext;
|
||||
|
||||
// this gives the UI some time to repaint before executing the action
|
||||
|
|
|
@ -23,8 +23,9 @@ import java.util.*;
|
|||
|
||||
import javax.swing.JPopupMenu;
|
||||
|
||||
import org.apache.commons.collections4.IteratorUtils;
|
||||
|
||||
import docking.action.*;
|
||||
import docking.actions.PopupActionProvider;
|
||||
import docking.menu.*;
|
||||
|
||||
public class PopupActionManager implements PropertyChangeListener {
|
||||
|
@ -67,6 +68,7 @@ public class PopupActionManager implements PropertyChangeListener {
|
|||
}
|
||||
|
||||
void popupMenu(ComponentPlaceholder info, MouseEvent e) {
|
||||
|
||||
if (e.isConsumed()) {
|
||||
return;
|
||||
}
|
||||
|
@ -76,27 +78,41 @@ public class PopupActionManager implements PropertyChangeListener {
|
|||
actionContext = new ActionContext();
|
||||
}
|
||||
|
||||
actionContext.setSource(e.getSource());
|
||||
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);
|
||||
|
@ -130,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));
|
||||
|
@ -145,7 +160,7 @@ public class PopupActionManager implements PropertyChangeListener {
|
|||
|
||||
Object source = actionContext.getSourceObject();
|
||||
|
||||
// this interface is deprecated in favor of the next block
|
||||
// this interface is deprecated in favor the code that calls this method; this will be deleted
|
||||
if (source instanceof DockingActionProviderIf) {
|
||||
DockingActionProviderIf actionProvider = (DockingActionProviderIf) source;
|
||||
List<DockingActionIf> dockingActions = actionProvider.getDockingActions();
|
||||
|
@ -158,23 +173,6 @@ public class PopupActionManager implements PropertyChangeListener {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// note: this is temporary; there is only one client that needs this. This will be
|
||||
// removed in a future ticket when that client uses the standard tool action system
|
||||
if (source instanceof PopupActionProvider) {
|
||||
PopupActionProvider actionProvider = (PopupActionProvider) source;
|
||||
DockingTool tool = windowManager.getTool();
|
||||
List<DockingActionIf> dockingActions =
|
||||
actionProvider.getPopupActions(tool, actionContext);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isRemovingFromPopup(MenuData oldData, MenuData newData) {
|
||||
|
|
|
@ -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.
|
||||
|
@ -48,7 +47,7 @@ public class PopupMenuHandler extends MenuHandler {
|
|||
public void processMenuAction(final DockingActionIf action, final ActionEvent event) {
|
||||
|
||||
DockingWindowManager.clearMouseOverHelp();
|
||||
actionContext.setSource(event.getSource());
|
||||
actionContext.setSourceObject(event.getSource());
|
||||
|
||||
// this gives the UI some time to repaint before executing the action
|
||||
SwingUtilities.invokeLater( new Runnable() {
|
||||
|
|
|
@ -372,9 +372,8 @@ public abstract class DockingAction implements DockingActionIf {
|
|||
/**
|
||||
* Cleans up any resources used by the action.
|
||||
*/
|
||||
@Override
|
||||
public void dispose() {
|
||||
// TODO this doesn't seem to be called by the framework. Should't we call this when
|
||||
// an action is removed from the tool??
|
||||
propertyListeners.clear();
|
||||
}
|
||||
|
||||
|
|
|
@ -300,4 +300,9 @@ public interface DockingActionIf extends HelpDescriptor {
|
|||
* @param newKeyBindingData the KeyBindingData to be used to assign this action to a keybinding
|
||||
*/
|
||||
public void setUnvalidatedKeyBindingData(KeyBindingData newKeyBindingData);
|
||||
|
||||
/**
|
||||
* Called when the action's owner is removed from the tool
|
||||
*/
|
||||
public void dispose();
|
||||
}
|
||||
|
|
|
@ -119,7 +119,7 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
|
|||
// Build list of actions which are valid in current context
|
||||
ComponentProvider localProvider = tool.getActiveComponentProvider();
|
||||
ActionContext localContext = getLocalContext(localProvider);
|
||||
localContext.setSource(event.getSource());
|
||||
localContext.setSourceObject(event.getSource());
|
||||
|
||||
ActionContext globalContext = tool.getGlobalContext();
|
||||
List<ExecutableKeyActionAdapter> list = getValidContextActions(localContext, globalContext);
|
||||
|
|
|
@ -122,7 +122,7 @@ public class ActionAdapter implements Action, PropertyChangeListener {
|
|||
}
|
||||
if (context == null) {
|
||||
context = new ActionContext();
|
||||
context.setSource(e.getSource());
|
||||
context.setSourceObject(e.getSource());
|
||||
}
|
||||
if (dockingAction.isEnabledForContext(context)) {
|
||||
dockingAction.actionPerformed(context);
|
||||
|
|
|
@ -91,5 +91,4 @@ public interface DockingToolActions {
|
|||
* @return the actions
|
||||
*/
|
||||
public Set<DockingActionIf> getAllActions();
|
||||
|
||||
}
|
||||
|
|
|
@ -284,13 +284,32 @@ public class KeyBindingUtils {
|
|||
am.put(keyText, action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the client to clear Java key bindings when the client is creating a docking
|
||||
* action. Without this call, any actions bound to the given component will prevent an
|
||||
* action with the same key binding from firing. This is useful when your
|
||||
* application is using tool-level key bindings that share the same
|
||||
* keystroke as a built-in Java action, such as Ctrl-C for the copy action.
|
||||
*
|
||||
* @param component the component for which to clear the key binding
|
||||
* @param action the action from which to get the key binding
|
||||
*/
|
||||
public static void clearKeyBinding(JComponent component, DockingActionIf action) {
|
||||
KeyStroke keyBinding = action.getKeyBinding();
|
||||
if (keyBinding == null) {
|
||||
return;
|
||||
}
|
||||
clearKeyBinding(component, keyBinding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows clients to clear Java key bindings. This is useful when your
|
||||
* application is using tool-level key bindings that share the same
|
||||
* keystroke as a built-in Java action, such as Ctrl-C for the copy action.
|
||||
* <p>
|
||||
* Note: this method clears focus for the default
|
||||
* ({@link JComponent#WHEN_FOCUSED}) focus condition.
|
||||
* Note: this method clears the key binding for the
|
||||
* {@link JComponent#WHEN_FOCUSED} and
|
||||
* {@link JComponent#WHEN_ANCESTOR_OF_FOCUSED_COMPONENT} focus conditions.
|
||||
*
|
||||
* @param component the component for which to clear the key binding
|
||||
* @param keyStroke the keystroke of the binding to be cleared
|
||||
|
@ -298,6 +317,7 @@ public class KeyBindingUtils {
|
|||
*/
|
||||
public static void clearKeyBinding(JComponent component, KeyStroke keyStroke) {
|
||||
clearKeyBinding(component, keyStroke, JComponent.WHEN_FOCUSED);
|
||||
clearKeyBinding(component, keyStroke, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -314,12 +334,9 @@ public class KeyBindingUtils {
|
|||
public static void clearKeyBinding(JComponent component, KeyStroke keyStroke,
|
||||
int focusCondition) {
|
||||
InputMap inputMap = component.getInputMap(focusCondition);
|
||||
ActionMap actionMap = component.getActionMap();
|
||||
if (inputMap == null || actionMap == null) {
|
||||
return;
|
||||
if (inputMap != null) {
|
||||
inputMap.put(keyStroke, "none");
|
||||
}
|
||||
|
||||
inputMap.put(keyStroke, "none");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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, ", ");
|
||||
}
|
||||
|
||||
|
|
|
@ -32,8 +32,7 @@ import docking.*;
|
|||
import docking.action.*;
|
||||
import docking.tool.util.DockingToolConstants;
|
||||
import ghidra.framework.options.*;
|
||||
import ghidra.util.ReservedKeyBindings;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import util.CollectionUtils;
|
||||
|
||||
|
@ -72,6 +71,7 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
|
|||
this.keyBindingOptions = tool.getOptions(DockingToolConstants.KEY_BINDINGS);
|
||||
|
||||
createReservedKeyBindings();
|
||||
SharedActionRegistry.installSharedActions(tool, this);
|
||||
}
|
||||
|
||||
private void createReservedKeyBindings() {
|
||||
|
@ -161,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;
|
||||
});
|
||||
|
||||
|
@ -178,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.
|
||||
|
@ -187,6 +189,16 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
|
|||
action.removePropertyChangeListener(this);
|
||||
removeAction(action);
|
||||
actionGuiHelper.removeToolAction(action);
|
||||
dispose(action);
|
||||
}
|
||||
|
||||
private void dispose(DockingActionIf action) {
|
||||
try {
|
||||
action.dispose();
|
||||
}
|
||||
catch (Throwable t) {
|
||||
Msg.error(this, "Exception disposing action '" + action.getFullName() + "'", t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -311,6 +323,7 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
|
|||
removeAction(action);
|
||||
keyBindingsManager.removeAction(action);
|
||||
actionGuiHelper.removeProviderAction(provider, action);
|
||||
dispose(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -197,21 +197,22 @@ class MenuItemManager implements ManagedMenuItem, PropertyChangeListener, Action
|
|||
public void actionPerformed(ActionEvent e) {
|
||||
if (menuHandler != null) {
|
||||
menuHandler.processMenuAction(action, e);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
try {
|
||||
ActionContext context = new ActionContext(null, null, e.getSource());
|
||||
if (action.isEnabledForContext(context)) {
|
||||
if (action instanceof ToggleDockingActionIf) {
|
||||
ToggleDockingActionIf toggleAction = ((ToggleDockingActionIf) action);
|
||||
toggleAction.setSelected(!toggleAction.isSelected());
|
||||
}
|
||||
action.actionPerformed(context);
|
||||
|
||||
try {
|
||||
ActionContext context = new ActionContext();
|
||||
context.setSourceObject(e.getSource());
|
||||
if (action.isEnabledForContext(context)) {
|
||||
if (action instanceof ToggleDockingActionIf) {
|
||||
ToggleDockingActionIf toggleAction = ((ToggleDockingActionIf) action);
|
||||
toggleAction.setSelected(!toggleAction.isSelected());
|
||||
}
|
||||
action.actionPerformed(context);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
Msg.error(this, "Unexpected Exception: " + t.getMessage(), t);
|
||||
}
|
||||
}
|
||||
catch (Throwable t) {
|
||||
Msg.error(this, "Unexpected Exception: " + t.getMessage(), t);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -184,7 +184,7 @@ public class MultipleActionDockingToolbarButton extends EmptyBorderButton {
|
|||
final DockingActionIf delegateAction = dockingAction;
|
||||
item.addActionListener(e -> {
|
||||
ActionContext context = getActionContext();
|
||||
context.setSource(e.getSource());
|
||||
context.setSourceObject(e.getSource());
|
||||
if (delegateAction instanceof ToggleDockingAction) {
|
||||
ToggleDockingAction toggleAction = (ToggleDockingAction) delegateAction;
|
||||
toggleAction.setSelected(!toggleAction.isSelected());
|
||||
|
|
|
@ -206,7 +206,7 @@ public class ToolBarItemManager implements PropertyChangeListener, ActionListene
|
|||
else {
|
||||
return; // context is not valid, nothing to do
|
||||
}
|
||||
tempContext.setSource(event.getSource());
|
||||
tempContext.setSourceObject(event.getSource());
|
||||
final ActionContext finalContext = tempContext;
|
||||
|
||||
// this gives the UI some time to repaint before executing the action
|
||||
|
|
|
@ -1326,7 +1326,7 @@ public abstract class AbstractDockingTest extends AbstractGenericTest {
|
|||
}
|
||||
|
||||
actionContext = newContext;
|
||||
actionContext.setSource(provider.getComponent());
|
||||
actionContext.setSourceObject(provider.getComponent());
|
||||
|
||||
return actionContext;
|
||||
});
|
||||
|
@ -1349,7 +1349,7 @@ public abstract class AbstractDockingTest extends AbstractGenericTest {
|
|||
ActionContext context = runSwing(() -> {
|
||||
ActionContext actionContext = provider.getActionContext(null);
|
||||
if (actionContext != null) {
|
||||
actionContext.setSource(provider.getComponent());
|
||||
actionContext.setSourceObject(provider.getComponent());
|
||||
}
|
||||
return actionContext;
|
||||
});
|
||||
|
@ -2188,6 +2188,25 @@ public abstract class AbstractDockingTest extends AbstractGenericTest {
|
|||
return ref.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a generic action context with no provider, with the given payload
|
||||
* @param payload the generic object to put in the context
|
||||
* @return the new context
|
||||
*/
|
||||
public ActionContext createContext(Object payload) {
|
||||
return new ActionContext().setContextObject(payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a generic action context with the given provider, with the given payload
|
||||
* @param provider the provider
|
||||
* @param payload the generic object to put in the context
|
||||
* @return the new context
|
||||
*/
|
||||
public ActionContext createContext(ComponentProvider provider, Object payload) {
|
||||
return new ActionContext(provider).setContextObject(payload);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Screen Capture
|
||||
//==================================================================================================
|
||||
|
|
|
@ -579,18 +579,6 @@ public class GhidraFileChooser extends DialogComponentProvider
|
|||
//==================================================================================================
|
||||
|
||||
@Override
|
||||
public ActionContext getActionContext(MouseEvent event) {
|
||||
if (event == null) {
|
||||
return super.getActionContext(event);
|
||||
}
|
||||
|
||||
return new ActionContext(null, event.getSource());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.util.filechooser.GhidraFileChooserListener#modelChanged()
|
||||
*/
|
||||
@Override
|
||||
public void modelChanged() {
|
||||
SystemUtilities.runSwingLater(() -> {
|
||||
directoryListModel.update();
|
||||
|
@ -598,9 +586,6 @@ public class GhidraFileChooser extends DialogComponentProvider
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.io.FileFilter#accept(java.io.File)
|
||||
*/
|
||||
@Override
|
||||
public boolean accept(File file) {
|
||||
if (!showDotFiles) {
|
||||
|
|
|
@ -32,7 +32,7 @@ import javax.swing.table.*;
|
|||
import docking.*;
|
||||
import docking.action.*;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
import docking.actions.PopupActionProvider;
|
||||
import docking.actions.ToolActions;
|
||||
import docking.widgets.OptionDialog;
|
||||
import docking.widgets.dialogs.SettingsDialog;
|
||||
import docking.widgets.filechooser.GhidraFileChooser;
|
||||
|
@ -70,12 +70,20 @@ import resources.ResourceManager;
|
|||
*
|
||||
* @see GTableFilterPanel
|
||||
*/
|
||||
public class GTable extends JTable implements KeyStrokeConsumer, PopupActionProvider {
|
||||
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;
|
||||
|
||||
private boolean isInitialized;
|
||||
private boolean allowActions;
|
||||
private KeyListener autoLookupListener;
|
||||
private long lastLookupTime;
|
||||
|
@ -96,14 +104,6 @@ public class GTable extends JTable implements KeyStrokeConsumer, PopupActionProv
|
|||
|
||||
/** A flag to signal that a copy operation is being performed. */
|
||||
private boolean copying;
|
||||
private DockingAction copyAction;
|
||||
private DockingAction copyColumnsAction;
|
||||
private DockingAction copyCurrentColumnAction;
|
||||
private DockingAction selectAllAction;
|
||||
private DockingAction exportAction;
|
||||
private DockingAction exportColumnsAction;
|
||||
|
||||
private String actionMenuGroup = "zzzTableGroup";
|
||||
|
||||
private SelectionManager selectionManager;
|
||||
private Integer visibleRowCount;
|
||||
|
@ -116,11 +116,11 @@ public class GTable extends JTable implements KeyStrokeConsumer, PopupActionProv
|
|||
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, PopupActionProv
|
|||
* @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,48 +474,42 @@ public class GTable extends JTable implements KeyStrokeConsumer, PopupActionProv
|
|||
return autoLookupKeyStrokeConsumer.isKeyConsumed(keyStroke);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DockingActionIf> getPopupActions(DockingTool tool, ActionContext context) {
|
||||
|
||||
// we want these top-level groups to all appear together, with no separator
|
||||
tool.setMenuGroup(new String[] { "Copy" }, actionMenuGroup, "1");
|
||||
tool.setMenuGroup(new String[] { "Export" }, actionMenuGroup, "2");
|
||||
tool.setMenuGroup(new String[] { "Select All" }, actionMenuGroup, "3");
|
||||
|
||||
List<DockingActionIf> list = new ArrayList<>();
|
||||
list.add(copyAction);
|
||||
list.add(copyCurrentColumnAction);
|
||||
list.add(copyColumnsAction);
|
||||
list.add(selectAllAction);
|
||||
list.add(exportAction);
|
||||
list.add(exportColumnsAction);
|
||||
return list;
|
||||
/**
|
||||
* 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 init(boolean 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();
|
||||
|
||||
|
@ -576,10 +534,28 @@ public class GTable extends JTable implements KeyStrokeConsumer, PopupActionProv
|
|||
}
|
||||
});
|
||||
|
||||
createPopupActions();
|
||||
removeActionKeyStrokes();
|
||||
|
||||
// updating the row height requires the 'isInitialized' to be set, so do it first
|
||||
isInitialized = true;
|
||||
initializeRowHeight();
|
||||
}
|
||||
|
||||
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) {
|
||||
header.setUpdateTableInRealTime(true);
|
||||
headerMouseListener = new GTableMouseListener(this);
|
||||
|
@ -600,7 +576,8 @@ public class GTable extends JTable implements KeyStrokeConsumer, PopupActionProv
|
|||
}
|
||||
|
||||
private void adjustRowHeight() {
|
||||
if (copyAction == null) { // crude test to know if our constructor has finished
|
||||
|
||||
if (!isInitialized) {
|
||||
return; // must be initializing
|
||||
}
|
||||
|
||||
|
@ -1153,188 +1130,55 @@ public class GTable extends JTable implements KeyStrokeConsumer, PopupActionProv
|
|||
return converted;
|
||||
}
|
||||
|
||||
private void createPopupActions() {
|
||||
/**
|
||||
* 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 subGroupIndex = 1; // order by insertion
|
||||
String owner = getClass().getSimpleName();
|
||||
owner = "GTable";
|
||||
copyAction = new DockingAction("Table Data Copy", owner, KeyBindingType.SHARED) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
copying = true;
|
||||
Action builtinCopyAction = TransferHandler.getCopyAction();
|
||||
int modelColumn = convertColumnIndexToModel(viewColumn);
|
||||
|
||||
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
|
||||
GTableCellRenderingData renderData = columnRenderingDataMap.get(modelColumn);
|
||||
|
||||
copyCurrentColumnAction =
|
||||
new DockingAction("Table Data Copy Current Column", owner, KeyBindingType.SHARED) {
|
||||
@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 DockingAction("Table Data Copy by Columns", owner, KeyBindingType.SHARED) {
|
||||
@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 DockingAction("Table Data CSV Export", owner, KeyBindingType.SHARED) {
|
||||
@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 DockingAction("Table Data CSV Export (by Columns)", owner, KeyBindingType.SHARED) {
|
||||
@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 DockingAction("Table Select All", owner, KeyBindingType.SHARED) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
selectAll();
|
||||
if (renderData == null) {
|
||||
Settings settings = SettingsImpl.NO_SETTINGS;
|
||||
ConfigurableColumnTableModel configurableModel = getConfigurableColumnTableModel();
|
||||
if (configurableModel != null) {
|
||||
settings = configurableModel.getColumnSettings(modelColumn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
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
|
||||
renderData = new GTableCellRenderingData(this, viewColumn, settings);
|
||||
columnRenderingDataMap.put(modelColumn, renderData);
|
||||
}
|
||||
|
||||
KeyBindingUtils.registerAction(this, copyAction);
|
||||
KeyBindingUtils.registerAction(this, copyCurrentColumnAction);
|
||||
KeyBindingUtils.registerAction(this, selectAllAction);
|
||||
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.
|
||||
*
|
||||
* @return true if popup actions are supported
|
||||
*/
|
||||
protected boolean supportsPopupActions() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void copyColumns(int... copyColumns) {
|
||||
|
@ -1413,43 +1257,216 @@ public class GTable extends JTable implements KeyStrokeConsumer, PopupActionProv
|
|||
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) {
|
||||
|
@ -1478,4 +1495,26 @@ public class GTable extends JTable implements KeyStrokeConsumer, PopupActionProv
|
|||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
private abstract static class GTableAction extends DockingAction {
|
||||
|
||||
GTableAction(String name, String owner) {
|
||||
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 instanceof GTable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -214,7 +214,7 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
|||
table.addPropertyChangeListener(badProgrammingPropertyChangeListener);
|
||||
|
||||
DockingWindowManager.registerComponentLoadedListener(this,
|
||||
windowManager -> initialize(windowManager));
|
||||
(windowManager, provider) -> initialize(windowManager));
|
||||
}
|
||||
|
||||
private void initialize(DockingWindowManager windowManager) {
|
||||
|
|
|
@ -106,7 +106,7 @@ public class TableColumnModelState implements SortListener {
|
|||
// 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.
|
||||
DockingWindowManager.registerComponentLoadedListener(table, windowManager -> {
|
||||
DockingWindowManager.registerComponentLoadedListener(table, (windowManager, provider) -> {
|
||||
if (!enabled) {
|
||||
setEnabled(true);
|
||||
restoreState();
|
||||
|
|
|
@ -112,7 +112,7 @@ public class GTree extends JPanel implements BusyListener {
|
|||
init();
|
||||
|
||||
DockingWindowManager.registerComponentLoadedListener(this,
|
||||
windowManager -> filterProvider.loadFilterPreference(windowManager,
|
||||
(windowManager, provider) -> filterProvider.loadFilterPreference(windowManager,
|
||||
uniquePreferenceKey));
|
||||
|
||||
filterUpdateManager = new SwingUpdateManager(1000, 30000, () -> performNodeFiltering());
|
||||
|
|
|
@ -1571,9 +1571,7 @@ public class GhidraFileChooserTest extends AbstractDockingTest {
|
|||
private ActionContext createDirListContext() {
|
||||
|
||||
DirectoryList dirlist = getDirectoryListViewOfFileChooser();
|
||||
MouseEvent e = new MouseEvent(dirlist, 0, 0, 0, 0, 0, 1, false);
|
||||
ActionContext context = chooser.getActionContext(e);
|
||||
return context;
|
||||
return new ActionContext(null, dirlist);
|
||||
}
|
||||
|
||||
private boolean isEnabled(DockingAction action, ActionContext context) {
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/* ###
|
||||
* 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;
|
||||
|
||||
import javax.swing.JPopupMenu;
|
||||
|
||||
/**
|
||||
* A class to help during testing to get objects otherwise restricted by package
|
||||
*/
|
||||
public class DockingWindowManagerTestHelper {
|
||||
|
||||
/**
|
||||
* Gets the popup menu for the given context
|
||||
* @param dwm the window manager
|
||||
* @param context the action context
|
||||
* @return the popup menu; null if there are no valid actions for the given context
|
||||
*/
|
||||
public static JPopupMenu getPopupMenu(DockingWindowManager dwm, ActionContext context) {
|
||||
|
||||
ActionToGuiMapper mapper = dwm.getActionToGuiMapper();
|
||||
PopupActionManager popupManager = mapper.getPopupActionManager();
|
||||
JPopupMenu popup = popupManager.createPopupMenu(null, context);
|
||||
return popup;
|
||||
}
|
||||
}
|
|
@ -18,8 +18,6 @@ package ghidra.util.bean;
|
|||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
import javax.swing.ButtonModel;
|
||||
|
||||
import org.junit.Before;
|
||||
|
@ -44,7 +42,7 @@ public class EmptyBorderButtonTest extends AbstractDockingTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testButtonBorderOnRollover() {
|
||||
public void testButtonBorderOnRollover() {
|
||||
assertEquals(emptyBorderButton.getBorder(), EmptyBorderButton.NO_BUTTON_BORDER);
|
||||
|
||||
buttonModel.setRollover(true);
|
||||
|
@ -61,7 +59,7 @@ public class EmptyBorderButtonTest extends AbstractDockingTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testButtonBorderOnPress() {
|
||||
public void testButtonBorderOnPress() {
|
||||
assertEquals(emptyBorderButton.getBorder(), EmptyBorderButton.NO_BUTTON_BORDER);
|
||||
|
||||
// just pressing the button does not change the border...
|
||||
|
@ -82,30 +80,15 @@ public class EmptyBorderButtonTest extends AbstractDockingTest {
|
|||
}
|
||||
|
||||
private void setButtonArmed(final boolean armed) {
|
||||
runSwing(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
buttonModel.setArmed(armed);
|
||||
}
|
||||
});
|
||||
runSwing(() -> buttonModel.setArmed(armed));
|
||||
}
|
||||
|
||||
private void setButtonPressed(final boolean pressed) {
|
||||
runSwing(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
buttonModel.setPressed(pressed);
|
||||
}
|
||||
});
|
||||
runSwing(() -> buttonModel.setPressed(pressed));
|
||||
}
|
||||
|
||||
private void setRollover(final boolean isRollover) {
|
||||
runSwing(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
buttonModel.setRollover(isRollover);
|
||||
}
|
||||
});
|
||||
runSwing(() -> buttonModel.setRollover(isRollover));
|
||||
}
|
||||
|
||||
// public void testButtonBorderOnModalDialog() throws InterruptedException {
|
||||
|
@ -146,7 +129,7 @@ public class EmptyBorderButtonTest extends AbstractDockingTest {
|
|||
// }
|
||||
|
||||
@Test
|
||||
public void testButtonEnablementFromAction() {
|
||||
public void testButtonEnablementFromAction() {
|
||||
DockingAction action = new DockingAction("name", "owner") {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
|
@ -158,13 +141,8 @@ public class EmptyBorderButtonTest extends AbstractDockingTest {
|
|||
return true;
|
||||
}
|
||||
};
|
||||
ActionContextProvider contextProvider = new ActionContextProvider() {
|
||||
|
||||
@Override
|
||||
public ActionContext getActionContext(MouseEvent e) {
|
||||
return new ActionContext(null, null, e.getSource());
|
||||
}
|
||||
};
|
||||
ActionContextProvider contextProvider =
|
||||
e -> new ActionContext(null, e.getSource(), e.getComponent());
|
||||
action.setToolBarData(new ToolBarData(ResourceManager.getDefaultIcon()));
|
||||
action.setEnabled(false);
|
||||
|
||||
|
|
|
@ -20,8 +20,6 @@ import java.awt.event.KeyEvent;
|
|||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.JMenuItem;
|
||||
|
||||
import org.jdom.Element;
|
||||
import org.jdom.input.SAXBuilder;
|
||||
|
||||
|
@ -511,7 +509,7 @@ class ToolActionManager implements ToolChestChangeListener {
|
|||
ToolAction runAction = new ToolAction(toolName, "Run_Tool") {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
String name = ((JMenuItem) context.getSourceObject()).getText();
|
||||
String name = getName();
|
||||
Workspace ws = plugin.getActiveWorkspace();
|
||||
ToolChest toolChest = plugin.getActiveProject().getLocalToolChest();
|
||||
ws.runTool(toolChest.getToolTemplate(name));
|
||||
|
@ -529,7 +527,7 @@ class ToolActionManager implements ToolChestChangeListener {
|
|||
ToolAction deleteAction = new ToolAction(toolName, "Delete_Tool") {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
String name = ((JMenuItem) context.getSourceObject()).getText();
|
||||
String name = getName();
|
||||
if (!plugin.confirmDelete(name + " from the project tool chest?")) {
|
||||
return;
|
||||
}
|
||||
|
@ -549,7 +547,7 @@ class ToolActionManager implements ToolChestChangeListener {
|
|||
ToolAction exportAction = new ToolAction(toolName, "Export_Tool") {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
String name = ((JMenuItem) context.getSourceObject()).getText();
|
||||
String name = getName();
|
||||
ToolChest toolChest = plugin.getActiveProject().getLocalToolChest();
|
||||
plugin.exportToolConfig(toolChest.getToolTemplate(name), "Tool Menu");
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ public class ProjectDataActionContext extends ActionContext implements DomainFil
|
|||
public ProjectDataActionContext(ComponentProvider provider, ProjectData projectData,
|
||||
Object contextObject, List<DomainFolder> selectedFolders,
|
||||
List<DomainFile> selectedFiles, Component comp, boolean isActiveProject) {
|
||||
super(provider, contextObject);
|
||||
super(provider, contextObject, comp);
|
||||
this.projectData = projectData;
|
||||
this.selectedFolders = selectedFolders;
|
||||
this.selectedFiles = selectedFiles;
|
||||
|
|
|
@ -25,8 +25,8 @@ import java.util.List;
|
|||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.*;
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.ActionContext;
|
||||
import docking.ComponentProvider;
|
||||
import docking.help.Help;
|
||||
import docking.help.HelpService;
|
||||
import docking.widgets.label.GHtmlLabel;
|
||||
|
@ -288,7 +288,7 @@ public class ProjectDataTablePanel extends JPanel {
|
|||
list.add(info.getDomainFile());
|
||||
}
|
||||
return new ProjectDataActionContext(provider, projectData,
|
||||
model.getRowObject(selectedRows[0]), null, list, table, true);
|
||||
model.getRowObject(selectedRows[0]), null, list, gTable, true);
|
||||
}
|
||||
|
||||
private void checkOpen(MouseEvent e) {
|
||||
|
@ -489,12 +489,8 @@ public class ProjectDataTablePanel extends JPanel {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<DockingActionIf> getPopupActions(DockingTool tool, ActionContext context) {
|
||||
|
||||
// TODO we should at least add the 'copy' action
|
||||
|
||||
// the table's default actions aren't that useful in the Front End
|
||||
return Collections.emptyList();
|
||||
protected boolean supportsPopupActions() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -135,7 +135,7 @@ public class VersionHistoryDialog extends DialogComponentProvider implements Pro
|
|||
|
||||
@Override
|
||||
public ActionContext getActionContext(MouseEvent event) {
|
||||
ActionContext actionContext = new ActionContext(null, versionPanel.getTable(), this);
|
||||
ActionContext actionContext = new ActionContext(null, this, versionPanel.getTable());
|
||||
actionContext.setMouseEvent(event);
|
||||
return actionContext;
|
||||
}
|
||||
|
|
|
@ -426,7 +426,7 @@ public class VersionHistoryPanel extends JPanel implements Draggable {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (context.getContextObject() != table) {
|
||||
if (context.getSourceComponent() != table) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -441,6 +441,8 @@ public abstract class PluginTool extends AbstractDockingTool implements Tool, Se
|
|||
winMgr.setVisible(false);
|
||||
eventMgr.clearLastEvents();
|
||||
pluginMgr.dispose();
|
||||
|
||||
toolActions.removeActions(ToolConstants.TOOL_OWNER);
|
||||
toolActions.dispose();
|
||||
|
||||
if (project != null) {
|
||||
|
|
|
@ -67,10 +67,10 @@ public class KeyBindingsPanel extends JPanel {
|
|||
private Options options;
|
||||
|
||||
private Map<String, List<DockingActionIf>> actionsByFullName;
|
||||
private Map<String, List<String>> actionNamesByKeyStroke;
|
||||
private Map<String, KeyStroke> keyStrokesByFullName;
|
||||
private Map<String, KeyStroke> originalValues; // to know what has been changed
|
||||
private List<DockingActionIf> tableActions;
|
||||
private Map<String, List<String>> actionNamesByKeyStroke = new HashMap<>();
|
||||
private Map<String, KeyStroke> keyStrokesByFullName = new HashMap<>();
|
||||
private Map<String, KeyStroke> originalValues = new HashMap<>(); // to know what has been changed
|
||||
private List<DockingActionIf> tableActions = new ArrayList<>();
|
||||
|
||||
private KeyEntryTextField ksField;
|
||||
private boolean unappliedChanges;
|
||||
|
@ -83,8 +83,8 @@ public class KeyBindingsPanel extends JPanel {
|
|||
public KeyBindingsPanel(PluginTool tool, Options options) {
|
||||
this.tool = tool;
|
||||
this.options = options;
|
||||
tableActions = new ArrayList<>();
|
||||
create();
|
||||
|
||||
createPanelComponents();
|
||||
createActionMap();
|
||||
addListeners();
|
||||
}
|
||||
|
@ -97,11 +97,9 @@ public class KeyBindingsPanel extends JPanel {
|
|||
tableFilterPanel.dispose();
|
||||
tableModel.dispose();
|
||||
actionTable.dispose();
|
||||
propertyChangeListener = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the changes to the actions.
|
||||
*/
|
||||
public void apply() {
|
||||
Iterator<String> iter = keyStrokesByFullName.keySet().iterator();
|
||||
while (iter.hasNext()) {
|
||||
|
@ -160,9 +158,7 @@ public class KeyBindingsPanel extends JPanel {
|
|||
}
|
||||
|
||||
private void createActionMap() {
|
||||
keyStrokesByFullName = new HashMap<>();
|
||||
actionNamesByKeyStroke = new HashMap<>();
|
||||
originalValues = new HashMap<>();
|
||||
|
||||
String longestName = "";
|
||||
|
||||
actionsByFullName = KeyBindingUtils.getAllActionsByFullName(tool);
|
||||
|
@ -198,10 +194,7 @@ public class KeyBindingsPanel extends JPanel {
|
|||
tableModel.fireTableDataChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the components in this panel.
|
||||
*/
|
||||
private void create() {
|
||||
private void createPanelComponents() {
|
||||
setLayout(new BorderLayout(10, 10));
|
||||
|
||||
tableModel = new KeyBindingsTableModel();
|
||||
|
@ -547,6 +540,7 @@ public class KeyBindingsPanel extends JPanel {
|
|||
// add each new key stroke mapping
|
||||
Iterator<String> iterator = keyBindingsMap.keySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
|
||||
String name = iterator.next();
|
||||
KeyStroke keyStroke = keyBindingsMap.get(name);
|
||||
keyStroke = KeyBindingUtils.validateKeyStroke(keyStroke);
|
||||
|
|
Loading…
Reference in a new issue