mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
GT-3044 - Table Actions - test and review fixes
This commit is contained in:
parent
28f6500039
commit
e9c0921cfe
36 changed files with 920 additions and 748 deletions
|
@ -37,7 +37,8 @@ public class SizeAlignmentPanel extends JPanel {
|
||||||
public SizeAlignmentPanel() {
|
public SizeAlignmentPanel() {
|
||||||
super(new BorderLayout());
|
super(new BorderLayout());
|
||||||
TableModel tableModel = new SizeAlignmentTableModel();
|
TableModel tableModel = new SizeAlignmentTableModel();
|
||||||
table = new GhidraTable(tableModel, true);
|
table = new GhidraTable(tableModel);
|
||||||
|
table.setAutoEditEnabled(true);
|
||||||
JScrollPane sp = new JScrollPane(table);
|
JScrollPane sp = new JScrollPane(table);
|
||||||
table.setPreferredScrollableViewportSize(new Dimension(200, 80));
|
table.setPreferredScrollableViewportSize(new Dimension(200, 80));
|
||||||
add(sp, BorderLayout.CENTER);
|
add(sp, BorderLayout.CENTER);
|
||||||
|
@ -45,12 +46,12 @@ public class SizeAlignmentPanel extends JPanel {
|
||||||
|
|
||||||
public void setOrganization(DataOrganizationImpl dataOrganization) {
|
public void setOrganization(DataOrganizationImpl dataOrganization) {
|
||||||
this.dataOrganization = dataOrganization;
|
this.dataOrganization = dataOrganization;
|
||||||
((SizeAlignmentTableModel)table.getModel()).fireTableDataChanged();
|
((SizeAlignmentTableModel) table.getModel()).fireTableDataChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
class SizeAlignmentTableModel extends AbstractTableModel {
|
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 SIZE_COLUMN = 0;
|
||||||
private final int ALIGNMENT_COLUMN = 1;
|
private final int ALIGNMENT_COLUMN = 1;
|
||||||
|
|
||||||
|
@ -69,6 +70,7 @@ public class SizeAlignmentPanel extends JPanel {
|
||||||
return Integer.class;
|
return Integer.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int getColumnCount() {
|
public int getColumnCount() {
|
||||||
return columnNames.length;
|
return columnNames.length;
|
||||||
}
|
}
|
||||||
|
@ -78,10 +80,12 @@ public class SizeAlignmentPanel extends JPanel {
|
||||||
return columnNames[columnIndex];
|
return columnNames[columnIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int getRowCount() {
|
public int getRowCount() {
|
||||||
return dataOrganization.getSizeAlignmentCount() + 1;
|
return dataOrganization.getSizeAlignmentCount() + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Object getValueAt(int rowIndex, int columnIndex) {
|
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||||
int[] sizes = dataOrganization.getSizes();
|
int[] sizes = dataOrganization.getSizes();
|
||||||
if (rowIndex < sizes.length) {
|
if (rowIndex < sizes.length) {
|
||||||
|
@ -92,7 +96,8 @@ public class SizeAlignmentPanel extends JPanel {
|
||||||
else if (columnIndex == ALIGNMENT_COLUMN) {
|
else if (columnIndex == ALIGNMENT_COLUMN) {
|
||||||
try {
|
try {
|
||||||
return dataOrganization.getSizeAlignment(size);
|
return dataOrganization.getSizeAlignment(size);
|
||||||
} catch (NoValueException e) {
|
}
|
||||||
|
catch (NoValueException e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,18 +126,19 @@ public class SizeAlignmentPanel extends JPanel {
|
||||||
}
|
}
|
||||||
int[] sizes = dataOrganization.getSizes();
|
int[] sizes = dataOrganization.getSizes();
|
||||||
if (rowIndex < sizes.length) {
|
if (rowIndex < sizes.length) {
|
||||||
int alignment = ((Integer)value).intValue();
|
int alignment = ((Integer) value).intValue();
|
||||||
int size = sizes[rowIndex];
|
int size = sizes[rowIndex];
|
||||||
dataOrganization.setSizeAlignment(size, alignment);
|
dataOrganization.setSizeAlignment(size, alignment);
|
||||||
}
|
}
|
||||||
if (rowIndex == sizes.length) {
|
if (rowIndex == sizes.length) {
|
||||||
int size = ((Integer)value).intValue();
|
int size = ((Integer) value).intValue();
|
||||||
// Check that we don't already have this size.
|
// Check that we don't already have this size.
|
||||||
try {
|
try {
|
||||||
dataOrganization.getSizeAlignment(size);
|
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;
|
return;
|
||||||
} catch (NoValueException e) {
|
}
|
||||||
|
catch (NoValueException e) {
|
||||||
// Actually don't want to find a value so we can set one below.
|
// 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.
|
int alignment = size; // Set the alignment to match the size initially.
|
||||||
|
|
|
@ -113,9 +113,7 @@ class EnumEditorPanel extends JPanel {
|
||||||
|
|
||||||
// invoke later because the key press on the table causes the selection
|
// invoke later because the key press on the table causes the selection
|
||||||
// to change
|
// to change
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(() -> {
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
try {
|
||||||
if (table.isEditing()) {
|
if (table.isEditing()) {
|
||||||
return; // don't change the selection if a new edit is in progress
|
return; // don't change the selection if a new edit is in progress
|
||||||
|
@ -131,7 +129,6 @@ class EnumEditorPanel extends JPanel {
|
||||||
catch (NoSuchElementException e) {
|
catch (NoSuchElementException e) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,8 +229,8 @@ class EnumEditorPanel extends JPanel {
|
||||||
void deleteSelectedEntries() {
|
void deleteSelectedEntries() {
|
||||||
EnumDataType enuum = getEnum();
|
EnumDataType enuum = getEnum();
|
||||||
int[] rows = getSelectedRows();
|
int[] rows = getSelectedRows();
|
||||||
for (int i = 0; i < rows.length; i++) {
|
for (int row : rows) {
|
||||||
String name = tableModel.getNameAt(rows[i]);
|
String name = tableModel.getNameAt(row);
|
||||||
enuum.remove(name);
|
enuum.remove(name);
|
||||||
}
|
}
|
||||||
tableModel.setEnum(enuum, true);
|
tableModel.setEnum(enuum, true);
|
||||||
|
@ -262,15 +259,12 @@ class EnumEditorPanel extends JPanel {
|
||||||
"All possible Enum values have already been used");
|
"All possible Enum values have already been used");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(() -> {
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
table.setRowSelectionInterval(newRow, newRow);
|
table.setRowSelectionInterval(newRow, newRow);
|
||||||
table.editCellAt(newRow, EnumTableModel.NAME_COL);
|
table.editCellAt(newRow, EnumTableModel.NAME_COL);
|
||||||
Rectangle r = table.getCellRect(newRow, 0, true);
|
Rectangle r = table.getCellRect(newRow, 0, true);
|
||||||
table.scrollRectToVisible(r);
|
table.scrollRectToVisible(r);
|
||||||
provider.stateChanged(null);
|
provider.stateChanged(null);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -418,9 +412,7 @@ class EnumEditorPanel extends JPanel {
|
||||||
|
|
||||||
sizeComboBox = new GhidraComboBox(new Integer[] { 1, 2, 4, 8 });
|
sizeComboBox = new GhidraComboBox(new Integer[] { 1, 2, 4, 8 });
|
||||||
sizeComboBox.setName("Size");
|
sizeComboBox.setName("Size");
|
||||||
sizeComboBox.addItemListener(new ItemListener() {
|
sizeComboBox.addItemListener(e -> {
|
||||||
@Override
|
|
||||||
public void itemStateChanged(ItemEvent e) {
|
|
||||||
Integer length = (Integer) sizeComboBox.getSelectedItem();
|
Integer length = (Integer) sizeComboBox.getSelectedItem();
|
||||||
if (!validateNewLength(length)) {
|
if (!validateNewLength(length)) {
|
||||||
return;
|
return;
|
||||||
|
@ -429,7 +421,6 @@ class EnumEditorPanel extends JPanel {
|
||||||
setStatusMessage("");
|
setStatusMessage("");
|
||||||
tableModel.setLength(length);
|
tableModel.setLength(length);
|
||||||
provider.stateChanged(null);
|
provider.stateChanged(null);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
JLabel label = new GLabel("Category:", SwingConstants.RIGHT);
|
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) {
|
private void vetoSizeChange(final int newLength, final int currentLength, final long badValue) {
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(() -> {
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
setStatusMessage("Enum size of " + newLength + " cannot contain the value " + "0x" +
|
setStatusMessage("Enum size of " + newLength + " cannot contain the value " + "0x" +
|
||||||
Long.toHexString(badValue));
|
Long.toHexString(badValue));
|
||||||
sizeComboBox.setSelectedItem(new Integer(currentLength));
|
sizeComboBox.setSelectedItem(new Integer(currentLength));
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -495,12 +483,9 @@ class EnumEditorPanel extends JPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void focus(final JTextField field) {
|
private void focus(final JTextField field) {
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(() -> {
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
field.requestFocusInWindow();
|
field.requestFocusInWindow();
|
||||||
field.selectAll();
|
field.selectAll();
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -510,7 +495,8 @@ class EnumEditorPanel extends JPanel {
|
||||||
|
|
||||||
private class EnumTable extends GhidraTable {
|
private class EnumTable extends GhidraTable {
|
||||||
EnumTable(TableModel model) {
|
EnumTable(TableModel model) {
|
||||||
super(model, true);
|
super(model);
|
||||||
|
setAutoEditEnabled(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -534,12 +520,7 @@ class EnumEditorPanel extends JPanel {
|
||||||
public EnumCellEditor(JTextField textField) {
|
public EnumCellEditor(JTextField textField) {
|
||||||
super(textField);
|
super(textField);
|
||||||
textField.addKeyListener(editingKeyListener);
|
textField.addKeyListener(editingKeyListener);
|
||||||
textField.addActionListener(new ActionListener() {
|
textField.addActionListener(e -> table.editingStopped(null));
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
table.editingStopped(null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -148,9 +148,8 @@ public class AutoTableDisassemblerPlugin extends ProgramPlugin implements Domain
|
||||||
};
|
};
|
||||||
findTableAction.setHelpLocation(
|
findTableAction.setHelpLocation(
|
||||||
new HelpLocation(HelpTopics.SEARCH, findTableAction.getName()));
|
new HelpLocation(HelpTopics.SEARCH, findTableAction.getName()));
|
||||||
findTableAction.setMenuBarData(
|
findTableAction.setMenuBarData(new MenuData(
|
||||||
new MenuData(new String[] { ToolConstants.MENU_SEARCH, "For Address Tables..." }, null,
|
new String[] { ToolConstants.MENU_SEARCH, "For Address Tables" }, null, "search for"));
|
||||||
"search for"));
|
|
||||||
findTableAction.setDescription(getPluginDescription().getDescription());
|
findTableAction.setDescription(getPluginDescription().getDescription());
|
||||||
|
|
||||||
tool.addAction(findTableAction);
|
tool.addAction(findTableAction);
|
||||||
|
|
|
@ -251,7 +251,7 @@ public class InstructionSearchPlugin extends ProgramPlugin {
|
||||||
};
|
};
|
||||||
searchAction.setHelpLocation(new HelpLocation("Search", "Instruction_Pattern_Search"));
|
searchAction.setHelpLocation(new HelpLocation("Search", "Instruction_Pattern_Search"));
|
||||||
searchAction.setMenuBarData(
|
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"));
|
null, "search for"));
|
||||||
searchAction.setDescription("Construct searches using selected instructions");
|
searchAction.setDescription("Construct searches using selected instructions");
|
||||||
tool.addAction(searchAction);
|
tool.addAction(searchAction);
|
||||||
|
|
|
@ -21,6 +21,7 @@ import javax.swing.JToolBar;
|
||||||
import javax.swing.table.TableCellRenderer;
|
import javax.swing.table.TableCellRenderer;
|
||||||
|
|
||||||
import docking.widgets.table.GTable;
|
import docking.widgets.table.GTable;
|
||||||
|
import ghidra.app.plugin.core.instructionsearch.InstructionSearchPlugin;
|
||||||
import ghidra.app.plugin.core.instructionsearch.model.*;
|
import ghidra.app.plugin.core.instructionsearch.model.*;
|
||||||
import ghidra.util.table.GhidraTable;
|
import ghidra.util.table.GhidraTable;
|
||||||
|
|
||||||
|
@ -85,6 +86,10 @@ public abstract class AbstractInstructionTable extends GhidraTable {
|
||||||
this.setRowHeight(this.getRowHeight() + CELL_HEIGHT_PADDING);
|
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
|
* 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.
|
* 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);
|
SystemUtilities.runSwingLater(runnable);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param addr
|
|
||||||
*/
|
|
||||||
private void goToLocation(Address addr) {
|
private void goToLocation(Address addr) {
|
||||||
GoToService gs = plugin.getTool().getService(GoToService.class);
|
GoToService gs = plugin.getTool().getService(GoToService.class);
|
||||||
gs.goTo(addr);
|
gs.goTo(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public InstructionSearchPlugin getPlugin() {
|
||||||
|
return plugin;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,14 +25,17 @@ import java.util.*;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.table.TableCellRenderer;
|
import javax.swing.table.TableCellRenderer;
|
||||||
|
|
||||||
import docking.*;
|
import docking.ActionContext;
|
||||||
import docking.action.*;
|
import docking.EmptyBorderToggleButton;
|
||||||
|
import docking.action.DockingAction;
|
||||||
|
import docking.action.MenuData;
|
||||||
import docking.dnd.GClipboard;
|
import docking.dnd.GClipboard;
|
||||||
import docking.widgets.EmptyBorderButton;
|
import docking.widgets.EmptyBorderButton;
|
||||||
import ghidra.app.plugin.core.instructionsearch.InstructionSearchPlugin;
|
import ghidra.app.plugin.core.instructionsearch.InstructionSearchPlugin;
|
||||||
import ghidra.app.plugin.core.instructionsearch.model.*;
|
import ghidra.app.plugin.core.instructionsearch.model.*;
|
||||||
import ghidra.app.plugin.core.instructionsearch.ui.SelectionModeWidget.InputMode;
|
import ghidra.app.plugin.core.instructionsearch.ui.SelectionModeWidget.InputMode;
|
||||||
import ghidra.app.plugin.core.instructionsearch.util.InstructionSearchUtils;
|
import ghidra.app.plugin.core.instructionsearch.util.InstructionSearchUtils;
|
||||||
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.exception.InvalidInputException;
|
import ghidra.util.exception.InvalidInputException;
|
||||||
import ghidra.util.task.*;
|
import ghidra.util.task.*;
|
||||||
|
@ -78,6 +81,7 @@ public class PreviewTable extends AbstractInstructionTable {
|
||||||
*
|
*
|
||||||
* @param numColumns the number of columns in the table
|
* @param numColumns the number of columns in the table
|
||||||
* @param plugin the parent plugin
|
* @param plugin the parent plugin
|
||||||
|
* @param dialog the search dialog
|
||||||
*/
|
*/
|
||||||
public PreviewTable(int numColumns, InstructionSearchPlugin plugin,
|
public PreviewTable(int numColumns, InstructionSearchPlugin plugin,
|
||||||
InstructionSearchDialog dialog) {
|
InstructionSearchDialog dialog) {
|
||||||
|
@ -131,30 +135,6 @@ public class PreviewTable extends AbstractInstructionTable {
|
||||||
refreshView();
|
refreshView();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds custom context-sensitive menus to the table. This does NOT modify
|
|
||||||
* any existing menus; it simply adds to them.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
// TODO
|
|
||||||
// TODO
|
|
||||||
// TODO Change the custom menu items to be real Docking Actions
|
|
||||||
// TODO
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
// @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
|
* Replaces the contents of the preview table at the given row with the
|
||||||
* given string.
|
* given string.
|
||||||
|
@ -381,14 +361,6 @@ public class PreviewTable extends AbstractInstructionTable {
|
||||||
return binaryBtn;
|
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
|
* Gathers the search strings for each instruction and returns them as a
|
||||||
* single string.
|
* single string.
|
||||||
|
@ -470,6 +442,10 @@ public class PreviewTable extends AbstractInstructionTable {
|
||||||
private void createContextMenuActions() {
|
private void createContextMenuActions() {
|
||||||
String owner = getClass().getSimpleName();
|
String owner = getClass().getSimpleName();
|
||||||
|
|
||||||
|
InstructionSearchPlugin plugin = getPlugin();
|
||||||
|
PluginTool tool = plugin.getTool();
|
||||||
|
tool.setMenuGroup(new String[] { "Copy Special" }, actionMenuGroup, "1");
|
||||||
|
|
||||||
createCopyNoSpacesAction(owner);
|
createCopyNoSpacesAction(owner);
|
||||||
copyNoSpacesAction.setPopupMenuData(
|
copyNoSpacesAction.setPopupMenuData(
|
||||||
new MenuData(new String[] { "Copy Special", "Selected instructions (no spaces)" },
|
new MenuData(new String[] { "Copy Special", "Selected instructions (no spaces)" },
|
||||||
|
@ -487,6 +463,10 @@ public class PreviewTable extends AbstractInstructionTable {
|
||||||
new MenuData(new String[] { "Copy Special", "Selected Instructions (with comments)" },
|
new MenuData(new String[] { "Copy Special", "Selected Instructions (with comments)" },
|
||||||
ResourceManager.loadImage("images/page_white_copy.png"), actionMenuGroup,
|
ResourceManager.loadImage("images/page_white_copy.png"), actionMenuGroup,
|
||||||
MenuData.NO_MNEMONIC, Integer.toString(1)));
|
MenuData.NO_MNEMONIC, Integer.toString(1)));
|
||||||
|
|
||||||
|
dialog.addAction(copyNoSpacesAction);
|
||||||
|
dialog.addAction(copyInstructionAction);
|
||||||
|
dialog.addAction(copyInstructionWithCommentsAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -518,7 +498,14 @@ public class PreviewTable extends AbstractInstructionTable {
|
||||||
Clipboard clip = GClipboard.getSystemClipboard();
|
Clipboard clip = GClipboard.getSystemClipboard();
|
||||||
clip.setContents(sel, null);
|
clip.setContents(sel, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabledForContext(ActionContext context) {
|
||||||
|
return context.getSourceComponent() == PreviewTable.this;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
copyInstructionWithCommentsAction.setHelpLocation(dialog.getHelpLocatdion());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -539,7 +526,14 @@ public class PreviewTable extends AbstractInstructionTable {
|
||||||
Clipboard clip = GClipboard.getSystemClipboard();
|
Clipboard clip = GClipboard.getSystemClipboard();
|
||||||
clip.setContents(sel, null);
|
clip.setContents(sel, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabledForContext(ActionContext context) {
|
||||||
|
return context.getSourceComponent() == PreviewTable.this;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
copyInstructionAction.setHelpLocation(dialog.getHelpLocatdion());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -560,7 +554,14 @@ public class PreviewTable extends AbstractInstructionTable {
|
||||||
Clipboard clip = GClipboard.getSystemClipboard();
|
Clipboard clip = GClipboard.getSystemClipboard();
|
||||||
clip.setContents(sel, null);
|
clip.setContents(sel, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabledForContext(ActionContext context) {
|
||||||
|
return context.getSourceComponent() == PreviewTable.this;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
copyNoSpacesAction.setHelpLocation(dialog.getHelpLocatdion());
|
||||||
}
|
}
|
||||||
|
|
||||||
private class BinaryAction extends AbstractAction {
|
private class BinaryAction extends AbstractAction {
|
||||||
|
|
|
@ -652,7 +652,8 @@ class MemoryMapProvider extends ComponentProviderAdapter {
|
||||||
|
|
||||||
private class MemoryMapTable extends GhidraTable {
|
private class MemoryMapTable extends GhidraTable {
|
||||||
MemoryMapTable(TableModel model) {
|
MemoryMapTable(TableModel model) {
|
||||||
super(model, true);
|
super(model);
|
||||||
|
setAutoEditEnabled(true);
|
||||||
setActionsEnabled(true);
|
setActionsEnabled(true);
|
||||||
setVisibleRowCount(10);
|
setVisibleRowCount(10);
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,66 +51,10 @@ public class GhidraTable extends GTable {
|
||||||
|
|
||||||
public GhidraTable() {
|
public GhidraTable() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public GhidraTable(TableModel model) {
|
public GhidraTable(TableModel model) {
|
||||||
super(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;
|
package ghidra.util.table.actions;
|
||||||
|
|
||||||
|
import java.awt.Component;
|
||||||
|
|
||||||
import javax.swing.KeyStroke;
|
import javax.swing.KeyStroke;
|
||||||
|
|
||||||
import docking.ActionContext;
|
import docking.ActionContext;
|
||||||
|
@ -36,10 +38,9 @@ import resources.Icons;
|
||||||
*/
|
*/
|
||||||
public class MakeProgramSelectionAction extends DockingAction {
|
public class MakeProgramSelectionAction extends DockingAction {
|
||||||
|
|
||||||
private GhidraTable table;
|
|
||||||
|
|
||||||
// we will have one of these fields be non-null after construction
|
// we will have one of these fields be non-null after construction
|
||||||
private Plugin plugin;
|
private Plugin plugin;
|
||||||
|
private GhidraTable table;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Special constructor for clients that do not have a plugin. Clients using this
|
* Special constructor for clients that do not have a plugin. Clients using this
|
||||||
|
@ -92,8 +93,8 @@ public class MakeProgramSelectionAction extends DockingAction {
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabledForContext(ActionContext context) {
|
public boolean isEnabledForContext(ActionContext context) {
|
||||||
|
|
||||||
Object contextObject = context.getContextObject();
|
Component component = context.getSourceComponent();
|
||||||
if (contextObject != table) {
|
if (component != table) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,8 +33,7 @@ import ghidra.framework.options.ToolOptions;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||||
import ghidra.test.TestEnv;
|
import ghidra.test.TestEnv;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.*;
|
||||||
import ghidra.util.SpyErrorLogger;
|
|
||||||
import resources.Icons;
|
import resources.Icons;
|
||||||
import resources.ResourceManager;
|
import resources.ResourceManager;
|
||||||
|
|
||||||
|
@ -335,8 +334,11 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio
|
||||||
KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, DockingUtils.CONTROL_KEY_MODIFIER_MASK);
|
KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, DockingUtils.CONTROL_KEY_MODIFIER_MASK);
|
||||||
setKeyBindingViaF4Dialog_FromCloseButton(controlEsc);
|
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);
|
pressKey(controlEsc);
|
||||||
assertProviderIsHidden();
|
assertProviderIsHidden_InNonBatchMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -461,9 +463,18 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio
|
||||||
// runSwing(() -> tool.isActive(provider)));
|
// runSwing(() -> tool.isActive(provider)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertProviderIsHidden() {
|
private void assertProviderIsHidden_InNonBatchMode() {
|
||||||
assertFalse("The test provider is showing, but should be hidden",
|
|
||||||
runSwing(() -> tool.isVisible(provider)));
|
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() {
|
private void assertNoToolbarAction() {
|
||||||
|
|
|
@ -27,6 +27,7 @@ import javax.swing.table.TableModel;
|
||||||
|
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
|
import docking.ActionContext;
|
||||||
import docking.action.DockingActionIf;
|
import docking.action.DockingActionIf;
|
||||||
import ghidra.app.cmd.memory.*;
|
import ghidra.app.cmd.memory.*;
|
||||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||||
|
@ -100,11 +101,12 @@ public class MemoryMapPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
for (DockingActionIf action : actions) {
|
for (DockingActionIf action : actions) {
|
||||||
String name = action.getName();
|
String name = action.getName();
|
||||||
if (name.equals("Add Block") || name.equals("Set Image Base") ||
|
if (name.equals("Add Block") || name.equals("Set Image Base") ||
|
||||||
name.equals("Memory Map") || name.equals("Close Window")) {
|
name.equals("Memory Map") || name.equals("Close Window") ||
|
||||||
assertTrue(action.isEnabledForContext(provider.getActionContext(null)));
|
name.contains("Table")) {
|
||||||
|
assertActionEnabled(action, getActionContext(), true);
|
||||||
}
|
}
|
||||||
else {
|
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")) {
|
if (name.equals("Memory Map") || name.equals("Close Window")) {
|
||||||
continue;
|
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
|
@Test
|
||||||
public void testBlockNameChanged() throws Exception {
|
public void testBlockNameChanged() throws Exception {
|
||||||
MemoryBlock block = memory.getBlock(program.getMinAddress());
|
MemoryBlock block = memory.getBlock(program.getMinAddress());
|
||||||
|
|
|
@ -331,26 +331,6 @@ public class NextPrevAddressPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||||
clickMouse(component, MouseEvent.BUTTON1, x, y, 1, 0);
|
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")
|
@SuppressWarnings("unchecked")
|
||||||
// let caution fly
|
// let caution fly
|
||||||
private JButton findButtonForAction(DockingWindowManager windowManager, DockingAction action) {
|
private JButton findButtonForAction(DockingWindowManager windowManager, DockingAction action) {
|
||||||
|
|
|
@ -31,7 +31,7 @@ import javax.swing.table.*;
|
||||||
import org.jdom.Element;
|
import org.jdom.Element;
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
import docking.ActionContext;
|
import docking.*;
|
||||||
import docking.action.DockingActionIf;
|
import docking.action.DockingActionIf;
|
||||||
import docking.action.ToggleDockingAction;
|
import docking.action.ToggleDockingAction;
|
||||||
import docking.widgets.filter.*;
|
import docking.widgets.filter.*;
|
||||||
|
@ -117,7 +117,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNavigation() throws Exception {
|
public void testNavigation() throws Exception {
|
||||||
openProgram("notepad");
|
openProgram("sample");
|
||||||
int row = findRow("ghidra", "Global");
|
int row = findRow("ghidra", "Global");
|
||||||
|
|
||||||
TableModel model = symbolTable.getModel();
|
TableModel model = symbolTable.getModel();
|
||||||
|
@ -128,7 +128,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSortingLabelColumn() throws Exception {
|
public void testSortingLabelColumn() throws Exception {
|
||||||
openProgram("notepad");
|
openProgram("sample");
|
||||||
|
|
||||||
Rectangle rect = symbolTableHeader.getHeaderRect(SymbolTableModel.LABEL_COL);
|
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
|
// Note: this is somewhat of a tripwire test--it is designed to catch a major breakage
|
||||||
// to the DynamicTableColumn discovery mechanism.
|
// to the DynamicTableColumn discovery mechanism.
|
||||||
//
|
//
|
||||||
openProgram("notepad");
|
openProgram("sample");
|
||||||
|
|
||||||
List<String> columnNames = new ArrayList<>();
|
List<String> columnNames = new ArrayList<>();
|
||||||
int columnCount = symbolModel.getColumnCount();
|
int columnCount = symbolModel.getColumnCount();
|
||||||
|
@ -203,7 +203,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSortingAddressColumn() throws Exception {
|
public void testSortingAddressColumn() throws Exception {
|
||||||
openProgram("notepad");
|
openProgram("sample");
|
||||||
|
|
||||||
Rectangle rect = symbolTableHeader.getHeaderRect(SymbolTableModel.LOCATION_COL);
|
Rectangle rect = symbolTableHeader.getHeaderRect(SymbolTableModel.LOCATION_COL);
|
||||||
|
|
||||||
|
@ -239,7 +239,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSortingReferenceColumn() throws Exception {
|
public void testSortingReferenceColumn() throws Exception {
|
||||||
openProgram("notepad");
|
openProgram("sample");
|
||||||
|
|
||||||
sortOnColumn(SymbolTableModel.REFS_COL);
|
sortOnColumn(SymbolTableModel.REFS_COL);
|
||||||
|
|
||||||
|
@ -267,7 +267,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFilter() throws Exception {
|
public void testFilter() throws Exception {
|
||||||
openProgram("notepad");
|
openProgram("sample");
|
||||||
|
|
||||||
performAction(setFilterAction, new ActionContext(), false);
|
performAction(setFilterAction, new ActionContext(), false);
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
|
@ -331,15 +331,6 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
assertEquals(2, symbolTable.getRowCount());
|
assertEquals(2, symbolTable.getRowCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
private FilterDialog showFilterDialog() {
|
|
||||||
|
|
||||||
performAction(setFilterAction, false);
|
|
||||||
|
|
||||||
FilterDialog dialog = waitForDialogComponent(FilterDialog.class);
|
|
||||||
assertNotNull(dialog);
|
|
||||||
return dialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFilterPersistence() throws Exception {
|
public void testFilterPersistence() throws Exception {
|
||||||
|
|
||||||
|
@ -362,39 +353,9 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
assertEquals(savedXml, restoredXml);
|
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
|
@Test
|
||||||
public void testEditing() throws Exception {
|
public void testEditing() throws Exception {
|
||||||
openProgram("notepad");
|
openProgram("sample");
|
||||||
|
|
||||||
waitForNotBusy(symbolTable);
|
waitForNotBusy(symbolTable);
|
||||||
|
|
||||||
|
@ -421,7 +382,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testQuickLookup() throws Exception {
|
public void testQuickLookup() throws Exception {
|
||||||
openProgram("notepad");
|
openProgram("sample");
|
||||||
|
|
||||||
int id = prog.startTransaction(testName.getMethodName());
|
int id = prog.startTransaction(testName.getMethodName());
|
||||||
try {
|
try {
|
||||||
|
@ -440,7 +401,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
waitForNotBusy(symbolTable);
|
waitForNotBusy(symbolTable);
|
||||||
|
|
||||||
runSwing(() -> symbolTable.setRowSelectionInterval(0, 0));
|
selectRow(0);
|
||||||
|
|
||||||
triggerAutoLookup("a");
|
triggerAutoLookup("a");
|
||||||
waitForNotBusy(symbolTable);
|
waitForNotBusy(symbolTable);
|
||||||
|
@ -462,7 +423,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
assertEquals(findRow("abc1", "Global"), symbolTable.getSelectedRow());
|
assertEquals(findRow("abc1", "Global"), symbolTable.getSelectedRow());
|
||||||
Thread.sleep(GTable.KEY_TIMEOUT);
|
Thread.sleep(GTable.KEY_TIMEOUT);
|
||||||
|
|
||||||
runSwing(() -> symbolTable.setRowSelectionInterval(0, 0));
|
selectRow(0);
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
triggerAutoLookup("abc12");
|
triggerAutoLookup("abc12");
|
||||||
waitForNotBusy(symbolTable);
|
waitForNotBusy(symbolTable);
|
||||||
|
@ -471,7 +432,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeleting() throws Exception {
|
public void testDeleting() throws Exception {
|
||||||
openProgram("notepad");
|
openProgram("sample");
|
||||||
|
|
||||||
int rowCount = symbolTable.getRowCount();
|
int rowCount = symbolTable.getRowCount();
|
||||||
assertTrue(!deleteAction.isEnabled());
|
assertTrue(!deleteAction.isEnabled());
|
||||||
|
@ -501,8 +462,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
assertEquals(rowCount, symbolTable.getRowCount());
|
assertEquals(rowCount, symbolTable.getRowCount());
|
||||||
|
|
||||||
final int anotherLocal_RowIndex = findRow("AnotherLocal", "Global");
|
final int anotherLocal_RowIndex = findRow("AnotherLocal", "Global");
|
||||||
runSwing(() -> symbolTable.setRowSelectionInterval(anotherLocal_RowIndex,
|
selectRow(anotherLocal_RowIndex);
|
||||||
anotherLocal_RowIndex));
|
|
||||||
|
|
||||||
int selectedRow = symbolTable.getSelectedRow();
|
int selectedRow = symbolTable.getSelectedRow();
|
||||||
assertEquals("Row was not selected!", anotherLocal_RowIndex, selectedRow);
|
assertEquals("Row was not selected!", anotherLocal_RowIndex, selectedRow);
|
||||||
|
@ -538,29 +498,37 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
setupSymbolTableFilterToShowParameters();
|
setupSymbolTableFilterToShowParameters();
|
||||||
|
|
||||||
final int row = getRowForSymbol(param1Symbol);
|
int row = getRowForSymbol(param1Symbol);
|
||||||
|
selectRow(row);
|
||||||
// select that row
|
|
||||||
runSwing(() -> symbolTable.setRowSelectionInterval(row, row));
|
|
||||||
|
|
||||||
// execute the delete action
|
// execute the delete action
|
||||||
performAction(deleteAction, true);
|
performAction(deleteAction, true);
|
||||||
Assert.assertNotEquals(param1Symbol, getUniqueSymbol(prog, "param_1", function));
|
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
|
@Test
|
||||||
public void testMakeSelection() throws Exception {
|
public void testMakeSelection() throws Exception {
|
||||||
openProgram("notepad");
|
openProgram("sample");
|
||||||
|
|
||||||
assertTrue(!makeSelectionAction.isEnabled());
|
assertTrue(!makeSelectionAction.isEnabled());
|
||||||
|
|
||||||
final int row = findRow("ghidra", "Global");
|
final int row = findRow("ghidra", "Global");
|
||||||
int rowCount = 3;
|
int rowCount = 3;
|
||||||
runSwing(() -> {
|
selectRow(row, row + 2);
|
||||||
symbolTable.setRowSelectionInterval(row, row + 2);
|
|
||||||
Rectangle rect = symbolTable.getCellRect(row + 2, 0, true);
|
|
||||||
symbolTable.scrollRectToVisible(rect);
|
|
||||||
});
|
|
||||||
|
|
||||||
assertTrue(makeSelectionAction.isEnabled());
|
assertTrue(makeSelectionAction.isEnabled());
|
||||||
|
|
||||||
|
@ -590,14 +558,11 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetAndClearPinnedAction() throws Exception {
|
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);
|
ActionContext actionContext = provider.getActionContext(null);
|
||||||
int[] selectedRows = symbolTable.getSelectedRows();
|
int[] selectedRows = symbolTable.getSelectedRows();
|
||||||
assertEquals(3, selectedRows.length);
|
assertEquals(3, selectedRows.length);
|
||||||
|
@ -626,14 +591,11 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetPinnedActionNotEnabledForExternalSymbols() throws Exception {
|
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);
|
ActionContext actionContext = provider.getActionContext(null);
|
||||||
int[] selectedRows = symbolTable.getSelectedRows();
|
int[] selectedRows = symbolTable.getSelectedRows();
|
||||||
|
|
||||||
|
@ -648,7 +610,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateOnSymbolsAdded() throws Exception {
|
public void testUpdateOnSymbolsAdded() throws Exception {
|
||||||
openProgram("notepad");
|
openProgram("sample");
|
||||||
Address sample = prog.getMinAddress();
|
Address sample = prog.getMinAddress();
|
||||||
SymbolTable st = prog.getSymbolTable();
|
SymbolTable st = prog.getSymbolTable();
|
||||||
Symbol sym = null;
|
Symbol sym = null;
|
||||||
|
@ -673,7 +635,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSymbolsAddedWithFilterOn() throws Exception {
|
public void testSymbolsAddedWithFilterOn() throws Exception {
|
||||||
openProgram("notepad");
|
openProgram("sample");
|
||||||
|
|
||||||
final JTextField textField = getFilterTextField();
|
final JTextField textField = getFilterTextField();
|
||||||
final JCheckBox checkBox = findComponent(filterPanel, JCheckBox.class);
|
final JCheckBox checkBox = findComponent(filterPanel, JCheckBox.class);
|
||||||
|
@ -709,7 +671,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDefaultFunctionToNamedFunctionWithFilterOn() throws Exception {
|
public void testDefaultFunctionToNamedFunctionWithFilterOn() throws Exception {
|
||||||
openProgram("notepad");
|
openProgram("sample");
|
||||||
|
|
||||||
performAction(setFilterAction, new ActionContext(), false);
|
performAction(setFilterAction, new ActionContext(), false);
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
|
@ -742,7 +704,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateOnSymbolsRemoved() throws Exception {
|
public void testUpdateOnSymbolsRemoved() throws Exception {
|
||||||
openProgram("notepad");
|
openProgram("sample");
|
||||||
|
|
||||||
SymbolTable st = prog.getSymbolTable();
|
SymbolTable st = prog.getSymbolTable();
|
||||||
Symbol sym = getUniqueSymbol(prog, "entry");
|
Symbol sym = getUniqueSymbol(prog, "entry");
|
||||||
|
@ -765,7 +727,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateOnReferencesAdded() throws Exception {
|
public void testUpdateOnReferencesAdded() throws Exception {
|
||||||
openProgram("notepad");
|
openProgram("sample");
|
||||||
Address sample = prog.getMinAddress();
|
Address sample = prog.getMinAddress();
|
||||||
|
|
||||||
Symbol s = getUniqueSymbol(prog, "entry");
|
Symbol s = getUniqueSymbol(prog, "entry");
|
||||||
|
@ -796,7 +758,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateOnReferencesRemoved() throws Exception {
|
public void testUpdateOnReferencesRemoved() throws Exception {
|
||||||
openProgram("notepad");
|
openProgram("sample");
|
||||||
Address sample = prog.getMinAddress();
|
Address sample = prog.getMinAddress();
|
||||||
|
|
||||||
Symbol s = getUniqueSymbol(prog, "doStuff");
|
Symbol s = getUniqueSymbol(prog, "doStuff");
|
||||||
|
@ -836,7 +798,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateOnProgramRestore() throws Exception {
|
public void testUpdateOnProgramRestore() throws Exception {
|
||||||
openProgram("notepad");
|
openProgram("sample");
|
||||||
|
|
||||||
int id = prog.startTransaction(testName.getMethodName());
|
int id = prog.startTransaction(testName.getMethodName());
|
||||||
try {
|
try {
|
||||||
|
@ -959,7 +921,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReferences() throws Exception {
|
public void testReferences() throws Exception {
|
||||||
openProgram("notepad");
|
openProgram("sample");
|
||||||
|
|
||||||
showReferencesTable();
|
showReferencesTable();
|
||||||
|
|
||||||
|
@ -1020,7 +982,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFilterTextField() throws Exception {
|
public void testFilterTextField() throws Exception {
|
||||||
openProgram("notepad");
|
openProgram("sample");
|
||||||
|
|
||||||
JTextField textField = getFilterTextField();
|
JTextField textField = getFilterTextField();
|
||||||
|
|
||||||
|
@ -1167,7 +1129,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFilterTextFieldFindsAllMatches() throws Exception {
|
public void testFilterTextFieldFindsAllMatches() throws Exception {
|
||||||
openProgram("notepad");
|
openProgram("sample");
|
||||||
|
|
||||||
JTextField textField = getFilterTextField();
|
JTextField textField = getFilterTextField();
|
||||||
|
|
||||||
|
@ -1214,6 +1176,86 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
// Helper methods
|
// 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) {
|
private void triggerAutoLookup(String text) {
|
||||||
|
|
||||||
KeyListener listener = (KeyListener) getInstanceField("autoLookupListener", symbolTable);
|
KeyListener listener = (KeyListener) getInstanceField("autoLookupListener", symbolTable);
|
||||||
|
|
|
@ -29,6 +29,7 @@ import javax.swing.tree.TreePath;
|
||||||
|
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
|
import docking.ComponentProvider;
|
||||||
import docking.action.DockingActionIf;
|
import docking.action.DockingActionIf;
|
||||||
import docking.actions.KeyBindingUtils;
|
import docking.actions.KeyBindingUtils;
|
||||||
import docking.options.editor.OptionsDialog;
|
import docking.options.editor.OptionsDialog;
|
||||||
|
@ -86,6 +87,13 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
// Msg.debug(this, "Writing debug data to: " + file);
|
// Msg.debug(this, "Writing debug data to: " + file);
|
||||||
// debug = new FileWriter(file);
|
// debug = new FileWriter(file);
|
||||||
|
|
||||||
|
// debug to the local console
|
||||||
|
// debug = new PrintWriter(System.out);
|
||||||
|
|
||||||
|
setUpTool();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setUpTool() throws Exception {
|
||||||
debug("setUp()");
|
debug("setUp()");
|
||||||
|
|
||||||
env = new TestEnv();
|
env = new TestEnv();
|
||||||
|
@ -104,9 +112,21 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
tool.addPlugin(FunctionPlugin.class.getName());
|
tool.addPlugin(FunctionPlugin.class.getName());
|
||||||
tool.addPlugin(EquateTablePlugin.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");
|
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) {
|
private void debug(String message) {
|
||||||
if (debug == null) {
|
if (debug == null) {
|
||||||
return;
|
return;
|
||||||
|
@ -208,7 +228,7 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
ToolOptions originalOptions = importOptions(saveFile);
|
ToolOptions originalOptions = importOptions(saveFile);
|
||||||
|
|
||||||
assertOptionsMatch(
|
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);
|
toolKeyBindingOptions, originalOptions);
|
||||||
|
|
||||||
debug("c");
|
debug("c");
|
||||||
|
@ -220,7 +240,7 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
// verify the changes are different than the original values
|
// verify the changes are different than the original values
|
||||||
assertOptionsDontMatch(
|
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);
|
toolKeyBindingOptions, originalOptions);
|
||||||
|
|
||||||
debug("e");
|
debug("e");
|
||||||
|
@ -319,6 +339,7 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
private void setKeyBindingsUpDialog() throws Exception {
|
private void setKeyBindingsUpDialog() throws Exception {
|
||||||
env.showTool();
|
env.showTool();
|
||||||
|
showTableProvider();
|
||||||
setKeyBindingsUpDialog(tool);
|
setKeyBindingsUpDialog(tool);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -531,13 +552,13 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
// compares the provided options with the mapping of property names to
|
// compares the provided options with the mapping of property names to
|
||||||
// keystrokes (the map is obtained from the key bindings panel after an
|
// keystrokes (the map is obtained from the key bindings panel after an
|
||||||
// import is done).
|
// import is done).
|
||||||
private boolean compareOptionsWithKeyStrokeMap(Options options,
|
private boolean compareOptionsWithKeyStrokeMap(Options oldOptions,
|
||||||
Map<String, KeyStroke> panelKeyStrokeMap) {
|
Map<String, KeyStroke> panelKeyStrokeMap) {
|
||||||
List<String> propertyNames = options.getOptionNames();
|
List<String> propertyNames = oldOptions.getOptionNames();
|
||||||
for (String element : propertyNames) {
|
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);
|
KeyStroke panelKs = panelKeyStrokeMap.get(element);
|
||||||
|
|
||||||
// if the value is null, then it would not have been placed into the options map
|
// 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() {
|
public void testManagedKeyBindings() {
|
||||||
Set<DockingActionIf> list = tool.getAllActions();
|
Set<DockingActionIf> list = tool.getAllActions();
|
||||||
for (DockingActionIf action : list) {
|
for (DockingActionIf action : list) {
|
||||||
if (action.getKeyBindingType().isManaged()) {
|
if (!ignoreAction(action)) {
|
||||||
assertTrue(actionInTable(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
|
@Test
|
||||||
public void testEditKeyBinding() throws Exception {
|
public void testEditKeyBinding() throws Exception {
|
||||||
// find action that has a keystroke assigned
|
// find action that has a keystroke assigned
|
||||||
|
@ -316,14 +326,14 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean supportsKeyBindings(DockingActionIf action) {
|
private boolean supportsKeyBindings(DockingActionIf action) {
|
||||||
return action.getKeyBindingType().isManaged();
|
return ignoreAction(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DockingActionIf getKeyBindingPluginAction() {
|
private DockingActionIf getKeyBindingPluginAction() {
|
||||||
Set<DockingActionIf> list = tool.getAllActions();
|
Set<DockingActionIf> list = tool.getAllActions();
|
||||||
for (DockingActionIf action : list) {
|
for (DockingActionIf action : list) {
|
||||||
KeyStroke ks = action.getKeyBinding();
|
KeyStroke ks = action.getKeyBinding();
|
||||||
if (action.getKeyBindingType().isManaged() && ks != null &&
|
if (ignoreAction(action) && ks != null &&
|
||||||
ks != KeyStroke.getKeyStroke(KeyEvent.VK_Z, 0)) {
|
ks != KeyStroke.getKeyStroke(KeyEvent.VK_Z, 0)) {
|
||||||
return action;
|
return action;
|
||||||
}
|
}
|
||||||
|
@ -331,7 +341,7 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean actionInTable(DockingActionIf action) {
|
private boolean actionInKeyBindingsTable(DockingActionIf action) {
|
||||||
String actionName = action.getName();
|
String actionName = action.getName();
|
||||||
KeyStroke ks = action.getKeyBinding();
|
KeyStroke ks = action.getKeyBinding();
|
||||||
|
|
||||||
|
@ -373,15 +383,14 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
private void setUpDialog() throws Exception {
|
private void setUpDialog() throws Exception {
|
||||||
runSwing(() -> {
|
runSwing(() -> {
|
||||||
panel = new KeyBindingsPanel(tool, tool.getOptions(DockingToolConstants.KEY_BINDINGS));
|
panel = new KeyBindingsPanel(tool, tool.getOptions(DockingToolConstants.KEY_BINDINGS));
|
||||||
|
panel.setOptionsPropertyChangeListener(evt -> {
|
||||||
|
// stub
|
||||||
|
});
|
||||||
|
|
||||||
dialog = new JDialog(tool.getToolFrame(), "Test KeyBindings", false);
|
dialog = new JDialog(tool.getToolFrame(), "Test KeyBindings", false);
|
||||||
dialog.getContentPane().add(panel);
|
dialog.getContentPane().add(panel);
|
||||||
dialog.pack();
|
dialog.pack();
|
||||||
dialog.setVisible(true);
|
dialog.setVisible(true);
|
||||||
// set the dialog so that the panel can enable the apply button
|
|
||||||
panel.setOptionsPropertyChangeListener(evt -> {
|
|
||||||
// stub
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
table = findComponent(panel, JTable.class);
|
table = findComponent(panel, JTable.class);
|
||||||
keyField = findComponent(panel, JTextField.class);
|
keyField = findComponent(panel, JTextField.class);
|
||||||
|
@ -390,21 +399,26 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
model = table.getModel();
|
model = table.getModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// find 2 actions that do not have key bindings so that we can add and change the values
|
||||||
private void grabActionsWithoutKeybinding() {
|
private void grabActionsWithoutKeybinding() {
|
||||||
Set<DockingActionIf> list = tool.getAllActions();
|
Set<DockingActionIf> list = tool.getAllActions();
|
||||||
for (DockingActionIf action : list) {
|
for (DockingActionIf action : list) {
|
||||||
if (!action.getKeyBindingType().isManaged()) {
|
if (ignoreAction(action)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (action.getKeyBinding() != null) {
|
if (action.getKeyBinding() != null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// good action
|
|
||||||
if (action1 == null) {
|
if (action1 == null) {
|
||||||
action1 = action;
|
action1 = action;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
||||||
|
if (action.getName().equals(action1.getName())) {
|
||||||
|
continue; // same name, different owners; these are 'shared' actions--ignore
|
||||||
|
}
|
||||||
|
|
||||||
action2 = action;
|
action2 = action;
|
||||||
return; // grabbed all actions--we are done
|
return; // grabbed all actions--we are done
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,9 +67,4 @@ public class DummyToolActions implements DockingToolActions {
|
||||||
public void removeActions(ComponentProvider provider) {
|
public void removeActions(ComponentProvider provider) {
|
||||||
// stub
|
// stub
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean containsAction(DockingActionIf action) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,13 +16,15 @@
|
||||||
package ghidra.bitpatterns.gui;
|
package ghidra.bitpatterns.gui;
|
||||||
|
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.awt.event.ActionEvent;
|
import java.util.ArrayList;
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
|
import org.apache.commons.collections4.list.LazyList;
|
||||||
|
|
||||||
import docking.widgets.label.GLabel;
|
import docking.widgets.label.GLabel;
|
||||||
|
import docking.widgets.table.AbstractSortedTableModel;
|
||||||
import docking.widgets.table.GTable;
|
import docking.widgets.table.GTable;
|
||||||
import docking.widgets.textfield.IntegerTextField;
|
import docking.widgets.textfield.IntegerTextField;
|
||||||
import ghidra.bitpatterns.info.FileBitPatternInfoReader;
|
import ghidra.bitpatterns.info.FileBitPatternInfoReader;
|
||||||
|
@ -34,8 +36,6 @@ import ghidra.util.layout.PairLayout;
|
||||||
*/
|
*/
|
||||||
public class AlignmentPanelBuilder extends ContextRegisterFilterablePanelBuilder {
|
public class AlignmentPanelBuilder extends ContextRegisterFilterablePanelBuilder {
|
||||||
private static final int DEFAULT_MODULUS = 16;
|
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 MODULUS_FIELD_TEXT = " Alignment Modulus ";
|
||||||
private static final String RECOMPUTE_BUTTON_TEXT = "Compute Alignment Info";
|
private static final String RECOMPUTE_BUTTON_TEXT = "Compute Alignment Info";
|
||||||
|
|
||||||
|
@ -75,12 +75,7 @@ public class AlignmentPanelBuilder extends ContextRegisterFilterablePanelBuilder
|
||||||
modulusPanel.add(modulusField.getComponent());
|
modulusPanel.add(modulusField.getComponent());
|
||||||
|
|
||||||
JButton recomputeButton = new JButton(RECOMPUTE_BUTTON_TEXT);
|
JButton recomputeButton = new JButton(RECOMPUTE_BUTTON_TEXT);
|
||||||
recomputeButton.addActionListener(new ActionListener() {
|
recomputeButton.addActionListener(e -> updateAlignmentPanel());
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
updateAlignmentPanel();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
getButtonPanel().add(recomputeButton);
|
getButtonPanel().add(recomputeButton);
|
||||||
|
|
||||||
|
@ -109,11 +104,13 @@ public class AlignmentPanelBuilder extends ContextRegisterFilterablePanelBuilder
|
||||||
}
|
}
|
||||||
|
|
||||||
private GTable createAlignmentTable(List<Long> startingAddresses, int numFuncs) {
|
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) {
|
if (startingAddresses != null) {
|
||||||
for (int i = 0; i < modulus; i++) {
|
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];
|
long[] countsAsLongs = new long[modulus];
|
||||||
for (Long currentAddress : startingAddresses) {
|
for (Long currentAddress : startingAddresses) {
|
||||||
countsAsLongs[(int) (Long.remainderUnsigned(currentAddress, modulus))] =
|
countsAsLongs[(int) (Long.remainderUnsigned(currentAddress, modulus))] =
|
||||||
|
@ -121,11 +118,13 @@ public class AlignmentPanelBuilder extends ContextRegisterFilterablePanelBuilder
|
||||||
}
|
}
|
||||||
for (int i = 0; i < modulus; i++) {
|
for (int i = 0; i < modulus; i++) {
|
||||||
double percent = (100.0 * countsAsLongs[i]) / numFuncs;
|
double percent = (100.0 * countsAsLongs[i]) / numFuncs;
|
||||||
modulusInfo[i][2] = Double.toString(Math.round(percent));
|
data.get(i).counts = Long.toString(countsAsLongs[i]);
|
||||||
modulusInfo[i][1] = 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;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,4 +160,69 @@ public class AlignmentPanelBuilder extends ContextRegisterFilterablePanelBuilder
|
||||||
updateAlignmentPanel();
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -529,11 +529,7 @@ public abstract class DecompilerCodeComparisonPanel<T extends DualDecompilerFiel
|
||||||
@Override
|
@Override
|
||||||
public ActionContext getActionContext(ComponentProvider provider, MouseEvent event) {
|
public ActionContext getActionContext(ComponentProvider provider, MouseEvent event) {
|
||||||
|
|
||||||
Component component = null;
|
Component component = event == null ? null : event.getComponent();
|
||||||
if (event != null) {
|
|
||||||
component = event.getComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
CDisplayPanel focusedDecompilerPanel = getFocusedDecompilerPanel();
|
CDisplayPanel focusedDecompilerPanel = getFocusedDecompilerPanel();
|
||||||
DualDecompilerActionContext dualDecompContext =
|
DualDecompilerActionContext dualDecompContext =
|
||||||
new DualDecompilerActionContext(provider, focusedDecompilerPanel, component);
|
new DualDecompilerActionContext(provider, focusedDecompilerPanel, component);
|
||||||
|
|
|
@ -24,7 +24,6 @@ import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.listing.Function;
|
import ghidra.program.model.listing.Function;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.model.pcode.*;
|
import ghidra.program.model.pcode.*;
|
||||||
import ghidra.util.Msg;
|
|
||||||
|
|
||||||
public class DecompilerUtils {
|
public class DecompilerUtils {
|
||||||
|
|
||||||
|
@ -315,11 +314,6 @@ public class DecompilerUtils {
|
||||||
int nchild = parentNode.numChildren();
|
int nchild = parentNode.numChildren();
|
||||||
for (int i = 0; i < nchild; i++) {
|
for (int i = 0; i < nchild; i++) {
|
||||||
ClangNode node = parentNode.Child(i);
|
ClangNode node = parentNode.Child(i);
|
||||||
|
|
||||||
if (node instanceof ClangFuncProto) {
|
|
||||||
Msg.debug(null, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.numChildren() > 0) {
|
if (node.numChildren() > 0) {
|
||||||
collectTokens(tokenList, node, addressSet);
|
collectTokens(tokenList, node, addressSet);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,11 @@ import docking.action.DockingActionIf;
|
||||||
* {@link DockingActionIf}s for them to decide if they are enabled for a given user action. User
|
* {@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
|
* 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,
|
* the user changes focus in the system, all actions are queried with the current context. Thus,
|
||||||
* toolbar buttons and menu items will enable and disable as the user interacts with the system.
|
* <b>toolbar buttons and menu items will enable and disable as the user interacts with the system.
|
||||||
|
* Further, popup menu items will not be added to popup menus when they report false for
|
||||||
|
* {@link DockingActionIf#isAddToPopup(ActionContext)}; they will appear in the popup, but be
|
||||||
|
* disabled if they report <tt>true</tt> for the above call, but <tt>false</tt> for
|
||||||
|
* {@link DockingActionIf#isEnabledForContext(ActionContext)}.</b>
|
||||||
* When the user executes an action, the current context will be passed to the backing
|
* 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
|
* {@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
|
* allow plugins to share state with actions without having to store that state information
|
||||||
|
|
|
@ -126,6 +126,10 @@ public class ActionToGuiMapper {
|
||||||
menuAndToolBarManager.contextChanged(placeHolder);
|
menuAndToolBarManager.contextChanged(placeHolder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PopupActionManager getPopupActionManager() {
|
||||||
|
return popupActionManager;
|
||||||
|
}
|
||||||
|
|
||||||
public MenuGroupMap getMenuGroupMap() {
|
public MenuGroupMap getMenuGroupMap() {
|
||||||
return menuGroupMap;
|
return menuGroupMap;
|
||||||
}
|
}
|
||||||
|
|
|
@ -396,19 +396,35 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
|
||||||
return createContext(c, null);
|
return createContext(c, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
/**
|
||||||
|
* A default method for creating an action context for this provider
|
||||||
|
* @return the new context
|
||||||
|
*/
|
||||||
protected ActionContext createContext() {
|
protected ActionContext createContext() {
|
||||||
return new ActionContext(this);
|
return new ActionContext(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
/**
|
||||||
protected ActionContext createContext(Object payload) {
|
* A default method for creating an action context for this provider, using the given
|
||||||
return new ActionContext(this).setContextObject(payload);
|
* {@link ActionContext#getContextObject() context object}
|
||||||
|
*
|
||||||
|
* @param contextObject the provider-specific context object
|
||||||
|
* @return the new context
|
||||||
|
*/
|
||||||
|
protected ActionContext createContext(Object contextObject) {
|
||||||
|
return new ActionContext(this).setContextObject(contextObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
/**
|
||||||
protected ActionContext createContext(Component source, Object payload) {
|
* A default method for creating an action context for this provider, using the given
|
||||||
return new ActionContext(this, source).setContextObject(payload);
|
* {@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.ActionAdapter;
|
||||||
import docking.actions.KeyBindingUtils;
|
import docking.actions.KeyBindingUtils;
|
||||||
import docking.event.mouse.GMouseListenerAdapter;
|
import docking.event.mouse.GMouseListenerAdapter;
|
||||||
|
import docking.help.HelpService;
|
||||||
import docking.menu.DockingToolbarButton;
|
import docking.menu.DockingToolbarButton;
|
||||||
import docking.util.AnimationUtils;
|
import docking.util.AnimationUtils;
|
||||||
import docking.widgets.label.GDHtmlLabel;
|
import docking.widgets.label.GDHtmlLabel;
|
||||||
|
@ -990,6 +991,15 @@ public class DialogComponentProvider
|
||||||
DockingWindowManager.setHelpLocation(rootPanel, helpLocation);
|
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
|
* 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.
|
* desired, then pass <tt>null</tt> as the <tt>button</tt> value.
|
||||||
|
@ -1139,6 +1149,10 @@ public class DialogComponentProvider
|
||||||
return new ActionContext(null, c);
|
return new ActionContext(null, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component sourceComponent = event.getComponent();
|
||||||
|
if (sourceComponent != null) {
|
||||||
|
c = sourceComponent;
|
||||||
|
}
|
||||||
return new ActionContext(null, c).setSourceObject(event.getSource());
|
return new ActionContext(null, c).setSourceObject(event.getSource());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -88,43 +88,14 @@ public class DialogComponentProviderPopupActionManager {
|
||||||
private void populatePopupMenuActions(DockingWindowManager dwm, MenuManager menuMgr,
|
private void populatePopupMenuActions(DockingWindowManager dwm, MenuManager menuMgr,
|
||||||
ActionContext actionContext) {
|
ActionContext actionContext) {
|
||||||
|
|
||||||
Iterator<DockingActionIf> iter = popupActions.iterator();
|
// This is a bit of a kludge, but allows us to get generic actions, like 'copy' for
|
||||||
while (iter.hasNext()) {
|
// tables. This can go away if we ever convert DialogComponentProviders to use the
|
||||||
DockingActionIf action = iter.next();
|
// primary action system (this was something we were going to do once). If that happens,
|
||||||
MenuData popupMenuData = action.getPopupMenuData();
|
// then this entire class goes away.
|
||||||
if (popupMenuData != null && action.isValidContext(actionContext) &&
|
ActionToGuiMapper actionManager = dwm.getActionToGuiMapper();
|
||||||
action.isAddToPopup(actionContext)) {
|
PopupActionManager toolPopupManager = actionManager.getPopupActionManager();
|
||||||
|
Iterator<DockingActionIf> localActions = popupActions.iterator();
|
||||||
action.setEnabled(action.isEnabledForContext(actionContext));
|
toolPopupManager.populatePopupMenuActions(localActions, actionContext, menuMgr);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
|
@ -23,6 +23,8 @@ import java.util.*;
|
||||||
|
|
||||||
import javax.swing.JPopupMenu;
|
import javax.swing.JPopupMenu;
|
||||||
|
|
||||||
|
import org.apache.commons.collections4.IteratorUtils;
|
||||||
|
|
||||||
import docking.action.*;
|
import docking.action.*;
|
||||||
import docking.menu.*;
|
import docking.menu.*;
|
||||||
|
|
||||||
|
@ -66,6 +68,7 @@ public class PopupActionManager implements PropertyChangeListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
void popupMenu(ComponentPlaceholder info, MouseEvent e) {
|
void popupMenu(ComponentPlaceholder info, MouseEvent e) {
|
||||||
|
|
||||||
if (e.isConsumed()) {
|
if (e.isConsumed()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -78,24 +81,38 @@ public class PopupActionManager implements PropertyChangeListener {
|
||||||
actionContext.setSourceObject(e.getSource());
|
actionContext.setSourceObject(e.getSource());
|
||||||
actionContext.setMouseEvent(e);
|
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 =
|
MenuManager menuMgr =
|
||||||
new MenuManager("Popup", '\0', null, true, popupMenuHandler, menuGroupMap);
|
new MenuManager("Popup", '\0', null, true, popupMenuHandler, menuGroupMap);
|
||||||
populatePopupMenuActions(info, actionContext, menuMgr);
|
populatePopupMenuActions(localActions, context, menuMgr);
|
||||||
if (menuMgr.isEmpty()) {
|
if (menuMgr.isEmpty()) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Popup menu if items are available
|
// Popup menu if items are available
|
||||||
JPopupMenu popupMenu = menuMgr.getPopupMenu();
|
JPopupMenu popupMenu = menuMgr.getPopupMenu();
|
||||||
Component c = (Component) e.getSource();
|
|
||||||
popupMenu.addPopupMenuListener(popupMenuHandler);
|
popupMenu.addPopupMenuListener(popupMenuHandler);
|
||||||
popupMenu.show(c, e.getX(), e.getY());
|
return popupMenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void populatePopupMenuActions(ComponentPlaceholder info, ActionContext actionContext,
|
void populatePopupMenuActions(Iterator<DockingActionIf> localActions,
|
||||||
MenuManager menuMgr) {
|
ActionContext actionContext, MenuManager menuMgr) {
|
||||||
|
|
||||||
// Unregistered actions are those used by special-needs components, on-the-fly
|
// Unregistered actions are those used by special-needs components, on-the-fly
|
||||||
addUnregisteredActions(actionContext, menuMgr);
|
addUnregisteredActions(actionContext, menuMgr);
|
||||||
|
@ -129,9 +146,8 @@ public class PopupActionManager implements PropertyChangeListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Include local actions for focused component
|
// Include local actions for focused component
|
||||||
iter = info.getActions();
|
while (localActions.hasNext()) {
|
||||||
while (iter.hasNext()) {
|
DockingActionIf action = localActions.next();
|
||||||
DockingActionIf action = iter.next();
|
|
||||||
if (action.getPopupMenuData() != null && action.isValidContext(actionContext) &&
|
if (action.getPopupMenuData() != null && action.isValidContext(actionContext) &&
|
||||||
action.isAddToPopup(actionContext)) {
|
action.isAddToPopup(actionContext)) {
|
||||||
action.setEnabled(action.isEnabledForContext(actionContext));
|
action.setEnabled(action.isEnabledForContext(actionContext));
|
||||||
|
|
|
@ -91,7 +91,4 @@ public interface DockingToolActions {
|
||||||
* @return the actions
|
* @return the actions
|
||||||
*/
|
*/
|
||||||
public Set<DockingActionIf> getAllActions();
|
public Set<DockingActionIf> getAllActions();
|
||||||
|
|
||||||
// TODO
|
|
||||||
public boolean containsAction(DockingActionIf action);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package docking.actions;
|
||||||
|
|
||||||
|
import docking.DockingTool;
|
||||||
|
import docking.action.DockingActionIf;
|
||||||
|
import docking.tool.ToolConstants;
|
||||||
|
import docking.widgets.table.GTable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A place used to hold {@link DockingActionIf}s that are meant to be used by components. Some
|
||||||
|
* components do not have access to the tool that is required to register their actions. This
|
||||||
|
* class helps those components by enabling the installation of shared actions for those
|
||||||
|
* components.
|
||||||
|
*/
|
||||||
|
public class SharedActionRegistry {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Install all known shared actions into the given tool
|
||||||
|
* @param tool the tool
|
||||||
|
* @param toolActions the tool action manager
|
||||||
|
*/
|
||||||
|
public static void installSharedActions(DockingTool tool, ToolActions toolActions) {
|
||||||
|
GTable.createSharedActions(tool, toolActions, ToolConstants.TOOL_OWNER);
|
||||||
|
}
|
||||||
|
}
|
|
@ -89,10 +89,16 @@ public class SharedStubKeyBindingAction extends DockingAction implements Options
|
||||||
@Override
|
@Override
|
||||||
public String getOwnerDescription() {
|
public String getOwnerDescription() {
|
||||||
List<String> owners = getDistinctOwners();
|
List<String> owners = getDistinctOwners();
|
||||||
Collections.sort(owners);
|
|
||||||
if (owners.size() == 1) {
|
if (owners.size() == 1) {
|
||||||
return owners.get(0);
|
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, ", ");
|
return StringUtils.join(owners, ", ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,7 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
|
||||||
this.keyBindingOptions = tool.getOptions(DockingToolConstants.KEY_BINDINGS);
|
this.keyBindingOptions = tool.getOptions(DockingToolConstants.KEY_BINDINGS);
|
||||||
|
|
||||||
createReservedKeyBindings();
|
createReservedKeyBindings();
|
||||||
|
SharedActionRegistry.installSharedActions(tool, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createReservedKeyBindings() {
|
private void createReservedKeyBindings() {
|
||||||
|
@ -160,12 +161,7 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
|
||||||
|
|
||||||
SharedStubKeyBindingAction newStub =
|
SharedStubKeyBindingAction newStub =
|
||||||
new SharedStubKeyBindingAction(name, keyBindingOptions);
|
new SharedStubKeyBindingAction(name, keyBindingOptions);
|
||||||
newStub.addPropertyChangeListener(this);
|
registerStub(newStub, defaultKeyStroke);
|
||||||
keyBindingOptions.registerOption(newStub.getFullName(), OptionType.KEYSTROKE_TYPE,
|
|
||||||
defaultKeyStroke, null, null);
|
|
||||||
|
|
||||||
keyBindingsManager.addAction(provider, newStub);
|
|
||||||
|
|
||||||
return newStub;
|
return newStub;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -177,6 +173,13 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void registerStub(SharedStubKeyBindingAction stub, KeyStroke defaultKeyStroke) {
|
||||||
|
stub.addPropertyChangeListener(this);
|
||||||
|
keyBindingOptions.registerOption(stub.getFullName(), OptionType.KEYSTROKE_TYPE,
|
||||||
|
defaultKeyStroke, null, null);
|
||||||
|
keyBindingsManager.addAction(null, stub);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the given action from the tool
|
* Removes the given action from the tool
|
||||||
* @param action the action to be removed.
|
* @param action the action to be removed.
|
||||||
|
@ -405,11 +408,6 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean containsAction(DockingActionIf action) {
|
|
||||||
return getActionStorage(action).contains(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Action getAction(KeyStroke ks) {
|
public Action getAction(KeyStroke ks) {
|
||||||
return keyBindingsManager.getDockingKeyAction(ks);
|
return keyBindingsManager.getDockingKeyAction(ks);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ import javax.swing.table.*;
|
||||||
import docking.*;
|
import docking.*;
|
||||||
import docking.action.*;
|
import docking.action.*;
|
||||||
import docking.actions.KeyBindingUtils;
|
import docking.actions.KeyBindingUtils;
|
||||||
|
import docking.actions.ToolActions;
|
||||||
import docking.widgets.OptionDialog;
|
import docking.widgets.OptionDialog;
|
||||||
import docking.widgets.dialogs.SettingsDialog;
|
import docking.widgets.dialogs.SettingsDialog;
|
||||||
import docking.widgets.filechooser.GhidraFileChooser;
|
import docking.widgets.filechooser.GhidraFileChooser;
|
||||||
|
@ -71,6 +72,13 @@ import resources.ResourceManager;
|
||||||
*/
|
*/
|
||||||
public class GTable extends JTable implements KeyStrokeConsumer {
|
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 static final String LAST_EXPORT_FILE = "LAST_EXPORT_DIR";
|
||||||
|
|
||||||
private int userDefinedRowHeight;
|
private int userDefinedRowHeight;
|
||||||
|
@ -97,14 +105,6 @@ public class GTable extends JTable implements KeyStrokeConsumer {
|
||||||
/** A flag to signal that a copy operation is being performed. */
|
/** A flag to signal that a copy operation is being performed. */
|
||||||
private boolean copying;
|
private boolean copying;
|
||||||
|
|
||||||
private static final String actionMenuGroup = "zzzTableGroup";
|
|
||||||
private DockingAction copyAction;
|
|
||||||
private DockingAction copyColumnsAction;
|
|
||||||
private DockingAction copyCurrentColumnAction;
|
|
||||||
private DockingAction selectAllAction;
|
|
||||||
private DockingAction exportAction;
|
|
||||||
private DockingAction exportColumnsAction;
|
|
||||||
|
|
||||||
private SelectionManager selectionManager;
|
private SelectionManager selectionManager;
|
||||||
private Integer visibleRowCount;
|
private Integer visibleRowCount;
|
||||||
|
|
||||||
|
@ -116,11 +116,11 @@ public class GTable extends JTable implements KeyStrokeConsumer {
|
||||||
private final Map<Integer, GTableCellRenderingData> columnRenderingDataMap = new HashMap<>();
|
private final Map<Integer, GTableCellRenderingData> columnRenderingDataMap = new HashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new GTable.
|
* Constructs a new GTable
|
||||||
*/
|
*/
|
||||||
public GTable() {
|
public GTable() {
|
||||||
super();
|
super();
|
||||||
init(false);
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -128,44 +128,8 @@ public class GTable extends JTable implements KeyStrokeConsumer {
|
||||||
* @param dm the table model
|
* @param dm the table model
|
||||||
*/
|
*/
|
||||||
public GTable(TableModel dm) {
|
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);
|
super(dm);
|
||||||
init(allowAutoEdit);
|
init();
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setVisibleRowCount(int visibleRowCount) {
|
public void setVisibleRowCount(int visibleRowCount) {
|
||||||
|
@ -510,13 +474,17 @@ public class GTable extends JTable implements KeyStrokeConsumer {
|
||||||
return autoLookupKeyStrokeConsumer.isKeyConsumed(keyStroke);
|
return autoLookupKeyStrokeConsumer.isKeyConsumed(keyStroke);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void init(boolean allowAutoEdit) {
|
/**
|
||||||
ToolTipManager.sharedInstance().unregisterComponent(this);
|
* Enables or disables auto-edit. When enabled, the user can start typing to trigger an
|
||||||
ToolTipManager.sharedInstance().registerComponent(this);
|
* edit of an editable table cell.
|
||||||
setTableHeader(new GTableHeader(this));
|
*
|
||||||
if (!allowAutoEdit) {
|
* @param allowAutoEdit true for auto-editing
|
||||||
putClientProperty("JTable.autoStartsEdit", Boolean.FALSE);
|
*/
|
||||||
|
public void setAutoEditEnabled(boolean allowAutoEdit) {
|
||||||
|
putClientProperty("JTable.autoStartsEdit", allowAutoEdit);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void installEditKeyBinding() {
|
||||||
AbstractAction action = new AbstractAction("StartEdit") {
|
AbstractAction action = new AbstractAction("StartEdit") {
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent ev) {
|
public void actionPerformed(ActionEvent ev) {
|
||||||
|
@ -535,6 +503,14 @@ public class GTable extends JTable implements KeyStrokeConsumer {
|
||||||
KeyBindingUtils.registerAction(this, ks, action, JComponent.WHEN_FOCUSED);
|
KeyBindingUtils.registerAction(this, ks, action, JComponent.WHEN_FOCUSED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
ToolTipManager.sharedInstance().unregisterComponent(this);
|
||||||
|
ToolTipManager.sharedInstance().registerComponent(this);
|
||||||
|
setTableHeader(new GTableHeader(this));
|
||||||
|
|
||||||
|
setAutoEditEnabled(false); // clients can turn this on as needed
|
||||||
|
installEditKeyBinding();
|
||||||
|
|
||||||
initDefaultRenderers();
|
initDefaultRenderers();
|
||||||
|
|
||||||
disableGridLines();
|
disableGridLines();
|
||||||
|
@ -558,27 +534,26 @@ public class GTable extends JTable implements KeyStrokeConsumer {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
removeActionKeyStrokes();
|
||||||
|
|
||||||
// updating the row height requires the 'isInitialized' to be set, so do it first
|
// updating the row height requires the 'isInitialized' to be set, so do it first
|
||||||
isInitialized = true;
|
isInitialized = true;
|
||||||
initializeRowHeight();
|
initializeRowHeight();
|
||||||
|
|
||||||
DockingWindowManager.registerComponentLoadedListener(this, (dwm, provider) -> {
|
|
||||||
DockingTool tool = dwm.getTool();
|
|
||||||
regiserActions(tool, provider);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void regiserActions(DockingTool tool, ComponentProvider provider) {
|
private void removeActionKeyStrokes() {
|
||||||
|
//
|
||||||
tool.setMenuGroup(new String[] { "Copy" }, actionMenuGroup, "1");
|
// We remove these keybindings as we have replaced Java's version with our own. To be
|
||||||
tool.setMenuGroup(new String[] { "Export" }, actionMenuGroup, "2");
|
// thorough, we should really clear all table keybindings, which would ensure that any
|
||||||
tool.setMenuGroup(new String[] { "Select All" }, actionMenuGroup, "3");
|
// 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.
|
||||||
String owner = getClass().getSimpleName();
|
// Also, there are many other built-in keybindings for table navigation, which we do not
|
||||||
if (provider != null) {
|
// wish to override. For now, just clear these. We can clear others if they become
|
||||||
owner = provider.getOwner();
|
// a problem.
|
||||||
}
|
//
|
||||||
installTableActions(tool, owner);
|
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) {
|
private void initializeHeader(JTableHeader header) {
|
||||||
|
@ -1155,6 +1130,47 @@ public class GTable extends JTable implements KeyStrokeConsumer {
|
||||||
return converted;
|
return converted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maintain a {@link docking.widgets.table.GTableCellRenderingData} object
|
||||||
|
* associated with each column that maintains some state and references to
|
||||||
|
* useful data. These objects are created as needed, stored by the table for
|
||||||
|
* convenient re-use and to prevent per-cell creation, and cleared when columns
|
||||||
|
* are removed from the table.
|
||||||
|
* <p>
|
||||||
|
* Row and cell state is cleared before returning to the caller to ensure
|
||||||
|
* consistent state; when the client is done rendering a cell, row and cell
|
||||||
|
* state should also be cleared to minimize references.
|
||||||
|
*
|
||||||
|
* @param viewColumn
|
||||||
|
* The columns' view index
|
||||||
|
* @return Data specific to the column. Row state is cleared before returning.
|
||||||
|
*/
|
||||||
|
GTableCellRenderingData getRenderingData(int viewColumn) {
|
||||||
|
|
||||||
|
int modelColumn = convertColumnIndexToModel(viewColumn);
|
||||||
|
|
||||||
|
GTableCellRenderingData renderData = columnRenderingDataMap.get(modelColumn);
|
||||||
|
|
||||||
|
if (renderData == null) {
|
||||||
|
Settings settings = SettingsImpl.NO_SETTINGS;
|
||||||
|
ConfigurableColumnTableModel configurableModel = getConfigurableColumnTableModel();
|
||||||
|
if (configurableModel != null) {
|
||||||
|
settings = configurableModel.getColumnSettings(modelColumn);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderData = new GTableCellRenderingData(this, viewColumn, settings);
|
||||||
|
columnRenderingDataMap.put(modelColumn, renderData);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderData.resetRowData();
|
||||||
|
return renderData;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Actions
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A method that subclasses can override to signal that they wish not to have this table's
|
* 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.
|
* built-in popup actions. Subclasses will almost never need to override this method.
|
||||||
|
@ -1165,198 +1181,6 @@ public class GTable extends JTable implements KeyStrokeConsumer {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void installTableActions(DockingTool tool, String owner) {
|
|
||||||
|
|
||||||
int subGroupIndex = 1; // order by insertion
|
|
||||||
copyAction = new GTableAction("Table Data Copy", owner) {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionContext context) {
|
|
||||||
copying = true;
|
|
||||||
Action builtinCopyAction = TransferHandler.getCopyAction();
|
|
||||||
|
|
||||||
try {
|
|
||||||
builtinCopyAction.actionPerformed(new ActionEvent(GTable.this, 0, "copy"));
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
copying = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//@formatter:off
|
|
||||||
copyAction.setPopupMenuData(new MenuData(
|
|
||||||
new String[] { "Copy", "Copy" },
|
|
||||||
ResourceManager.loadImage("images/page_white_copy.png"),
|
|
||||||
actionMenuGroup, NO_MNEMONIC,
|
|
||||||
Integer.toString(subGroupIndex++)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
copyAction.setKeyBindingData(new KeyBindingData(
|
|
||||||
KeyStroke.getKeyStroke(KeyEvent.VK_C,
|
|
||||||
CONTROL_KEY_MODIFIER_MASK)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
copyAction.setHelpLocation(new HelpLocation("Tables", "Copy"));
|
|
||||||
//@formatter:on
|
|
||||||
|
|
||||||
copyCurrentColumnAction = new GTableAction("Table Data Copy Current Column", owner) {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionContext context) {
|
|
||||||
|
|
||||||
int column = getSelectedColumn();
|
|
||||||
MouseEvent event = context.getMouseEvent();
|
|
||||||
if (event != null) {
|
|
||||||
column = columnAtPoint(event.getPoint());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (column < 0) {
|
|
||||||
Msg.debug(this, "Copy failed--no column selected");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
copyColumns(column);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//@formatter:off
|
|
||||||
copyCurrentColumnAction.setPopupMenuData(new MenuData(
|
|
||||||
new String[] { "Copy",
|
|
||||||
"Copy Current Column" },
|
|
||||||
ResourceManager.loadImage("images/page_white_copy.png"),
|
|
||||||
actionMenuGroup,
|
|
||||||
NO_MNEMONIC,
|
|
||||||
Integer.toString(subGroupIndex++)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
copyCurrentColumnAction.setKeyBindingData(new KeyBindingData(
|
|
||||||
KeyStroke.getKeyStroke(
|
|
||||||
KeyEvent.VK_C, CONTROL_KEY_MODIFIER_MASK | SHIFT_DOWN_MASK)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
copyCurrentColumnAction.setHelpLocation(new HelpLocation("Tables", "Copy_Current_Column"));
|
|
||||||
//@formatter:on
|
|
||||||
|
|
||||||
copyColumnsAction = new GTableAction("Table Data Copy by Columns", owner) {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionContext context) {
|
|
||||||
int[] userColumns = promptUserForColumns();
|
|
||||||
if (userColumns == null) {
|
|
||||||
return; // cancelled
|
|
||||||
}
|
|
||||||
|
|
||||||
copyColumns(userColumns);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//@formatter:off
|
|
||||||
copyColumnsAction.setPopupMenuData(new MenuData(
|
|
||||||
new String[] { "Copy", "Copy Columns..." },
|
|
||||||
ResourceManager.loadImage("images/page_white_copy.png"),
|
|
||||||
actionMenuGroup,
|
|
||||||
NO_MNEMONIC,
|
|
||||||
Integer.toString(subGroupIndex++)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
copyColumnsAction.setHelpLocation(new HelpLocation("Tables", "Copy_Columns"));
|
|
||||||
//@formatter:on
|
|
||||||
|
|
||||||
exportAction = new GTableAction("Table Data CSV Export", owner) {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionContext context) {
|
|
||||||
File file = chooseExportFile();
|
|
||||||
if (file != null) {
|
|
||||||
GTableToCSV.writeCSV(file, GTable.this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//@formatter:off
|
|
||||||
exportAction.setPopupMenuData(new MenuData(
|
|
||||||
new String[] { "Export", GTableToCSV.TITLE + "..." },
|
|
||||||
ResourceManager.loadImage("images/application-vnd.oasis.opendocument.spreadsheet-template.png"),
|
|
||||||
actionMenuGroup,
|
|
||||||
NO_MNEMONIC,
|
|
||||||
Integer.toString(subGroupIndex++)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
exportAction.setHelpLocation(new HelpLocation("Tables", "ExportCSV"));
|
|
||||||
//@formatter:on
|
|
||||||
|
|
||||||
exportColumnsAction = new GTableAction("Table Data CSV Export (by Columns)", owner) {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionContext context) {
|
|
||||||
int[] userColumns = promptUserForColumns();
|
|
||||||
if (userColumns == null) {
|
|
||||||
return; // cancelled
|
|
||||||
}
|
|
||||||
|
|
||||||
File file = chooseExportFile();
|
|
||||||
if (file == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Integer> columnList = new ArrayList<>();
|
|
||||||
for (int userColumn : userColumns) {
|
|
||||||
columnList.add(userColumn);
|
|
||||||
}
|
|
||||||
GTableToCSV.writeCSVUsingColunns(file, GTable.this, columnList);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//@formatter:off
|
|
||||||
exportColumnsAction.setPopupMenuData(new MenuData(
|
|
||||||
new String[] { "Export", "Export Columns to CSV..." },
|
|
||||||
ResourceManager.loadImage("images/application-vnd.oasis.opendocument.spreadsheet-template.png"),
|
|
||||||
actionMenuGroup,
|
|
||||||
NO_MNEMONIC,
|
|
||||||
Integer.toString(subGroupIndex++)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
exportColumnsAction.setHelpLocation(new HelpLocation("Tables", "ExportCSV_Columns"));
|
|
||||||
//@formatter:on
|
|
||||||
|
|
||||||
selectAllAction = new GTableAction("Table Select All", owner) {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionContext context) {
|
|
||||||
selectAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabledForContext(ActionContext context) {
|
|
||||||
if (!super.isEnabledForContext(context)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return getSelectionModel().getSelectionMode() != ListSelectionModel.SINGLE_SELECTION;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//@formatter:off
|
|
||||||
selectAllAction.setPopupMenuData(new MenuData(
|
|
||||||
new String[] { "Select All" },
|
|
||||||
null /*icon*/,
|
|
||||||
actionMenuGroup,
|
|
||||||
NO_MNEMONIC,
|
|
||||||
Integer.toString(subGroupIndex++)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
selectAllAction.setKeyBindingData(new KeyBindingData(
|
|
||||||
KeyStroke.getKeyStroke(KeyEvent.VK_A,
|
|
||||||
CONTROL_KEY_MODIFIER_MASK)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
selectAllAction.setHelpLocation(new HelpLocation("Tables", "SelectAll"));
|
|
||||||
//@formatter:on
|
|
||||||
|
|
||||||
// remove any conflicting key bindings that Java has installed on this component
|
|
||||||
KeyBindingUtils.clearKeyBinding(this, copyAction);
|
|
||||||
KeyBindingUtils.clearKeyBinding(this, copyCurrentColumnAction);
|
|
||||||
KeyBindingUtils.clearKeyBinding(this, copyColumnsAction);
|
|
||||||
KeyBindingUtils.clearKeyBinding(this, selectAllAction);
|
|
||||||
KeyBindingUtils.clearKeyBinding(this, exportAction);
|
|
||||||
KeyBindingUtils.clearKeyBinding(this, exportColumnsAction);
|
|
||||||
|
|
||||||
tool.addAction(copyAction);
|
|
||||||
tool.addAction(copyCurrentColumnAction);
|
|
||||||
tool.addAction(copyColumnsAction);
|
|
||||||
tool.addAction(selectAllAction);
|
|
||||||
tool.addAction(exportAction);
|
|
||||||
tool.addAction(exportColumnsAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void copyColumns(int... copyColumns) {
|
private void copyColumns(int... copyColumns) {
|
||||||
|
|
||||||
int[] originalColumns = new int[0];
|
int[] originalColumns = new int[0];
|
||||||
|
@ -1433,43 +1257,216 @@ public class GTable extends JTable implements KeyStrokeConsumer {
|
||||||
Preferences.store();
|
Preferences.store();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void doCopy() {
|
||||||
* Maintain a {@link docking.widgets.table.GTableCellRenderingData} object
|
copying = true;
|
||||||
* associated with each column that maintains some state and references to
|
Action builtinCopyAction = TransferHandler.getCopyAction();
|
||||||
* useful data. These objects are created as needed, stored by the table for
|
|
||||||
* convenient re-use and to prevent per-cell creation, and cleared when columns
|
|
||||||
* are removed from the table.
|
|
||||||
* <p>
|
|
||||||
* Row and cell state is cleared before returning to the caller to ensure
|
|
||||||
* consistent state; when the client is done rendering a cell, row and cell
|
|
||||||
* state should also be cleared to minimize references.
|
|
||||||
*
|
|
||||||
* @param viewColumn
|
|
||||||
* The columns' view index
|
|
||||||
* @return Data specific to the column. Row state is cleared before returning.
|
|
||||||
*/
|
|
||||||
GTableCellRenderingData getRenderingData(int viewColumn) {
|
|
||||||
|
|
||||||
int modelColumn = convertColumnIndexToModel(viewColumn);
|
try {
|
||||||
|
builtinCopyAction.actionPerformed(new ActionEvent(GTable.this, 0, "copy"));
|
||||||
GTableCellRenderingData renderData = columnRenderingDataMap.get(modelColumn);
|
}
|
||||||
|
finally {
|
||||||
if (renderData == null) {
|
copying = false;
|
||||||
Settings settings = SettingsImpl.NO_SETTINGS;
|
}
|
||||||
ConfigurableColumnTableModel configurableModel = getConfigurableColumnTableModel();
|
|
||||||
if (configurableModel != null) {
|
|
||||||
settings = configurableModel.getColumnSettings(modelColumn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderData = new GTableCellRenderingData(this, viewColumn, settings);
|
private void doCopyCurrentColumn(MouseEvent event) {
|
||||||
columnRenderingDataMap.put(modelColumn, renderData);
|
int column = getSelectedColumn();
|
||||||
|
if (event != null) {
|
||||||
|
column = columnAtPoint(event.getPoint());
|
||||||
}
|
}
|
||||||
|
|
||||||
renderData.resetRowData();
|
if (column < 0) {
|
||||||
return renderData;
|
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 {
|
private class MyTableColumnModelListener implements TableColumnModelListener {
|
||||||
@Override
|
@Override
|
||||||
public void columnSelectionChanged(ListSelectionEvent e) {
|
public void columnSelectionChanged(ListSelectionEvent e) {
|
||||||
|
@ -1499,17 +1496,25 @@ public class GTable extends JTable implements KeyStrokeConsumer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private abstract class GTableAction extends DockingAction {
|
private abstract static class GTableAction extends DockingAction {
|
||||||
|
|
||||||
GTableAction(String name, String owner) {
|
GTableAction(String name, String owner) {
|
||||||
super(name, owner, KeyBindingType.SHARED);
|
super(name, owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAddToPopup(ActionContext context) {
|
||||||
|
if (!isEnabledForContext(context)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
GTable gTable = (GTable) context.getSourceComponent();
|
||||||
|
return gTable.supportsPopupActions();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabledForContext(ActionContext context) {
|
public boolean isEnabledForContext(ActionContext context) {
|
||||||
Component sourceComponent = context.getSourceComponent();
|
Component sourceComponent = context.getSourceComponent();
|
||||||
return sourceComponent == GTable.this;
|
return sourceComponent instanceof GTable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1571,9 +1571,7 @@ public class GhidraFileChooserTest extends AbstractDockingTest {
|
||||||
private ActionContext createDirListContext() {
|
private ActionContext createDirListContext() {
|
||||||
|
|
||||||
DirectoryList dirlist = getDirectoryListViewOfFileChooser();
|
DirectoryList dirlist = getDirectoryListViewOfFileChooser();
|
||||||
MouseEvent e = new MouseEvent(dirlist, 0, 0, 0, 0, 0, 1, false);
|
return new ActionContext(null, dirlist);
|
||||||
ActionContext context = chooser.getActionContext(e);
|
|
||||||
return context;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isEnabled(DockingAction action, ActionContext context) {
|
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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -288,7 +288,7 @@ public class ProjectDataTablePanel extends JPanel {
|
||||||
list.add(info.getDomainFile());
|
list.add(info.getDomainFile());
|
||||||
}
|
}
|
||||||
return new ProjectDataActionContext(provider, projectData,
|
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) {
|
private void checkOpen(MouseEvent e) {
|
||||||
|
|
|
@ -426,7 +426,7 @@ public class VersionHistoryPanel extends JPanel implements Draggable {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.getContextObject() != table) {
|
if (context.getSourceComponent() != table) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,10 +67,10 @@ public class KeyBindingsPanel extends JPanel {
|
||||||
private Options options;
|
private Options options;
|
||||||
|
|
||||||
private Map<String, List<DockingActionIf>> actionsByFullName;
|
private Map<String, List<DockingActionIf>> actionsByFullName;
|
||||||
private Map<String, List<String>> actionNamesByKeyStroke;
|
private Map<String, List<String>> actionNamesByKeyStroke = new HashMap<>();
|
||||||
private Map<String, KeyStroke> keyStrokesByFullName;
|
private Map<String, KeyStroke> keyStrokesByFullName = new HashMap<>();
|
||||||
private Map<String, KeyStroke> originalValues; // to know what has been changed
|
private Map<String, KeyStroke> originalValues = new HashMap<>(); // to know what has been changed
|
||||||
private List<DockingActionIf> tableActions;
|
private List<DockingActionIf> tableActions = new ArrayList<>();
|
||||||
|
|
||||||
private KeyEntryTextField ksField;
|
private KeyEntryTextField ksField;
|
||||||
private boolean unappliedChanges;
|
private boolean unappliedChanges;
|
||||||
|
@ -83,8 +83,8 @@ public class KeyBindingsPanel extends JPanel {
|
||||||
public KeyBindingsPanel(PluginTool tool, Options options) {
|
public KeyBindingsPanel(PluginTool tool, Options options) {
|
||||||
this.tool = tool;
|
this.tool = tool;
|
||||||
this.options = options;
|
this.options = options;
|
||||||
tableActions = new ArrayList<>();
|
|
||||||
create();
|
createPanelComponents();
|
||||||
createActionMap();
|
createActionMap();
|
||||||
addListeners();
|
addListeners();
|
||||||
}
|
}
|
||||||
|
@ -97,11 +97,9 @@ public class KeyBindingsPanel extends JPanel {
|
||||||
tableFilterPanel.dispose();
|
tableFilterPanel.dispose();
|
||||||
tableModel.dispose();
|
tableModel.dispose();
|
||||||
actionTable.dispose();
|
actionTable.dispose();
|
||||||
|
propertyChangeListener = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Apply the changes to the actions.
|
|
||||||
*/
|
|
||||||
public void apply() {
|
public void apply() {
|
||||||
Iterator<String> iter = keyStrokesByFullName.keySet().iterator();
|
Iterator<String> iter = keyStrokesByFullName.keySet().iterator();
|
||||||
while (iter.hasNext()) {
|
while (iter.hasNext()) {
|
||||||
|
@ -160,9 +158,7 @@ public class KeyBindingsPanel extends JPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createActionMap() {
|
private void createActionMap() {
|
||||||
keyStrokesByFullName = new HashMap<>();
|
|
||||||
actionNamesByKeyStroke = new HashMap<>();
|
|
||||||
originalValues = new HashMap<>();
|
|
||||||
String longestName = "";
|
String longestName = "";
|
||||||
|
|
||||||
actionsByFullName = KeyBindingUtils.getAllActionsByFullName(tool);
|
actionsByFullName = KeyBindingUtils.getAllActionsByFullName(tool);
|
||||||
|
@ -198,10 +194,7 @@ public class KeyBindingsPanel extends JPanel {
|
||||||
tableModel.fireTableDataChanged();
|
tableModel.fireTableDataChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void createPanelComponents() {
|
||||||
* Create the components in this panel.
|
|
||||||
*/
|
|
||||||
private void create() {
|
|
||||||
setLayout(new BorderLayout(10, 10));
|
setLayout(new BorderLayout(10, 10));
|
||||||
|
|
||||||
tableModel = new KeyBindingsTableModel();
|
tableModel = new KeyBindingsTableModel();
|
||||||
|
@ -547,6 +540,7 @@ public class KeyBindingsPanel extends JPanel {
|
||||||
// add each new key stroke mapping
|
// add each new key stroke mapping
|
||||||
Iterator<String> iterator = keyBindingsMap.keySet().iterator();
|
Iterator<String> iterator = keyBindingsMap.keySet().iterator();
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
|
|
||||||
String name = iterator.next();
|
String name = iterator.next();
|
||||||
KeyStroke keyStroke = keyBindingsMap.get(name);
|
KeyStroke keyStroke = keyBindingsMap.get(name);
|
||||||
keyStroke = KeyBindingUtils.validateKeyStroke(keyStroke);
|
keyStroke = KeyBindingUtils.validateKeyStroke(keyStroke);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue