GT-2763 - Table Sorting - updated help; removed old

DefaultSortedTableModel
This commit is contained in:
dragonmacher 2019-05-03 16:54:36 -04:00
parent 4a8144c288
commit da5f009c71
18 changed files with 410 additions and 908 deletions

View File

@ -128,13 +128,11 @@ src/main/help/help/topics/CallTreePlugin/images/CallTreeWindow.png||GHIDRA||||EN
src/main/help/help/topics/CallTreePlugin/images/arrow_rotate_clockwise.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
src/main/help/help/topics/CallTreePlugin/images/collapse_all.png||GHIDRA||||END|
src/main/help/help/topics/CallTreePlugin/images/depth-input.png||GHIDRA||reviewed||END|
src/main/help/help/topics/CallTreePlugin/images/expand_all.png||GHIDRA||||END|
src/main/help/help/topics/CallTreePlugin/images/go-home.png||Tango Icons - Public Domain|||tango icon set|END|
src/main/help/help/topics/CallTreePlugin/images/locationIn.gif||GHIDRA||||END|
src/main/help/help/topics/CallTreePlugin/images/locationOut.gif||GHIDRA||||END|
src/main/help/help/topics/CallTreePlugin/images/package.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
src/main/help/help/topics/CallTreePlugin/images/stopNode.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
src/main/help/help/topics/CallTreePlugin/images/view-filter.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END|
src/main/help/help/topics/ClearPlugin/Clear.htm||GHIDRA||||END|
src/main/help/help/topics/ClearPlugin/images/ClearFlow.png||GHIDRA||||END|
src/main/help/help/topics/ClearPlugin/images/ClearWithOptions.png||GHIDRA||||END|
@ -153,7 +151,6 @@ src/main/help/help/topics/CodeBrowserPlugin/images/ClosedStructure.png||GHIDRA||
src/main/help/help/topics/CodeBrowserPlugin/images/CodeBrowser.png||GHIDRA||||END|
src/main/help/help/topics/CodeBrowserPlugin/images/CodeBrowserColors.png||GHIDRA||||END|
src/main/help/help/topics/CodeBrowserPlugin/images/CodeBrowserReferencePopup.png||GHIDRA||||END|
src/main/help/help/topics/CodeBrowserPlugin/images/CodeBrowserTruncatedTextPopup.png||GHIDRA||||END|
src/main/help/help/topics/CodeBrowserPlugin/images/CodeBrowserWithFlowArrows.png||GHIDRA||||END|
src/main/help/help/topics/CodeBrowserPlugin/images/CodeBrowserWithMarkers.png||GHIDRA||||END|
src/main/help/help/topics/CodeBrowserPlugin/images/CodeBrowser_OperandHighlight.png||GHIDRA||||END|
@ -214,7 +211,6 @@ src/main/help/help/topics/DataTypeEditors/images/disk.png||FAMFAMFAM Icons - CC
src/main/help/help/topics/DataTypeEditors/images/down.png||GHIDRA||||END|
src/main/help/help/topics/DataTypeEditors/images/edit-delete.png||Oxygen Icons - LGPL 3.0||||END|
src/main/help/help/topics/DataTypeEditors/images/erase16.png||GHIDRA||||END|
src/main/help/help/topics/DataTypeEditors/images/pencil16.png||GHIDRA||reviewed||END|
src/main/help/help/topics/DataTypeEditors/images/up.png||GHIDRA||||END|
src/main/help/help/topics/DataTypeManagerPlugin/data_type_manager_archives.html||GHIDRA||||END|
src/main/help/help/topics/DataTypeManagerPlugin/data_type_manager_description.htm||GHIDRA||||END|
@ -224,7 +220,6 @@ src/main/help/help/topics/DataTypeManagerPlugin/images/BookShelfOpen.png||GHIDRA
src/main/help/help/topics/DataTypeManagerPlugin/images/CommitDialog.png||GHIDRA||||END|
src/main/help/help/topics/DataTypeManagerPlugin/images/DataTypeConflict.png||GHIDRA||||END|
src/main/help/help/topics/DataTypeManagerPlugin/images/DataTypeManager.png||GHIDRA||||END|
src/main/help/help/topics/DataTypeManagerPlugin/images/DataTypeTreeWithAssociations.png||GHIDRA||||END|
src/main/help/help/topics/DataTypeManagerPlugin/images/DisassociateDialog.png||GHIDRA||||END|
src/main/help/help/topics/DataTypeManagerPlugin/images/EditPaths.png||GHIDRA||||END|
src/main/help/help/topics/DataTypeManagerPlugin/images/FavoriteDts.png||GHIDRA||||END|
@ -270,7 +265,6 @@ src/main/help/help/topics/DataWindowPlugin/data_window.htm||GHIDRA||||END|
src/main/help/help/topics/DataWindowPlugin/images/DataWindow.png||GHIDRA||||END|
src/main/help/help/topics/DataWindowPlugin/images/DataWindowFilter.png||GHIDRA||||END|
src/main/help/help/topics/DataWindowPlugin/images/text_align_justify.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
src/main/help/help/topics/DataWindowPlugin/images/view-filter.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END|
src/main/help/help/topics/DbViewerPlugin/DbViewer.htm||GHIDRA||reviewed||END|
src/main/help/help/topics/DbViewerPlugin/images/DatabaseViewer.png||GHIDRA||||END|
src/main/help/help/topics/DisassembledViewPlugin/DisassembledViewPlugin.htm||GHIDRA||||END|
@ -292,7 +286,6 @@ src/main/help/help/topics/EclipseIntegration/EclipseIntegration.htm||GHIDRA||||E
src/main/help/help/topics/EquatePlugin/Equates.htm||GHIDRA||||END|
src/main/help/help/topics/EquatePlugin/images/AfterApplyEnum.png||GHIDRA||||END|
src/main/help/help/topics/EquatePlugin/images/ApplyEnum.png||GHIDRA||||END|
src/main/help/help/topics/EquatePlugin/images/ApplyEnumPopup.png||GHIDRA||||END|
src/main/help/help/topics/EquatePlugin/images/BeforeApplyEnum.png||GHIDRA||||END|
src/main/help/help/topics/EquatePlugin/images/ConfirmEquateDelete.png||GHIDRA||||END|
src/main/help/help/topics/EquatePlugin/images/EquatesTable.png||GHIDRA||||END|
@ -302,7 +295,6 @@ src/main/help/help/topics/EquatePlugin/images/SetEquate.png||GHIDRA||||END|
src/main/help/help/topics/ExporterPlugin/exporter.htm||GHIDRA||||END|
src/main/help/help/topics/ExporterPlugin/images/Ascii_Options.png||GHIDRA||||END|
src/main/help/help/topics/ExporterPlugin/images/C_Options.png||GHIDRA||||END|
src/main/help/help/topics/ExporterPlugin/images/Export_Data_Panel.png||GHIDRA||reviewed||END|
src/main/help/help/topics/ExporterPlugin/images/Export_Dialog.png||GHIDRA||||END|
src/main/help/help/topics/ExporterPlugin/images/Intel_Hex_Options.png||GHIDRA||||END|
src/main/help/help/topics/FallThroughPlugin/Override_Fallthrough.htm||GHIDRA||||END|
@ -339,7 +331,6 @@ src/main/help/help/topics/FrontEndPlugin/images/ConnectTools.png||GHIDRA||||END|
src/main/help/help/topics/FrontEndPlugin/images/DeleteProject.png||GHIDRA||||END|
src/main/help/help/topics/FrontEndPlugin/images/EditPluginPath.png||GHIDRA||||END|
src/main/help/help/topics/FrontEndPlugin/images/EditProjectAccessList.png||GHIDRA||||END|
src/main/help/help/topics/FrontEndPlugin/images/EditProjectAccessPanel.png||GHIDRA||||END|
src/main/help/help/topics/FrontEndPlugin/images/MemoryUsage.png||GHIDRA||||END|
src/main/help/help/topics/FrontEndPlugin/images/NonSharedProjectInfo.png||GHIDRA||||END|
src/main/help/help/topics/FrontEndPlugin/images/OpenProject.png||GHIDRA||||END|
@ -366,7 +357,6 @@ src/main/help/help/topics/FrontEndPlugin/images/VersionedFileCOnoServer.png||GHI
src/main/help/help/topics/FrontEndPlugin/images/VersionedFileCOwithServer.png||GHIDRA||||END|
src/main/help/help/topics/FrontEndPlugin/images/VersionedFileIcon.png||GHIDRA||||END|
src/main/help/help/topics/FrontEndPlugin/images/ViewOtherProjects.png||GHIDRA||||END|
src/main/help/help/topics/FrontEndPlugin/images/ViewProjectAccessPanel.png||GHIDRA||||END|
src/main/help/help/topics/FrontEndPlugin/images/closedBookBlue.png||GHIDRA||reviewed||END|
src/main/help/help/topics/FrontEndPlugin/images/connected.gif||GHIDRA||||END|
src/main/help/help/topics/FrontEndPlugin/images/disconnected.gif||GHIDRA||||END|
@ -378,7 +368,6 @@ src/main/help/help/topics/FrontEndPlugin/images/up.png||GHIDRA||||END|
src/main/help/help/topics/FrontEndPlugin/images/user.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
src/main/help/help/topics/FunctionComparison/FunctionComparison.htm||GHIDRA||||END|
src/main/help/help/topics/FunctionComparison/images/FunctionComparisonWindow.png||GHIDRA||||END|
src/main/help/help/topics/FunctionComparison/images/FunctionComparisonWindowFromMap.png||GHIDRA||||END|
src/main/help/help/topics/FunctionComparison/images/FunctionScope.gif||GHIDRA||||END|
src/main/help/help/topics/FunctionComparison/images/ListingCodeComparisonOptions.png||GHIDRA||||END|
src/main/help/help/topics/FunctionComparison/images/MultiFunctionComparisonWindow.png||GHIDRA||||END|
@ -410,7 +399,6 @@ src/main/help/help/topics/FunctionTagPlugin/images/DeleteWarning.png||GHIDRA||||
src/main/help/help/topics/FunctionTagPlugin/images/EditNotAllowedWarning.png||GHIDRA||||END|
src/main/help/help/topics/FunctionTagPlugin/images/EditTag.png||GHIDRA||||END|
src/main/help/help/topics/FunctionTagPlugin/images/FilterField.png||GHIDRA||||END|
src/main/help/help/topics/FunctionTagPlugin/images/FullWindow.png||GHIDRA||||END|
src/main/help/help/topics/FunctionTagPlugin/images/FunctionTagPlugin.png||GHIDRA||||END|
src/main/help/help/topics/FunctionTagPlugin/images/InputField.png||GHIDRA||||END|
src/main/help/help/topics/FunctionWindowPlugin/function_window.htm||GHIDRA||||END|
@ -534,10 +522,6 @@ src/main/help/help/topics/OverviewPlugin/images/EntropyLegend.png||GHIDRA||||END
src/main/help/help/topics/OverviewPlugin/images/EntropyOptions.png||GHIDRA||||END|
src/main/help/help/topics/OverviewPlugin/images/Equation.png||GHIDRA||||END|
src/main/help/help/topics/OverviewPlugin/images/OverviewPanel.png||GHIDRA||||END|
src/main/help/help/topics/OverviewPlugin/images/Program.gif||GHIDRA||||END|
src/main/help/help/topics/OverviewPlugin/images/View.gif||GHIDRA||||END|
src/main/help/help/topics/OverviewPlugin/images/zoom_in.png||FAMFAMFAM Icons - CC 2.5|||silk|END|
src/main/help/help/topics/OverviewPlugin/images/zoom_out.png||FAMFAMFAM Icons - CC 2.5|||silk|END|
src/main/help/help/topics/PDB/PDB.htm||GHIDRA||||END|
src/main/help/help/topics/PDB/download_pdb_file.html||GHIDRA||||END|
src/main/help/help/topics/PDB/images/KnownSymbolServerURLsDialog.png||GHIDRA||||END|
@ -689,11 +673,9 @@ src/main/help/help/topics/Search/images/DOSA_D.png||GHIDRA||||END|
src/main/help/help/topics/Search/images/DOSA_O.png||GHIDRA||||END|
src/main/help/help/topics/Search/images/DOSA_S.png||GHIDRA||||END|
src/main/help/help/topics/Search/images/DirectReferences.png||GHIDRA||||END|
src/main/help/help/topics/Search/images/DirectRefsOnSelection.png||GHIDRA||||END|
src/main/help/help/topics/Search/images/MultipleSelectionError.png||GHIDRA||||END|
src/main/help/help/topics/Search/images/QueryResultsSearch.png||GHIDRA||||END|
src/main/help/help/topics/Search/images/SearchForAddressTables.png||GHIDRA||||END|
src/main/help/help/topics/Search/images/SearchInThstructionPatternsResultsTable.png||GHIDRA||||END|
src/main/help/help/topics/Search/images/SearchInstructionPatterns.png||GHIDRA||||END|
src/main/help/help/topics/Search/images/SearchInstructionPatternsControlPanel.png||GHIDRA||||END|
src/main/help/help/topics/Search/images/SearchInstructionPatternsInstructionTable.png||GHIDRA||||END|
@ -716,7 +698,6 @@ src/main/help/help/topics/Search/images/SearchText.png||GHIDRA||||END|
src/main/help/help/topics/Search/images/StringSearchDialog.png||GHIDRA||||END|
src/main/help/help/topics/Search/images/StringSearchResults.png||GHIDRA||||END|
src/main/help/help/topics/Search/images/binaryData.gif||GHIDRA||||END|
src/main/help/help/topics/Search/images/browser.png||Nuvola Icons - LGPL 2.1|||Nuvola icon set|END|
src/main/help/help/topics/Search/images/collapse.gif||GHIDRA||||END|
src/main/help/help/topics/Search/images/dialog-warning.png||Oxygen Icons - LGPL 3.0||||END|
src/main/help/help/topics/Search/images/dialog-warning_red.png||Tango Icons - Public Domain||||END|
@ -726,7 +707,6 @@ src/main/help/help/topics/Search/images/expand.gif||GHIDRA||||END|
src/main/help/help/topics/Search/images/font.png||FAMFAMFAM Icons - CC 2.5||||END|
src/main/help/help/topics/Search/images/go-home.png||Tango Icons - Public Domain||||END|
src/main/help/help/topics/Search/images/hexData.png||GHIDRA||||END|
src/main/help/help/topics/Search/images/locationOut.gif||GHIDRA||||END|
src/main/help/help/topics/Search/images/magnifier.png||FAMFAMFAM Icons - CC 2.5||||END|
src/main/help/help/topics/Search/images/page_white_copy.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
src/main/help/help/topics/Search/images/reload.png||Nuvola Icons - LGPL 2.1||||END|
@ -734,7 +714,6 @@ src/main/help/help/topics/Search/images/searchm_obj.gif||GHIDRA||||END|
src/main/help/help/topics/Search/images/table_delete.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
src/main/help/help/topics/Search/images/text_align_justify.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
src/main/help/help/topics/Search/images/view-filter.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END|
src/main/help/help/topics/Search/images/viewmag.png||Nuvola Icons - LGPL 2.1|||Nuvola icon set|END|
src/main/help/help/topics/SelectBlockPlugin/Select_Block_Help.html||GHIDRA||||END|
src/main/help/help/topics/SelectBlockPlugin/images/Dialog.png||GHIDRA||||END|
src/main/help/help/topics/SelectBlockPlugin/images/ToBadAddr.png||GHIDRA||||END|
@ -780,7 +759,6 @@ src/main/help/help/topics/SymbolTreePlugin/images/EditExternalLocation.png||GHID
src/main/help/help/topics/SymbolTreePlugin/images/SymbolTree.png||GHIDRA||||END|
src/main/help/help/topics/SymbolTreePlugin/images/openFolderGroup.png||Modified Nuvola Icons - LGPL 2.1||||END|
src/main/help/help/topics/SymbolTreePlugin/images/sitemap_color.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
src/main/help/help/topics/SymbolTreePlugin/locationIn.gif||GHIDRA||||END|
src/main/help/help/topics/Tables/GhidraTableHeaders.html||GHIDRA||||END|
src/main/help/help/topics/Tables/images/BytesSettingsDialog.png||GHIDRA||reviewed||END|
src/main/help/help/topics/Tables/images/MultipleColumnSortDialog.png||GHIDRA||||END|

View File

@ -23,6 +23,8 @@
If you are using a table that does not offer one of the above features, and you would like
us to add that feature, then please contact the Ghidra team to request additional support.
</p>
<blockquote>
<p>
<img src="../../shared/note.png" border="0">In addition to table header actions, many
tables support a common set of actions when right-clicking in the body of the table, such
@ -34,6 +36,7 @@
<li><a href="#SelectAll">Select All (also available via <b>Ctrl-A</b></a>.</li>
</ol>
</p>
</blockquote>
</blockquote>
@ -45,10 +48,12 @@
sorted column is denoted by an icon that shows the sort direction.
</p>
<p><img border="0" src="../../shared/note.png" alt="">
<blockquote>
<p><img border="0" src="../../shared/note.yellow.png" alt="">
If you click a table column header and no sorting takes place, then that particular
table does not support sorting on columns.
</p>
</blockquote>
<p>
When you click a column to sort the data, the default sort will be ascending. If you
@ -73,17 +78,27 @@
</p>
<p>
With multiple sorted columns, you may change the direction of any individual
sort column by left-clicking that column. To remove a sort column from a multiple
column sort, Ctrl-left-click that column.
sort column by left-clicking that column.
<blockquote>
<p><img border="0" src="../../shared/tip.png" alt="">
To remove a sort column from a multiple column sort, Ctrl-left-click that column.
This will even work when only one column is sorted, <b><i>thus effectively disabling
sorting for the table</i></b>.
</p>
</blockquote>
</p>
<blockquote>
<p><img border="0" src="../../shared/note.png" alt="">
It is possible to cancel some tables while they are loading or sorting their data.
If this happens, you can trigger a reload of the data by sorting on one of the
columns.
</p>
</blockquote>
</blockquote>
<br>
<br>
<h2><a name="SelectColumns"></a>Choosing Columns</h2>
@ -159,19 +174,17 @@
<blockquote>
<p>
Exports the current table to a comma-separated value (CSV) text file. The CSV file
will export the selected columns in the order displayed.
will export the selected columns in the order displayed. The first row of ouput will
contain the table's column names.
<blockquote>
<p>
<img src="../../shared/note.png" border="0">This action uses the current
<img src="../../shared/note.yellow.png" border="0">This action uses the current
table selection when deciding what to export. If no row is selected, then no data is
exported. To select all rows, use the <b>Select All</b> action or press <b>Ctrl-A</b> on
the keyboard.
</p>
<p>
<img src="../../shared/note.png" border="0">
Note: The first row will contain the column names.
</p>
</p>
</blockquote>
</p>
</blockquote>

View File

@ -16,20 +16,18 @@
package ghidra.app.plugin.core.data;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.*;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellEditor;
import docking.DialogComponentProvider;
import docking.widgets.combobox.GComboBox;
import docking.widgets.dialogs.StringChoices;
import docking.widgets.table.DefaultSortedTableModel;
import docking.widgets.table.AbstractSortedTableModel;
import docking.widgets.table.GTable;
import ghidra.docking.settings.*;
import ghidra.program.model.data.Composite;
@ -67,9 +65,6 @@ public class DataSettingsDialog extends DialogComponentProvider {
private boolean appliedSettings;
private Program program;
/**
* Construct instance settings dialog.
*/
public DataSettingsDialog(Program program, ProgramSelection sel) throws CancelledException {
super("Data Settings", true, false, true, false);
this.program = program;
@ -83,9 +78,6 @@ public class DataSettingsDialog extends DialogComponentProvider {
buildPanel();
}
/**
* Construct instance settings dialog.
*/
public DataSettingsDialog(Program program, Data data) {
super("Data Settings", true, false, true, false);
this.data = data;
@ -111,9 +103,6 @@ public class DataSettingsDialog extends DialogComponentProvider {
buildPanel();
}
/**
* Construct default data type settings dialog.
*/
public DataSettingsDialog(Program program, DataType dataType) {
super("Data Settings", true, false, true, false);
this.dataType = dataType;
@ -126,9 +115,6 @@ public class DataSettingsDialog extends DialogComponentProvider {
setHelpLocation(new HelpLocation("DataPlugin", "Default_Data_Settings"));
}
/**
* Construct default data type component settings dialog.
*/
DataSettingsDialog(Program program, DataTypeComponent dtc) {
super("Data Settings", true, false, true, false);
this.dtc = dtc;
@ -204,12 +190,7 @@ public class DataSettingsDialog extends DialogComponentProvider {
addOKButton();
JButton newApplyButton = new JButton("Apply");
newApplyButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
applySettings();
}
});
newApplyButton.addActionListener(e -> applySettings());
addButton(newApplyButton);
addCancelButton();
@ -219,17 +200,9 @@ public class DataSettingsDialog extends DialogComponentProvider {
JPanel workPanel = new JPanel(new BorderLayout());
workPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
settingsTableModel = new SettingsTableModel();
DefaultSortedTableModel sorter = new DefaultSortedTableModel(settingsTableModel);
sorter.sortByColumn(SettingsTableModel.DEFAULT_SORT_COL);
sorter.addTableModelListener(new TableModelListener() {
@Override
public void tableChanged(TableModelEvent e) {
appliedSettings = false;
}
});
settingsTable = new GhidraTable(sorter);
settingsTableModel = new SettingsTableModel(settingsDefs);
settingsTableModel.addTableModelListener(e -> appliedSettings = false);
settingsTable = new GhidraTable(settingsTableModel);
settingsTable.setAutoscrolls(true);
settingsTable.setRowSelectionAllowed(false);
settingsTable.setColumnSelectionAllowed(false);
@ -251,18 +224,12 @@ public class DataSettingsDialog extends DialogComponentProvider {
return workPanel;
}
/**
* @see ghidra.util.bean.GhidraDialog#cancelCallback()
*/
@Override
protected void cancelCallback() {
close();
dispose();
}
/**
* @see ghidra.util.bean.GhidraDialog#okCallback()
*/
@Override
protected void okCallback() {
applySettings();
@ -565,7 +532,7 @@ public class DataSettingsDialog extends DialogComponentProvider {
// For selection case we must ensure that settings has a non-null value even for defaults
if (selection != null && settings.getValue(def.getName()) == null) {
settings.setValue(def.getName(), new Long(def.getChoice(settings)));
settings.setValue(def.getName(), Long.valueOf(def.getChoice(settings)));
}
}
@ -594,81 +561,98 @@ public class DataSettingsDialog extends DialogComponentProvider {
return newChoices;
}
class SettingsTableModel extends AbstractTableModel {
//==================================================================================================
// Inner Classes
//==================================================================================================
private static final long serialVersionUID = 1L;
class SettingsRowObject {
static final int DEFAULT_SORT_COL = 0;
private SettingsDefinition definition;
SettingsRowObject(SettingsDefinition definition) {
this.definition = definition;
}
public String getName() {
return definition.getName();
}
Object getSettingsChoices() {
if (definition instanceof EnumSettingsDefinition) {
StringChoices choices = getChoices((EnumSettingsDefinition) definition);
return choices;
}
else if (definition instanceof BooleanSettingsDefinition) {
StringChoices choices = getChoices((BooleanSettingsDefinition) definition);
return choices;
}
return "<Unsupported>";
}
boolean useDefault() {
if (definition instanceof EnumSettingsDefinition) {
EnumSettingsDefinition def = (EnumSettingsDefinition) definition;
return def.getChoice(settings) == def.getChoice(defaultSettings);
}
else if (definition instanceof BooleanSettingsDefinition) {
BooleanSettingsDefinition def = (BooleanSettingsDefinition) definition;
return def.getValue(settings) == def.getValue(defaultSettings);
}
return false;
}
boolean setSettingsChoice(Object value) {
if (definition instanceof EnumSettingsDefinition) {
setChoice(value, (EnumSettingsDefinition) definition);
return true;
}
else if (definition instanceof BooleanSettingsDefinition) {
setChoice(value, (BooleanSettingsDefinition) definition);
return true;
}
return false;
}
void clear(SettingsImpl s) {
definition.clear(s);
}
}
private class SettingsTableModel extends AbstractSortedTableModel<SettingsRowObject> {
private List<SettingsRowObject> rows = new ArrayList<>();
SettingsTableModel(SettingsDefinition[] settingsDefs) {
for (SettingsDefinition sd : settingsDefs) {
rows.add(new SettingsRowObject(sd));
}
}
@Override
public List<SettingsRowObject> getModelData() {
return rows;
}
@Override
public String getName() {
return "Settings Definition Model";
}
@Override
public boolean isSortable(int columnIndex) {
return columnIndex == 0;
}
@Override
public boolean isCellEditable(int row, int col) {
return col != 0;
}
@Override
public int getColumnCount() {
return (selection != null || editingDefaults) ? 2 : 3;
}
@Override
public int getRowCount() {
return settingsDefs != null ? settingsDefs.length : 0;
}
@Override
public Object getValueAt(int row, int col) {
switch (col) {
case 0:
return settingsDefs[row].getName();
case 1:
if (settingsDefs[row] instanceof EnumSettingsDefinition) {
return getChoices((EnumSettingsDefinition) settingsDefs[row]);
}
else if (settingsDefs[row] instanceof BooleanSettingsDefinition) {
return getChoices((BooleanSettingsDefinition) settingsDefs[row]);
}
return "<Unsupported>";
case 2:
if (settingsDefs[row] instanceof EnumSettingsDefinition) {
EnumSettingsDefinition def = (EnumSettingsDefinition) settingsDefs[row];
return new Boolean(
def.getChoice(settings) == def.getChoice(defaultSettings));
}
else if (settingsDefs[row] instanceof BooleanSettingsDefinition) {
BooleanSettingsDefinition def =
(BooleanSettingsDefinition) settingsDefs[row];
return new Boolean(def.getValue(settings) == def.getValue(defaultSettings));
}
break;
}
return null;
}
/**
* @see TableModel#setValueAt(Object, int, int)
*/
@Override
public void setValueAt(Object value, int row, int col) {
switch (col) {
case 1:
if (settingsDefs[row] instanceof EnumSettingsDefinition) {
setChoice(value, (EnumSettingsDefinition) settingsDefs[row]);
fireTableDataChanged();
}
else if (settingsDefs[row] instanceof BooleanSettingsDefinition) {
setChoice(value, (BooleanSettingsDefinition) settingsDefs[row]);
fireTableDataChanged();
}
break;
case 2:
if (((Boolean) value).booleanValue()) {
settingsDefs[row].clear(settings);
fireTableDataChanged();
}
break;
}
}
/**
* @see TableModel#getColumnName(int)
*/
@Override
public String getColumnName(int col) {
switch (col) {
@ -682,40 +666,40 @@ public class DataSettingsDialog extends DialogComponentProvider {
return null;
}
/**
* @see TableModel#getColumnClass(int)
*/
@Override
public Class<?> getColumnClass(int col) {
switch (col) {
public Object getColumnValueForRow(SettingsRowObject t, int columnIndex) {
switch (columnIndex) {
case 0:
return String.class;
return t.getName();
case 1:
return Settings.class;
return t.getSettingsChoices();
case 2:
return Boolean.class;
return t.useDefault();
}
return null;
}
public boolean isColumnSortable(int col) {
return col == 0;
}
/**
* @see TableModel#isCellEditable(int, int)
*/
@Override
public boolean isCellEditable(int row, int col) {
return col != 0;
public void setValueAt(Object value, int row, int col) {
SettingsRowObject rowObject = rows.get(row);
switch (col) {
case 1:
if (rowObject.setSettingsChoice(value)) {
fireTableDataChanged();
}
break;
case 2:
if (((Boolean) value).booleanValue()) {
rowObject.clear(settings);
fireTableDataChanged();
}
break;
}
}
}
private class SettingsEditor extends AbstractCellEditor implements TableCellEditor {
private static final long serialVersionUID = 1L;
final static int ENUM = 0;
final static int BOOLEAN = 1;
@ -723,12 +707,7 @@ public class DataSettingsDialog extends DialogComponentProvider {
private GComboBox<String> comboBox = new GComboBox<>();
SettingsEditor() {
comboBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
fireEditingStopped();
}
});
comboBox.addItemListener(e -> fireEditingStopped());
}
/**
@ -755,9 +734,6 @@ public class DataSettingsDialog extends DialogComponentProvider {
return enuum;
}
/**
* @see javax.swing.table.TableCellEditor#getTableCellEditorComponent(javax.swing.JTable, java.lang.Object, boolean, int, int)
*/
@Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected,
int row, int column) {

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,6 +15,12 @@
*/
package ghidra.app.plugin.core.references;
import java.awt.Window;
import java.util.*;
import org.apache.commons.lang3.StringUtils;
import docking.widgets.table.AbstractSortedTableModel;
import ghidra.app.cmd.refs.UpdateExternalNameCmd;
import ghidra.framework.cmd.Command;
import ghidra.framework.plugintool.PluginTool;
@ -25,57 +30,40 @@ import ghidra.program.model.symbol.ExternalManager;
import ghidra.program.model.symbol.SourceType;
import ghidra.util.Msg;
import java.awt.Window;
import java.util.*;
import javax.swing.table.AbstractTableModel;
/**
* TableModel for the external program names and corresponding ghidra path names.
*/
class ExternalNamesTableModel extends AbstractTableModel {
public class ExternalNamesTableModel extends AbstractSortedTableModel<ExternalPath> {
final static int NAME_COL = 0;
final static int PATH_COL = 1;
final static String EXTERNAL_NAME = "Name";
final static String PATH_NAME = "Ghidra Program";
private final List<String> columns = List.of(EXTERNAL_NAME, PATH_NAME);
private final String[] columnNames = { EXTERNAL_NAME, PATH_NAME };
private List<String> nameList = new ArrayList<String>();
private List<String> pathNameList = new ArrayList<String>();
private Program program;
private PluginTool tool;
private Program program;
private List<ExternalPath> paths = new ArrayList<>();
public ExternalNamesTableModel(PluginTool tool) {
this.tool = tool;
}
@Override
public int getColumnCount() {
return columnNames.length;
}
public int getRowCount() {
return nameList.size();
return columns.size();
}
@Override
public String getColumnName(int column) {
return columnNames[column];
return columns.get(column);
}
public Object getValueAt(int rowIndex, int columnIndex) {
if (rowIndex >= nameList.size()) {
return "";
}
switch (columnIndex) {
case NAME_COL:
return nameList.get(rowIndex);
@Override
public String getName() {
return "External Programs Model";
}
case PATH_COL:
return pathNameList.get(rowIndex);
}
return "Unknown Column!";
@Override
public boolean isSortable(int columnIndex) {
return true;
}
@Override
@ -87,55 +75,81 @@ class ExternalNamesTableModel extends AbstractTableModel {
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
String extName = ((String) aValue).trim();
public List<ExternalPath> getModelData() {
return paths;
}
if ("".equals(extName)) {
@Override
public Object getColumnValueForRow(ExternalPath t, int columnIndex) {
switch (columnIndex) {
case NAME_COL:
return t.getName();
case PATH_COL:
return t.getPath();
}
return "Unknown Column!";
}
@Override
public void setValueAt(Object aValue, int row, int column) {
String newName = ((String) aValue).trim();
if (StringUtils.isBlank(newName)) {
return;
}
if (nameList.contains(extName)) {
if (nameList.indexOf(extName) != rowIndex) {
Window window = tool.getActiveWindow();
Msg.showInfo(getClass(), window, "Duplicate Name", "Name already exists: " +
extName);
}
int index = indexOf(newName);
if (index >= 0) {
Window window = tool.getActiveWindow();
Msg.showInfo(getClass(), window, "Duplicate Name", "Name already exists: " + newName);
return;
}
Command cmd =
new UpdateExternalNameCmd(nameList.get(rowIndex), extName, SourceType.USER_DEFINED);
ExternalPath path = paths.get(row);
String oldName = path.getName();
Command cmd = new UpdateExternalNameCmd(oldName, newName, SourceType.USER_DEFINED);
if (!tool.execute(cmd, program)) {
tool.setStatusInfo(cmd.getStatusMsg());
}
}
private int indexOf(String name) {
for (int i = 0; i < paths.size(); i++) {
ExternalPath path = paths.get(i);
if (path.getName().equals(name)) {
return i;
}
}
return 0;
}
void setProgram(Program program) {
this.program = program;
updateTableData();
}
///////////////////////////////////////////////////////////////////////////
void updateTableData() {
nameList.clear();
pathNameList.clear();
paths.clear();
if (program == null) {
fireTableDataChanged();
return;
}
ExternalManager extMgr = program.getExternalManager();
String[] programNames = extMgr.getExternalLibraryNames();
Arrays.sort(programNames);
for (int i = 0; i < programNames.length; i++) {
if (Library.UNKNOWN.equals(programNames[i])) {
for (String programName : programNames) {
if (Library.UNKNOWN.equals(programName)) {
continue;
}
nameList.add(programNames[i]);
pathNameList.add(extMgr.getExternalLibraryPath(programNames[i]));
ExternalPath path =
new ExternalPath(programName, extMgr.getExternalLibraryPath(programName));
paths.add(path);
}
fireTableDataChanged();
}

View File

@ -0,0 +1,72 @@
/* ###
* 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 ghidra.app.plugin.core.references;
import java.util.Objects;
/**
* A simple container class used as a row object
*/
class ExternalPath {
private String name;
private String path;
ExternalPath(String name, String path) {
this.name = name;
this.path = path;
}
String getName() {
return name;
}
String getPath() {
return path;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((path == null) ? 0 : path.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
ExternalPath other = (ExternalPath) obj;
if (!Objects.equals(name, other.name)) {
return false;
}
if (!Objects.equals(path, other.path)) {
return false;
}
return true;
}
}

View File

@ -24,7 +24,6 @@ import java.util.List;
import javax.swing.*;
import docking.ActionContext;
import docking.widgets.table.DefaultSortedTableModel;
import ghidra.framework.model.DomainObjectListener;
import ghidra.framework.plugintool.ComponentProviderAdapter;
import ghidra.framework.plugintool.PluginTool;
@ -35,7 +34,6 @@ import ghidra.util.table.GhidraTable;
public class ExternalReferencesProvider extends ComponentProviderAdapter {
private JPanel mainPanel;
private ExternalNamesTableModel tableModel;
private DefaultSortedTableModel sortedModel;
private GhidraTable table;
private Program program;
@ -106,9 +104,7 @@ public class ExternalReferencesProvider extends ComponentProviderAdapter {
JPanel panel = new JPanel(new BorderLayout());
tableModel = new ExternalNamesTableModel(tool);
sortedModel = new DefaultSortedTableModel(tableModel);
sortedModel.sortByColumn(ExternalNamesTableModel.NAME_COL);
table = new GhidraTable(sortedModel);
table = new GhidraTable(tableModel);
InputMap inputMap = table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
KeyStroke enter = KeyStroke.getKeyStroke("ENTER");
@ -150,40 +146,6 @@ public class ExternalReferencesProvider extends ComponentProviderAdapter {
panel.add(sp, BorderLayout.CENTER);
// addButton = new JButton("Add");
// addButton.setMnemonic('A');
// addButton.addActionListener(new ActionListener() {
// public void actionPerformed(ActionEvent e) {
// clearStatusText();
// tableModel.addDefaultRow();
// }
// });
// ToolTipManager.setToolTipText(addButton,
// "Link new External Program name to blank Ghidra Pathname");
//
// clearButton = new JButton("Clear");
// clearButton.setMnemonic('C');
// clearButton.addActionListener(new ActionListener() {
// public void actionPerformed(ActionEvent e) {
// clear();
// }
// });
// ToolTipManager.setToolTipText(clearButton, "Remove External Program Link");
// editButton = new JButton("Edit");
// editButton.setMnemonic('E');
// editButton.addActionListener(new ActionListener() {
// public void actionPerformed(ActionEvent e) {
// editPathName();
// }
// });
// ToolTipManager.setToolTipText(editButton, "Edit Ghidra Pathname");
//
// addButton(addButton);
// addButton(editButton);
// addButton(clearButton);
// enableButtons();
return panel;
}
@ -201,9 +163,8 @@ public class ExternalReferencesProvider extends ComponentProviderAdapter {
public List<String> getSelectedExternalNames() {
List<String> externalNames = new ArrayList<>();
int[] selectedRows = table.getSelectedRows();
for (int selectedRow : selectedRows) {
int index = sortedModel.getSortedIndex(selectedRow);
String externalName = (String) tableModel.getValueAt(index, 0);
for (int row : selectedRows) {
String externalName = (String) tableModel.getValueAt(row, 0);
externalNames.add(externalName);
}
return externalNames;

View File

@ -23,7 +23,6 @@ import javax.swing.event.ListSelectionListener;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;
import docking.widgets.table.DefaultSortedTableModel;
import docking.widgets.table.GTable;
import ghidra.app.nav.Navigatable;
import ghidra.app.services.GoToService;
@ -214,10 +213,6 @@ public class GhidraTable extends GTable {
if (model instanceof ProgramTableModel) {
return (ProgramTableModel) model;
}
else if (model instanceof DefaultSortedTableModel) {
DefaultSortedTableModel defaultSortedTableModel = (DefaultSortedTableModel) model;
return getProgramTableModel(defaultSortedTableModel.getModel());
}
return null;
}

View File

@ -30,6 +30,7 @@ import ghidra.program.util.ProgramSelection;
/**
* This is a "program aware" version of GTableFilterPanel
* @param <ROW_OBJECT> the row type
*/
public class GhidraTableFilterPanel<ROW_OBJECT> extends GTableFilterPanel<ROW_OBJECT> {

View File

@ -26,14 +26,17 @@ import docking.ActionContext;
import docking.action.DockingActionIf;
import docking.action.MenuData;
import docking.widgets.dialogs.StringChoices;
import docking.widgets.table.AbstractSortedTableModel;
import ghidra.app.LocationCallback;
import ghidra.app.context.ListingActionContext;
import ghidra.app.events.ProgramLocationPluginEvent;
import ghidra.app.events.ProgramSelectionPluginEvent;
import ghidra.app.plugin.core.clear.ClearCmd;
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
import ghidra.app.plugin.core.data.DataSettingsDialog.SettingsRowObject;
import ghidra.app.plugin.core.navigation.NextPrevAddressPlugin;
import ghidra.docking.settings.*;
import ghidra.docking.settings.FormatSettingsDefinition;
import ghidra.docking.settings.SettingsDefinition;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*;
import ghidra.program.model.data.*;
@ -215,7 +218,7 @@ public abstract class AbstractDataActionTest extends AbstractGhidraHeadedIntegra
waitForSwing();
Runnable r = () -> {
DataSettingsDialog.SettingsTableModel model = dlg.getSettingsTableModel();
AbstractSortedTableModel<SettingsRowObject> model = dlg.getSettingsTableModel();
int useDefaultCol = model.findColumn("Use Default");
int rowCnt = model.getRowCount();
@ -242,27 +245,25 @@ public abstract class AbstractDataActionTest extends AbstractGhidraHeadedIntegra
waitForSwing();
final DataSettingsDialog.SettingsTableModel model = dlg.getSettingsTableModel();
final int settingsCol = model.findColumn("Settings");
assertEquals(Settings.class, model.getColumnClass(settingsCol));
AbstractSortedTableModel<SettingsRowObject> model = dlg.getSettingsTableModel();
error = null;
Runnable r = () -> {
int nameCol = model.findColumn("Name");
int settingsCol1 = model.findColumn("Settings");
int settingsCol = model.findColumn("Settings");
int rowCnt = model.getRowCount();
for (int i = 0; i < rowCnt; i++) {
String name = (String) model.getValueAt(i, nameCol);
int index = findSettingIndex(settingNames, name);
if (index != -1) {
Object v = model.getValueAt(i, settingsCol1);
Object v = model.getValueAt(i, settingsCol);
if (v instanceof StringChoices) {
StringChoices choices = (StringChoices) v;
choices.setSelectedValue(newValues[index]);
model.setValueAt(choices, i, settingsCol1);
model.setValueAt(choices, i, settingsCol);
}
else {
error = "Unsupported test setting: " + v.getClass();
@ -770,7 +771,7 @@ public abstract class AbstractDataActionTest extends AbstractGhidraHeadedIntegra
protected void manipulateMutabilitySettings(boolean testDefaultSetting, boolean insideStruct,
boolean commonStruct, Data data1, Data data2) throws Exception {
assertTrue(data1.getDataType() == data2.getDataType());
assertSame(data1.getDataType(), data2.getDataType());
boolean settingsAreShared =
testDefaultSetting && (!insideStruct || (insideStruct && commonStruct));

View File

@ -26,14 +26,6 @@ import ghidra.program.model.data.DataType;
@Category(NightlyCategory.class)
public class DataAction1Test extends AbstractDataActionTest {
/**
* Constructor for FallThroughActionTest.
* @param arg0
*/
public DataAction1Test() {
super();
}
@Test
public void testAllStructDataSettings() throws Exception {

View File

@ -26,14 +26,6 @@ import ghidra.program.model.data.DataType;
@Category(NightlyCategory.class)
public class DataAction2Test extends AbstractDataActionTest {
/**
* Constructor for FallThroughActionTest.
* @param arg0
*/
public DataAction2Test() {
super();
}
@Test
public void testAllDefaultStructDataSettings() throws Exception {

View File

@ -26,20 +26,11 @@ import ghidra.program.model.data.DataType;
@Category(NightlyCategory.class)
public class DataAction3Test extends AbstractDataActionTest {
/**
* Constructor for FallThroughActionTest.
* @param arg0
*/
public DataAction3Test() {
super();
}
@Test
public void testAllDefaultDataSettings() throws Exception {
List<DataType> builtIns = getBuiltInDataTypesAsFavorites();
for (DataType type : builtIns) {
//if (!(type instanceof UnicodeDataType)) continue;
String actionName = "Define " + type.getName();
manipulateAllSettings(true, false, false, actionName);
}

View File

@ -484,7 +484,7 @@ public class DataAction4Test extends AbstractDataActionTest {
doAction(CREATE_ARRAY, false);
final NumberInputDialog dlg1 = env.waitForDialogComponent(NumberInputDialog.class, 1000);
final NumberInputDialog dlg1 = waitForDialogComponent(NumberInputDialog.class);
assertNotNull("Expected element count input dialog", dlg1);
Runnable r = () -> dlg1.setInput(0x20);
@ -500,8 +500,6 @@ public class DataAction4Test extends AbstractDataActionTest {
// Test action disablement on array element location
Data array = getContextData();
Data d0 = array.getComponent(0);
BytesFieldLocation loc =
new BytesFieldLocation(program, addr(0x010069f2), addr(0x010069f2), new int[] { 0 }, 0);
locationGenerated(loc);
@ -515,7 +513,7 @@ public class DataAction4Test extends AbstractDataActionTest {
doAction(CREATE_ARRAY, false);
final NumberInputDialog dlg2 = env.waitForDialogComponent(NumberInputDialog.class, 1000);
final NumberInputDialog dlg2 = waitForDialogComponent(NumberInputDialog.class);
assertNotNull("Expected element count input dialog", dlg2);
r = () -> dlg2.setInput(0x10);
@ -748,7 +746,7 @@ public class DataAction4Test extends AbstractDataActionTest {
doAction(CREATE_ARRAY, false);
final NumberInputDialog dlg1 = env.waitForDialogComponent(NumberInputDialog.class, 1000);
final NumberInputDialog dlg1 = waitForDialogComponent(NumberInputDialog.class);
assertNotNull("Expected element count input dialog", dlg1);
Runnable r = () -> dlg1.setInput(5);

View File

@ -16,18 +16,22 @@
package docking.widgets.dialogs;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.table.*;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellEditor;
import docking.DialogComponentProvider;
import docking.DockingWindowManager;
import docking.DockingWindowManager;<<<<<<<Upstream,based on origin/master
import docking.widgets.checkbox.GCheckBox;
import docking.widgets.combobox.GComboBox;
import docking.widgets.table.DefaultSortedTableModel;
import docking.widgets.table.DefaultSortedTableModel;=======
import docking.widgets.table.AbstractSortedTableModel;>>>>>>>000543e GT-2763-Table Sorting-updated help;removed old DefaultSortedTableModel
import docking.widgets.table.GTable;
import ghidra.docking.settings.*;
import ghidra.util.HelpLocation;
@ -44,9 +48,6 @@ public class SettingsDialog extends DialogComponentProvider {
private SettingsTableModel settingsTableModel;
private GTable settingsTable;
/**
* Construct instance settings dialog.
*/
public SettingsDialog(HelpLocation help) {
super("Settings", true, false, true, false);
if (help != null) {
@ -58,9 +59,6 @@ public class SettingsDialog extends DialogComponentProvider {
setHelpLocation(new HelpLocation("Tables/GhidraTableHeaders.html", "ColumnSettings"));
}
/**
* Show dialog for the specified set of settings definitions and settings storage.
*/
public void show(Component parent, String title, SettingsDefinition[] newSettingsDefs,
Settings newSettings) {
this.settingsDefs = newSettingsDefs;
@ -82,18 +80,13 @@ public class SettingsDialog extends DialogComponentProvider {
JPanel workPanel = new JPanel(new BorderLayout());
workPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
settingsTableModel = new SettingsTableModel();
DefaultSortedTableModel sorter = new DefaultSortedTableModel(settingsTableModel);
sorter.sortByColumn(SettingsTableModel.DEFAULT_SORT_COL);
settingsTable = new GTable(sorter);
settingsTableModel = new SettingsTableModel(settingsDefs);
settingsTable = new GTable(settingsTableModel);
settingsTable.setAutoscrolls(true);
settingsTable.setRowSelectionAllowed(false);
settingsTable.setColumnSelectionAllowed(false);
// disable user sorting and column adding (we don't expect enough data to require sort
// changes)
// disable sorting and column adding (we don't expect enough data to require sort changes)
settingsTable.getTableHeader().setReorderingAllowed(false);
settingsTable.setColumnHeaderPopupEnabled(false);
settingsTable.setUserSortingEnabled(false);
@ -109,83 +102,97 @@ public class SettingsDialog extends DialogComponentProvider {
return workPanel;
}
/**
* @see ghidra.util.bean.GhidraDialog#cancelCallback()
*/
@Override
protected void cancelCallback() {
dispose();
}
class SettingsTableModel extends AbstractTableModel {
//==================================================================================================
// Private Methods
//==================================================================================================
static final int DEFAULT_SORT_COL = 0;
private class SettingsRowObject {
private SettingsDefinition definition;
SettingsRowObject(SettingsDefinition definition) {
this.definition = definition;
}
public String getName() {
return definition.getName();
}
Object getSettingsChoices() {
if (definition instanceof EnumSettingsDefinition) {
EnumSettingsDefinition def = (EnumSettingsDefinition) definition;
StringChoices choices = new StringChoices(def.getDisplayChoices(settings));
choices.setSelectedValue(def.getChoice(settings));
return choices;
}
else if (definition instanceof BooleanSettingsDefinition) {
BooleanSettingsDefinition def = (BooleanSettingsDefinition) definition;
return Boolean.valueOf(def.getValue(settings));
}
return "<Unsupported>";
}
boolean setSettingsChoice(Object value) {
if (definition instanceof EnumSettingsDefinition) {
EnumSettingsDefinition def = (EnumSettingsDefinition) definition;
StringChoices choices = (StringChoices) value;
def.setChoice(settings, choices.getSelectedValueIndex());
return true;
}
else if (definition instanceof BooleanSettingsDefinition) {
BooleanSettingsDefinition def = (BooleanSettingsDefinition) definition;
def.setValue(settings, ((Boolean) value).booleanValue());
return true;
}
return false;
}
void clear(Settings s) {
definition.clear(s);
}
}
private class SettingsTableModel extends AbstractSortedTableModel<SettingsRowObject> {
private List<SettingsRowObject> rows = new ArrayList<>();
SettingsTableModel(SettingsDefinition[] settingsDefs) {
for (SettingsDefinition sd : settingsDefs) {
rows.add(new SettingsRowObject(sd));
}
}
@Override
public List<SettingsRowObject> getModelData() {
return rows;
}
@Override
public String getName() {
return "Settings Definition Model";
}
@Override
public boolean isSortable(int columnIndex) {
return columnIndex == 0;
}
@Override
public boolean isCellEditable(int row, int col) {
return col != 0;
}
@Override
public int getColumnCount() {
return 2;
}
@Override
public int getRowCount() {
return settingsDefs != null ? settingsDefs.length : 0;
}
@Override
public Object getValueAt(int row, int col) {
switch (col) {
case 0:
return settingsDefs[row].getName();
case 1:
if (settingsDefs[row] instanceof EnumSettingsDefinition) {
EnumSettingsDefinition def = (EnumSettingsDefinition) settingsDefs[row];
StringChoices choices = new StringChoices(def.getDisplayChoices(settings));
choices.setSelectedValue(def.getChoice(settings));
return choices;
}
else if (settingsDefs[row] instanceof BooleanSettingsDefinition) {
BooleanSettingsDefinition def =
(BooleanSettingsDefinition) settingsDefs[row];
return new Boolean(def.getValue(settings));
}
return "<Unsupported>";
}
return null;
}
/**
* @see TableModel#setValueAt(Object, int, int)
*/
@Override
public void setValueAt(Object value, int row, int col) {
switch (col) {
case 1:
if (settingsDefs[row] instanceof EnumSettingsDefinition) {
EnumSettingsDefinition def = (EnumSettingsDefinition) settingsDefs[row];
StringChoices choices = (StringChoices) value;
def.setChoice(settings, choices.getSelectedValueIndex());
fireTableDataChanged();
}
else if (settingsDefs[row] instanceof BooleanSettingsDefinition) {
BooleanSettingsDefinition def =
(BooleanSettingsDefinition) settingsDefs[row];
def.setValue(settings, ((Boolean) value).booleanValue());
fireTableDataChanged();
}
break;
case 2:
if (((Boolean) value).booleanValue()) {
settingsDefs[row].clear(settings);
fireTableDataChanged();
}
break;
}
}
/**
* @see TableModel#getColumnName(int)
*/
@Override
public String getColumnName(int col) {
switch (col) {
@ -197,32 +204,34 @@ public class SettingsDialog extends DialogComponentProvider {
return null;
}
/**
* @see TableModel#getColumnClass(int)
*/
@Override
public Class<?> getColumnClass(int col) {
switch (col) {
public Object getColumnValueForRow(SettingsRowObject t, int columnIndex) {
switch (columnIndex) {
case 0:
return String.class;
return t.getName();
case 1:
return Settings.class;
return t.getSettingsChoices();
}
return null;
}
public boolean isColumnSortable(int col) {
return col == 0;
}
/**
* @see TableModel#isCellEditable(int, int)
*/
@Override
public boolean isCellEditable(int row, int col) {
return col != 0;
public void setValueAt(Object value, int row, int col) {
SettingsRowObject rowObject = rows.get(row);
switch (col) {
case 1:
if (rowObject.setSettingsChoice(value)) {
fireTableDataChanged();
}
break;
case 2:
if (((Boolean) value).booleanValue()) {
rowObject.clear(settings);
fireTableDataChanged();
}
break;
}
}
}
private class SettingsEditor extends AbstractCellEditor
@ -235,28 +244,20 @@ public class SettingsDialog extends DialogComponentProvider {
private GComboBox<String> comboBox = new GComboBox<>();
private GCheckBox checkBox = new GCheckBox();
private final Runnable editStopped = new Runnable() {
@Override
public void run() {
fireEditingStopped();
}
};
private final Runnable editStopped = () -> fireEditingStopped();
SettingsEditor() {
super();
comboBox.addPopupMenuListener(this);
}
/**
* @see javax.swing.CellEditor#getCellEditorValue()
*/
@Override
public Object getCellEditorValue() {
switch (mode) {
case ENUM:
return getComboBoxEnum();
case BOOLEAN:
return Boolean.valueOf(checkBox.isSelected());
return checkBox.isSelected();
}
throw new AssertException();
}
@ -271,9 +272,6 @@ public class SettingsDialog extends DialogComponentProvider {
return choices;
}
/**
* @see javax.swing.table.TableCellEditor#getTableCellEditorComponent(javax.swing.JTable, java.lang.Object, boolean, int, int)
*/
@Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected,
int row, int column) {
@ -304,26 +302,19 @@ public class SettingsDialog extends DialogComponentProvider {
comboBox.setSelectedIndex(choices.getSelectedValueIndex());
}
/**
* @see javax.swing.event.PopupMenuListener#popupMenuWillBecomeVisible(javax.swing.event.PopupMenuEvent)
*/
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
// stub
}
/**
* @see javax.swing.event.PopupMenuListener#popupMenuWillBecomeInvisible(javax.swing.event.PopupMenuEvent)
*/
@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
SwingUtilities.invokeLater(editStopped);
}
/**
* @see javax.swing.event.PopupMenuListener#popupMenuCanceled(javax.swing.event.PopupMenuEvent)
*/
@Override
public void popupMenuCanceled(PopupMenuEvent e) {
// stub
}
}

View File

@ -1,432 +0,0 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docking.widgets.table;
import ghidra.util.Msg;
import ghidra.util.SystemUtilities;
import ghidra.util.datastruct.WeakDataStructureFactory;
import ghidra.util.datastruct.WeakSet;
import ghidra.util.exception.AssertException;
import java.util.*;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
import docking.widgets.table.ColumnSortState.SortDirection;
/**
* A sorter for TableModels. The sorter has a model (conforming to TableModel)
* and itself implements TableModel. TableSorter does not store or copy
* the data in the TableModel, instead it maintains an array of
* integers which it keeps the same size as the number of rows in its
* model. When the model changes it notifies the sorter that something
* has changed (e.g., "rowsAdded") so that its internal array of integers
* can be reallocated. As requests are made of the sorter (like
* getValueAt(row, col) it redirects them to its model via the mapping
* array. That way the TableSorter appears to hold another copy of the table
* with the rows in a different order. The sorting algorithm used is stable
* which means that it does not move around rows when its comparison
* function returns 0 to denote that they are equivalent.
*
* @deprecated You should instead be using {@link AbstractSortedTableModel}
*/
@Deprecated
public class DefaultSortedTableModel extends AbstractTableModel implements SortedTableModel,
TableModelListener {
// all callbacks to fire changes and add listeners are expected to be in the Swing thread
private WeakSet<SortListener> listeners =
WeakDataStructureFactory.createSingleThreadAccessWeakSet();
//========================================================================
// conversion to multi sorting tables
private TableSortState createSortState(int column, boolean ascending) {
ColumnSortState sortState =
new ColumnSortState(column, ascending ? SortDirection.ASCENDING
: SortDirection.DESCENDING, 1);
return new TableSortState(sortState);
}
public void setSort(int column, boolean ascending) {
TableSortState sortState = createSortState(column, ascending);
setTableSortState(sortState);
}
@Override
public int getPrimarySortColumnIndex() {
return tableSortState.iterator().next().getColumnModelIndex();
}
public boolean isAscending() {
return true;
}
// end conversion to multi sorting tables
private TableSortState tableSortState = new TableSortState();
private int[] indexes;
/**
* A poorly named variable that is intended to indicate that the client is making bulk updates
* and this table should not perform indexing while this bulk operation is happening.
*/
private boolean sortEnabled = true;
private Map<Integer, Comparator<?>> registeredComparatorMap =
new HashMap<Integer, Comparator<?>>();
protected TableModel model;
/**
* Construct a new TableSorter using the given model.
*
* @deprecated You should instead be using {@link AbstractSortedTableModel}
*/
@Deprecated
public DefaultSortedTableModel(TableModel model) {
if (model == null) {
throw new NullPointerException("Model cannot be null!");
}
if (model instanceof AbstractSortedTableModel<?>) {
throw new AssertException("You cannot pass an AbstractSortedTableModel to " +
getClass().getSimpleName() + "--it is already sorted!");
}
setModel(model);
init();
}
private void init() {
TableSortState defaultSortState = createSortState(0, true);
this.tableSortState = defaultSortState;
}
public void setModel(TableModel model) {
if (model == null) {
throw new NullPointerException("Model cannot be null!");
}
this.model = model;
model.addTableModelListener(this);
reallocateIndexes();
}
public TableModel getModel() {
return model;
}
/**
* Enable the sorter according to the enable parameter. This method
* should be called with enable set to <b>false</b> <i>before</i> the table
* model is populated or else a sort will be done after each row is
* inserted, and that would not be good.
* @param enable true means to enable the sorting.
*/
public void enableSorter(boolean enable) {
sortEnabled = enable;
if (!sortEnabled) {
return;
}
reallocateIndexes();
sort();
SystemUtilities.runIfSwingOrPostSwingLater(new Runnable() {
@Override
public void run() {
notifySorted();
}
});
}
private void notifySorted() {
fireTableChangedEvent();
for (SortListener listener : listeners) {
listener.modelSorted(tableSortState);
}
}
private void fireTableChangedEvent() {
fireTableChanged(new TableModelEvent(this));
}
@Override
public void addSortListener(SortListener l) {
listeners.add(l);
}
public void registerComparator(Comparator<?> comparator, int column) {
registeredComparatorMap.put(column, comparator);
}
public void deRegisterComparator(int column) {
registeredComparatorMap.remove(column);
}
@SuppressWarnings("unchecked")
// for the Comparator<?> usage
private int compareRowsByColumn(int row1, int row2, int column) {
TableModel data = model;
Object o1 = data.getValueAt(row1, column);
Object o2 = data.getValueAt(row2, column);
// If both values are null return 0
if (o1 == null && o2 == null) {
return 0;
}
else if (o1 == null) { // Define null less than everything.
return -1;
}
else if (o2 == null) {
return 1;
}
// if the user has registered a comparator, prefer that
Comparator comparator = getComparator(column);
Object value1 = data.getValueAt(row1, column);
Object value2 = data.getValueAt(row2, column);
return comparator.compare(value1, value2);
}
private Comparator<?> getComparator(int column) {
Comparator<?> comparator = registeredComparatorMap.get(column);
if (comparator != null) {
return comparator;
}
return DEFAULT_COMPARATOR;
}
private int compare(int row1, int row2) {
for (ColumnSortState columnSortState : tableSortState) {
int result = compareRowsByColumn(row1, row2, columnSortState.getColumnModelIndex());
if (result != 0) {
return columnSortState.isAscending() ? result : -result;
}
}
return 0;
}
private void reallocateIndexes() {
int rowCount = model.getRowCount();
// Set up a new array of indexes with the right number of elements
// for the new data model.
indexes = new int[rowCount];
// Initialize with the identity mapping.
for (int row = 0; row < rowCount; row++) {
indexes[row] = row;
}
}
@Override
public void tableChanged(TableModelEvent e) {
if (sortEnabled) {
reallocateIndexes();
sort();
}
fireTableChanged(e);
}
private void checkModel() {
if (indexes.length == model.getRowCount()) {
return; // O.K.!
}
if (!sortEnabled) {
return; // don't report an issue if we are called while in the middle of updating
}
Msg.error(this, "Sorter not informed of a change in model.");
}
private void sort() {
checkModel();
shuttlesort(indexes.clone(), indexes, 0, indexes.length);
}
// This is a home-grown implementation which we have not had time
// to research - it may perform poorly in some circumstances. It
// requires twice the space of an in-place algorithm and makes
// NlogN assignments shuttling the values between the two
// arrays. The number of compares appears to vary between N-1 and
// NlogN depending on the initial order but the main reason for
// using it here is that, unlike qsort, it is stable.
private void shuttlesort(int[] from, int[] to, int low, int high) {
if (high - low < 2) {
return;
}
int middle = (low + high) / 2;
shuttlesort(to, from, low, middle);
shuttlesort(to, from, middle, high);
int p = low;
int q = middle;
/* This is an optional short-cut; at each recursive call,
check to see if the elements in this subset are already
ordered. If so, no further comparisons are needed; the
sub-array can just be copied. The array must be copied rather
than assigned otherwise sister calls in the recursion might
get out of sinc. When the number of elements is three they
are partitioned so that the first set, [low, mid), has one
element and and the second, [mid, high), has two. We skip the
optimization when the number of elements is three or less as
the first compare in the normal merge will produce the same
sequence of steps. This optimization seems to be worthwhile
for partially ordered lists but some analysis is needed to
find out how the performance drops to Nlog(N) as the initial
order diminishes - it may drop very quickly. */
if (high - low >= 4 && compare(from[middle - 1], from[middle]) <= 0) {
for (int i = low; i < high; i++) {
to[i] = from[i];
}
return;
}
// A normal merge.
for (int i = low; i < high; i++) {
if (q >= high || (p < middle && compare(from[p], from[q]) <= 0)) {
to[i] = from[p++];
}
else {
to[i] = from[q++];
}
}
}
/**
* @see javax.swing.table.TableModel#getValueAt(int, int)
*/
@Override
public Object getValueAt(int aRow, int aColumn) {
checkModel();
return model.getValueAt(indexes[aRow], aColumn);
}
/**
* Converts a sorted index into an unsorted index.
* This is good if you need to access the underlying table directly by
* the unsorted index.
*/
public int getSortedIndex(int aRow) {
checkModel();
return indexes[aRow];
}
/**
* @see javax.swing.table.TableModel#setValueAt(java.lang.Object, int, int)
*/
@Override
public void setValueAt(Object aValue, int aRow, int aColumn) {
checkModel();
model.setValueAt(aValue, indexes[aRow], aColumn);
}
/**
* Sorts the model in ascending order by the specified columnIndex.
* @param column the index of the column to sort
*/
public void sortByColumn(int column) {
ColumnSortState sortState = new ColumnSortState(column, SortDirection.ASCENDING, 1);
TableSortState newCollection = new TableSortState(sortState);
setTableSortState(newCollection);
}
@Override
public boolean isSortable(int columnIndex) {
return true;
}
public void resort() {
tableChanged(new TableModelEvent(this));
}
@Override
public TableSortState getTableSortState() {
return tableSortState;
}
@Override
public void setTableSortState(TableSortState sortStates) {
this.tableSortState = sortStates;
resort();
}
//==================================================================================================
// Delegate Methods - We are a wrapper
//==================================================================================================
/**
*
* @see javax.swing.table.TableModel#getRowCount()
*/
@Override
public int getRowCount() {
// Always rely on the indexed values as the count. This prevents getValueAt() from
// being called when the indexes are out-of-date, while the client is manipulating the table
return indexes.length;
}
/**
*
* @see javax.swing.table.TableModel#getColumnCount()
*/
@Override
public int getColumnCount() {
return model.getColumnCount();
}
/**
*
* @see javax.swing.table.TableModel#getColumnName(int)
*/
@Override
public String getColumnName(int aColumn) {
return model.getColumnName(aColumn);
}
/**
*
* @see javax.swing.table.TableModel#getColumnClass(int)
*/
@Override
public Class<?> getColumnClass(int aColumn) {
return model.getColumnClass(aColumn);
}
/**
*
* @see javax.swing.table.TableModel#isCellEditable(int, int)
*/
@Override
public boolean isCellEditable(int row, int column) {
return model.isCellEditable(row, column);
}
}

View File

@ -22,7 +22,6 @@ import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.TableModel;
import org.apache.commons.collections4.map.LazyMap;
import org.apache.logging.log4j.LogManager;
@ -40,17 +39,8 @@ import utilities.util.reflection.ReflectionUtilities;
* filtered, the contents change (and then change back when the filter is removed). It is nice
* to be able to filter a table, select an item of interest, and then unfilter the table to see
* that item in more context.
* <p>
* Notes on usage:
* <ul>
* <li>Some table models are sensitive to the order in which {@link TableModel#tableChanged()}
* is called. These models should either not use this SelectionManger, or need to
* change their code to be more robust. As an example, the {@link DefaultSortedTableModel}
* updates its indexes in odd ways. Further, there is a chance that the state of its
* indexing is incorrect when <tt>tableChanged</tt> is called. So, that model has to
* account for the fact that it may get called by this class when it is in a bad state.
* </li>
* </ul>
*
* @param <T> the row type
*/
public class RowObjectSelectionManager<T> extends DefaultListSelectionModel
implements SelectionManager {
@ -109,14 +99,6 @@ public class RowObjectSelectionManager<T> extends DefaultListSelectionModel
installListSelectionListener();
}
/**
* Sets the logger used by this class. Useful for debugging individual table issues.
*/
@Override
public void setLogger(Logger logger) {
this.log = logger;
}
@Override
public void addSelectionManagerListener(SelectionManagerListener listener) {
listeners.add(listener);

View File

@ -15,6 +15,7 @@
*/
package docking.widgets.table;
import java.util.Arrays;
import java.util.List;
import javax.swing.table.TableModel;
@ -29,11 +30,7 @@ public interface RowObjectTableModel<T> extends TableModel {
public static TableModel unwrap(TableModel m) {
// TODO can we now get rid of the default sorted model usage?
TableModel model = m;
while (model instanceof DefaultSortedTableModel) {
model = ((DefaultSortedTableModel) model).getModel();
}
while (model instanceof TableModelWrapper) {
model = ((TableModelWrapper<?>) model).getWrappedModel();
}
@ -52,6 +49,7 @@ public interface RowObjectTableModel<T> extends TableModel {
* non-filtering models the view and model rows will always be the same.
*
* @param viewRow the row for which to return a row object.
* @return the row object
*/
public T getRowObject(int viewRow);
@ -77,7 +75,7 @@ public interface RowObjectTableModel<T> extends TableModel {
* the underlying data and not a copy, as this method will potentially sort the given data.
* <p>
* For those subclasses using an array, you may use the <tt>Arrays</tt> class to create
* a list backed by the array ({@link Arrays#asList(Object...)).
* a list backed by the array ({@link Arrays#asList(Object...)}).
* @return the model data.
*/
public List<T> getModelData();

View File

@ -17,9 +17,6 @@ package docking.widgets.table;
import javax.swing.ListSelectionModel;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableModel;
import org.apache.logging.log4j.Logger;
/**
* A class to track and restore selections made in a table. We use this in the docking
@ -27,17 +24,6 @@ import org.apache.logging.log4j.Logger;
* filtered, the contents change (and then change back when the filter is removed). It is nice
* to be able to filter a table, select an item of interest, and then unfilter the table to see
* that item in more context.
* <p>
* Notes on usage:
* <ul>
* <li>Some table models are sensitive to the order in which {@link TableModel#tableChanged()}
* is called. These models should either not use this SelectionManger, or need to
* change their code to be more robust. As an example, the {@link DefaultSortedTableModel}
* updates its indexes in odd ways. Further, there is a chance that the state of its
* indexing is incorrect when <tt>tableChanged</tt> is called. So, that model has to
* account for the fact that it may get called by this class when it is in a bad state.
* </li>
* </ul>
*/
public interface SelectionManager extends ListSelectionModel, TableModelListener {
@ -47,12 +33,5 @@ public interface SelectionManager extends ListSelectionModel, TableModelListener
public void clearSavedSelection();
/**
* Allows clients to enable tracing by providing a logger with tracing enabled.
* @param logger The logger to be used by this manager, which has tracing embedded in its
* code.
*/
public void setLogger(Logger logger);
public void dispose();
}