mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-24 03:09:36 +08:00
Merge remote-tracking branch 'origin/GP-1-dragonmacher-table-model-filter-fix--SQUASHED'
This commit is contained in:
+1
@@ -128,6 +128,7 @@ class MemoryMapProvider extends ComponentProviderAdapter {
|
||||
table = new MemoryMapTable(tableModel);
|
||||
filterPanel = new GhidraTableFilterPanel<>(table, tableModel);
|
||||
|
||||
table.installNavigation(tool);
|
||||
table.setAutoCreateColumnsFromModel(false);
|
||||
|
||||
GTableCellRenderer monoRenderer = new GTableCellRenderer();
|
||||
|
||||
@@ -15,15 +15,14 @@
|
||||
*/
|
||||
package ghidra.util.table;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.event.TableModelEvent;
|
||||
import javax.swing.event.TableModelListener;
|
||||
import javax.swing.table.TableModel;
|
||||
|
||||
import docking.widgets.table.*;
|
||||
import docking.widgets.table.threaded.ThreadedTableModel;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
@@ -49,210 +48,201 @@ public class GhidraTableFilterPanel<ROW_OBJECT> extends GTableFilterPanel<ROW_OB
|
||||
super(table, tableModel, filterLabel);
|
||||
}
|
||||
|
||||
@Override
|
||||
// overridden because of our knowledge of threaded models and program table models
|
||||
@Override // overridden because of our knowledge of program table models
|
||||
protected RowObjectFilterModel<ROW_OBJECT> createTextFilterModel(
|
||||
RowObjectTableModel<ROW_OBJECT> model) {
|
||||
RowObjectTableModel<ROW_OBJECT> clientModel) {
|
||||
|
||||
// NOTE: order is important here, since ThreadedTableModels can also be sorted table
|
||||
// models. We want to handle those first!
|
||||
RowObjectFilterModel<ROW_OBJECT> newModel = super.createTextFilterModel(model);
|
||||
if (newModel instanceof ThreadedTableModel<?, ?>) {
|
||||
// we don't wrap Threaded models, as they can do their own filtering
|
||||
return newModel;
|
||||
RowObjectFilterModel<ROW_OBJECT> filterModel = super.createTextFilterModel(clientModel);
|
||||
if (!(clientModel instanceof ProgramTableModel)) {
|
||||
return filterModel; // nothing to do
|
||||
}
|
||||
|
||||
// Next, see if we need to create a wrapper to handle ProgramTableModel implementations
|
||||
if (!(model instanceof ProgramTableModel)) {
|
||||
return newModel; // nope, the given model is not a ProgramTableModel; no new
|
||||
if (filterModel instanceof ProgramTableModel) {
|
||||
// the model is already filterable and a ProgramTableModel (e.g., a ThreadedTableModel)
|
||||
return filterModel;
|
||||
}
|
||||
|
||||
return new ProgramTableModelWrapperWrapper(newModel, newModel);
|
||||
return new FilterModelToProgramModelAdapter(filterModel, (ProgramTableModel) clientModel);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
||||
private class ProgramTableModelWrapperWrapper
|
||||
implements RowObjectFilterModel<ROW_OBJECT>, ProgramTableModel {
|
||||
/**
|
||||
* A class to adapt a table model that can filter into a {@link ProgramTableModel}.
|
||||
*/
|
||||
private class FilterModelToProgramModelAdapter
|
||||
implements RowObjectFilterModel<ROW_OBJECT>, ProgramTableModel, WrappingTableModel {
|
||||
|
||||
private final RowObjectFilterModel<ROW_OBJECT> wrappedFilterModel;
|
||||
private final RowObjectFilterModel<ROW_OBJECT> wrappedTableModel;
|
||||
private final RowObjectFilterModel<ROW_OBJECT> filterModel;
|
||||
private final ProgramTableModel programModel;
|
||||
|
||||
private ProgramTableModelWrapperWrapper(RowObjectFilterModel<ROW_OBJECT> tableModel,
|
||||
RowObjectFilterModel<ROW_OBJECT> filterModel) {
|
||||
this.wrappedTableModel = tableModel;
|
||||
this.wrappedFilterModel = filterModel;
|
||||
private FilterModelToProgramModelAdapter(RowObjectFilterModel<ROW_OBJECT> tableModel,
|
||||
ProgramTableModel programModel) {
|
||||
this.filterModel = tableModel;
|
||||
this.programModel = programModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return wrappedTableModel.getName();
|
||||
return filterModel.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fireTableChanged(TableModelEvent event) {
|
||||
if (filterModel instanceof WrappingTableModel) {
|
||||
((WrappingTableModel) filterModel).fireTableChanged(event);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void wrappedModelChangedFromTableChangedEvent() {
|
||||
if (filterModel instanceof WrappingTableModel) {
|
||||
((WrappingTableModel) filterModel).wrappedModelChangedFromTableChangedEvent();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ROW_OBJECT getRowObject(int row) {
|
||||
return wrappedTableModel.getRowObject(row);
|
||||
return filterModel.getRowObject(row);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ROW_OBJECT> getModelData() {
|
||||
return wrappedTableModel.getModelData();
|
||||
return filterModel.getModelData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getModelRow(int viewRow) {
|
||||
return wrappedFilterModel.getModelRow(viewRow);
|
||||
return filterModel.getModelRow(viewRow);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowIndex(ROW_OBJECT t) {
|
||||
return wrappedFilterModel.getRowIndex(t);
|
||||
return filterModel.getRowIndex(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getViewIndex(ROW_OBJECT t) {
|
||||
return wrappedFilterModel.getViewIndex(t);
|
||||
return filterModel.getViewIndex(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getModelIndex(ROW_OBJECT t) {
|
||||
return wrappedFilterModel.getModelIndex(t);
|
||||
return filterModel.getModelIndex(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUnfilteredRowCount() {
|
||||
return wrappedFilterModel.getUnfilteredRowCount();
|
||||
return filterModel.getUnfilteredRowCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getViewRow(int modelRow) {
|
||||
return wrappedFilterModel.getViewRow(modelRow);
|
||||
return filterModel.getViewRow(modelRow);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFiltered() {
|
||||
return wrappedFilterModel.isFiltered();
|
||||
return filterModel.isFiltered();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTableFilter(TableFilter<ROW_OBJECT> tableFilter) {
|
||||
wrappedFilterModel.setTableFilter(tableFilter);
|
||||
filterModel.setTableFilter(tableFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableFilter<ROW_OBJECT> getTableFilter() {
|
||||
return wrappedFilterModel.getTableFilter();
|
||||
return filterModel.getTableFilter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fireTableDataChanged() {
|
||||
wrappedTableModel.fireTableDataChanged();
|
||||
filterModel.fireTableDataChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTableModelListener(TableModelListener l) {
|
||||
wrappedTableModel.addTableModelListener(l);
|
||||
filterModel.addTableModelListener(l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeTableModelListener(TableModelListener l) {
|
||||
wrappedTableModel.removeTableModelListener(l);
|
||||
filterModel.removeTableModelListener(l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getColumnClass(int columnIndex) {
|
||||
return wrappedTableModel.getColumnClass(columnIndex);
|
||||
return filterModel.getColumnClass(columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return wrappedTableModel.getColumnCount();
|
||||
return filterModel.getColumnCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int columnIndex) {
|
||||
return wrappedTableModel.getColumnName(columnIndex);
|
||||
return filterModel.getColumnName(columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return wrappedTableModel.getRowCount();
|
||||
return filterModel.getRowCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ROW_OBJECT> getUnfilteredData() {
|
||||
List<ROW_OBJECT> list = new ArrayList<>();
|
||||
int rowCount = getUnfilteredRowCount();
|
||||
for (int i = 0; i < rowCount; i++) {
|
||||
list.add(wrappedTableModel.getRowObject(i));
|
||||
}
|
||||
return list;
|
||||
return filterModel.getUnfilteredData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getColumnValueForRow(ROW_OBJECT t, int columnIndex) {
|
||||
return wrappedTableModel.getColumnValueForRow(t, columnIndex);
|
||||
return filterModel.getColumnValueForRow(t, columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||
return wrappedTableModel.getValueAt(rowIndex, columnIndex);
|
||||
return filterModel.getValueAt(rowIndex, columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCellEditable(int rowIndex, int columnIndex) {
|
||||
return wrappedTableModel.isCellEditable(rowIndex, columnIndex);
|
||||
return filterModel.isCellEditable(rowIndex, columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueAt(Object value, int rowIndex, int columnIndex) {
|
||||
wrappedTableModel.setValueAt(value, rowIndex, columnIndex);
|
||||
filterModel.setValueAt(value, rowIndex, columnIndex);
|
||||
}
|
||||
|
||||
private TableModel getBaseTableModel() {
|
||||
if (wrappedTableModel instanceof TableModelWrapper<?>) {
|
||||
TableModelWrapper<ROW_OBJECT> tableModelWrapper =
|
||||
(TableModelWrapper<ROW_OBJECT>) wrappedTableModel;
|
||||
return tableModelWrapper.getWrappedModel();
|
||||
}
|
||||
return wrappedTableModel;
|
||||
}
|
||||
|
||||
private ProgramTableModel getProgramTableModel() {
|
||||
TableModel baseTableModel = getBaseTableModel();
|
||||
if (baseTableModel instanceof ProgramTableModel) {
|
||||
return (ProgramTableModel) baseTableModel;
|
||||
}
|
||||
return null;
|
||||
@Override
|
||||
public TableModel getWrappedModel() {
|
||||
return filterModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Program getProgram() {
|
||||
ProgramTableModel programTableModel = getProgramTableModel();
|
||||
if (programTableModel != null) {
|
||||
return programTableModel.getProgram();
|
||||
}
|
||||
return null;
|
||||
return programModel.getProgram();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramLocation getProgramLocation(int row, int column) {
|
||||
ProgramTableModel programTableModel = getProgramTableModel();
|
||||
if (programTableModel != null) {
|
||||
return programTableModel.getProgramLocation(row, column);
|
||||
}
|
||||
return null;
|
||||
// Note: the given row is the filtered row index
|
||||
int unfilteredRow = getModelRow(row);
|
||||
return programModel.getProgramLocation(unfilteredRow, column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramSelection getProgramSelection(int[] rows) {
|
||||
ProgramTableModel programTableModel = getProgramTableModel();
|
||||
if (programTableModel != null) {
|
||||
return programTableModel.getProgramSelection(rows);
|
||||
// Note: the given rows are the filtered row indexes
|
||||
int[] unfilteredRows = new int[rows.length];
|
||||
for (int i = 0; i < rows.length; i++) {
|
||||
unfilteredRows[i] = getModelRow(rows[i]);
|
||||
}
|
||||
return null;
|
||||
return programModel.getProgramSelection(unfilteredRows);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -514,10 +514,10 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
||||
RowObjectFilterModel<ROW_OBJECT> newModel = createTextFilterModel(currentModel);
|
||||
|
||||
// only wrapped models are set on tables, since they have to replace the original
|
||||
if (newModel instanceof TableModelWrapper) {
|
||||
if (newModel instanceof WrappingTableModel) {
|
||||
table.setModel(newModel);
|
||||
|
||||
TableModelWrapper<ROW_OBJECT> wrapper = (TableModelWrapper<ROW_OBJECT>) newModel;
|
||||
WrappingTableModel wrapper = (WrappingTableModel) newModel;
|
||||
currentModel.addTableModelListener(new TranslatingTableModelListener(wrapper));
|
||||
}
|
||||
|
||||
@@ -527,7 +527,6 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
||||
return newModel;
|
||||
}
|
||||
|
||||
// Cast from ThreadedTableModel...
|
||||
protected RowObjectFilterModel<ROW_OBJECT> createTextFilterModel(
|
||||
RowObjectTableModel<ROW_OBJECT> model) {
|
||||
RowObjectFilterModel<ROW_OBJECT> newModel = null;
|
||||
@@ -894,9 +893,9 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
||||
*/
|
||||
private class TranslatingTableModelListener implements TableModelListener {
|
||||
|
||||
private TableModelWrapper<ROW_OBJECT> tableModelWrapper;
|
||||
private WrappingTableModel tableModelWrapper;
|
||||
|
||||
TranslatingTableModelListener(TableModelWrapper<ROW_OBJECT> tableModelWrapper) {
|
||||
TranslatingTableModelListener(WrappingTableModel tableModelWrapper) {
|
||||
this.tableModelWrapper = tableModelWrapper;
|
||||
}
|
||||
|
||||
@@ -907,7 +906,7 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
||||
// so that the indices used in the event are correct for the filtered state of the
|
||||
// view.
|
||||
//
|
||||
tableModelWrapper.fireTableDataChanged(translateEventForFilter(e));
|
||||
tableModelWrapper.fireTableChanged(translateEventForFilter(e));
|
||||
}
|
||||
|
||||
private TableModelEvent translateEventForFilter(TableModelEvent event) {
|
||||
@@ -948,9 +947,8 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
||||
}
|
||||
|
||||
isUpdatingModel = true;
|
||||
if (textFilterModel instanceof TableModelWrapper) {
|
||||
TableModelWrapper<ROW_OBJECT> tableModelWrapper =
|
||||
(TableModelWrapper<ROW_OBJECT>) textFilterModel;
|
||||
if (textFilterModel instanceof WrappingTableModel) {
|
||||
WrappingTableModel tableModelWrapper = (WrappingTableModel) textFilterModel;
|
||||
tableModelWrapper.wrappedModelChangedFromTableChangedEvent();
|
||||
}
|
||||
filterField.alert();
|
||||
|
||||
@@ -258,10 +258,6 @@ public final class GTableToCSV {
|
||||
RowObjectFilterModel<?> threadedModel = (RowObjectFilterModel<?>) model;
|
||||
return threadedModel.getModelRow(viewRow);
|
||||
}
|
||||
else if (model instanceof TableModelWrapper) {
|
||||
TableModelWrapper<?> wrapper = (TableModelWrapper<?>) model;
|
||||
return wrapper.getModelRow(viewRow);
|
||||
}
|
||||
return viewRow; // assume no filtering, as we don't know how to handle it anyway
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -31,8 +31,8 @@ public interface RowObjectTableModel<T> extends TableModel {
|
||||
public static TableModel unwrap(TableModel m) {
|
||||
|
||||
TableModel model = m;
|
||||
while (model instanceof TableModelWrapper) {
|
||||
model = ((TableModelWrapper<?>) model).getWrappedModel();
|
||||
while (model instanceof WrappingTableModel) {
|
||||
model = ((WrappingTableModel) model).getWrappedModel();
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
+38
-7
@@ -20,8 +20,11 @@ import java.util.List;
|
||||
|
||||
import javax.swing.event.TableModelEvent;
|
||||
import javax.swing.event.TableModelListener;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import javax.swing.table.TableModel;
|
||||
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.datastruct.WeakDataStructureFactory;
|
||||
import ghidra.util.datastruct.WeakSet;
|
||||
|
||||
@@ -32,8 +35,10 @@ import ghidra.util.datastruct.WeakSet;
|
||||
* @param <ROW_OBJECT> the row object type
|
||||
*/
|
||||
public class TableModelWrapper<ROW_OBJECT>
|
||||
implements RowObjectFilterModel<ROW_OBJECT>, SelectionStorage<ROW_OBJECT> {
|
||||
implements RowObjectFilterModel<ROW_OBJECT>, SelectionStorage<ROW_OBJECT>,
|
||||
WrappingTableModel {
|
||||
|
||||
// The index in the list is the view index; the Integer value at that index is the model index
|
||||
protected List<Integer> filteredIndexList;
|
||||
private TableFilter<ROW_OBJECT> tableFilter;
|
||||
|
||||
@@ -45,7 +50,12 @@ public class TableModelWrapper<ROW_OBJECT>
|
||||
|
||||
public TableModelWrapper(RowObjectTableModel<ROW_OBJECT> wrappedModel) {
|
||||
this.wrappedModel = wrappedModel;
|
||||
|
||||
SystemUtilities.assertTrue(!(wrappedModel instanceof WrappingTableModel),
|
||||
"Attempted to wrap a table model that has already been wrapped");
|
||||
|
||||
filteredIndexList = getMatchingFilteredIndices();
|
||||
copyListeners();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -96,13 +106,23 @@ public class TableModelWrapper<ROW_OBJECT>
|
||||
return -1; // asking for a row that has been filtered out
|
||||
}
|
||||
|
||||
@Override
|
||||
public void wrappedModelChangedFromTableChangedEvent() {
|
||||
// note: don't call notifyTableModelChanged(), as we get this callback during a
|
||||
// tableChanged() event and we do not want to trigger another event.
|
||||
updateFilterIndices();
|
||||
}
|
||||
|
||||
public void fireTableDataChanged(TableModelEvent event) {
|
||||
@Override
|
||||
public void fireTableChanged(TableModelEvent event) {
|
||||
for (TableModelListener listener : listeners) {
|
||||
listener.tableChanged(event);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fireTableDataChanged() {
|
||||
TableModelEvent event = new TableModelEvent(this);
|
||||
for (TableModelListener listener : listeners) {
|
||||
listener.tableChanged(event);
|
||||
}
|
||||
@@ -115,6 +135,21 @@ public class TableModelWrapper<ROW_OBJECT>
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
private void copyListeners() {
|
||||
if (!(wrappedModel instanceof AbstractTableModel)) {
|
||||
Msg.warn(this, "Wrapping a table model that does not give access to listeners. You " +
|
||||
"should change your base table model implement a known class");
|
||||
return;
|
||||
}
|
||||
|
||||
AbstractTableModel abstractModel = (AbstractTableModel) wrappedModel;
|
||||
TableModelListener[] wrappedListeners =
|
||||
abstractModel.getListeners(TableModelListener.class);
|
||||
for (TableModelListener listener : wrappedListeners) {
|
||||
addTableModelListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateFilterIndices() {
|
||||
filteredIndexList = getMatchingFilteredIndices();
|
||||
}
|
||||
@@ -141,11 +176,6 @@ public class TableModelWrapper<ROW_OBJECT>
|
||||
return accepts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fireTableDataChanged() {
|
||||
wrappedModel.fireTableDataChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFiltered() {
|
||||
return filteredIndexList.size() != wrappedModel.getRowCount();
|
||||
@@ -276,6 +306,7 @@ public class TableModelWrapper<ROW_OBJECT>
|
||||
wrappedModel.setValueAt(value, modelRowIndex, columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableModel getWrappedModel() {
|
||||
return wrappedModel;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/* ###
|
||||
* 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.widgets.table;
|
||||
|
||||
import javax.swing.event.TableModelEvent;
|
||||
import javax.swing.table.TableModel;
|
||||
|
||||
/**
|
||||
* Signals that the implementing table model is wrapping another table model.
|
||||
*/
|
||||
public interface WrappingTableModel extends TableModel {
|
||||
|
||||
/**
|
||||
* Returns the wrapped model
|
||||
* @return the model
|
||||
*/
|
||||
public TableModel getWrappedModel();
|
||||
|
||||
/**
|
||||
* Allows this wrapping model to get update notifications directly from the filtering framework
|
||||
*/
|
||||
public void wrappedModelChangedFromTableChangedEvent();
|
||||
|
||||
/**
|
||||
* This method allows us to call the delegate model with a translated event
|
||||
* @param e the event
|
||||
*/
|
||||
public void fireTableChanged(TableModelEvent e);
|
||||
}
|
||||
Reference in New Issue
Block a user