mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
GP-4410 - Version Tracking - Added support for deleting matches; Added table column filters
This commit is contained in:
parent
76977bd514
commit
9f73d23ee4
36 changed files with 1335 additions and 699 deletions
|
@ -24,29 +24,21 @@ import javax.swing.*;
|
|||
import javax.swing.border.BevelBorder;
|
||||
import javax.swing.event.*;
|
||||
import javax.swing.table.TableColumnModel;
|
||||
import javax.swing.table.TableModel;
|
||||
|
||||
import org.jdom.Element;
|
||||
|
||||
import docking.DockingWindowManager;
|
||||
import docking.menu.*;
|
||||
import docking.widgets.EmptyBorderButton;
|
||||
import docking.widgets.EventTrigger;
|
||||
import docking.widgets.filter.*;
|
||||
import docking.widgets.label.GDLabel;
|
||||
import docking.widgets.table.columnfilter.ColumnBasedTableFilter;
|
||||
import docking.widgets.table.columnfilter.ColumnFilterSaveManager;
|
||||
import docking.widgets.table.constraint.dialog.ColumnFilterDialog;
|
||||
import generic.theme.GIcon;
|
||||
import docking.widgets.table.columnfilter.ColumnFilterManager;
|
||||
import ghidra.framework.options.PreferenceState;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.datastruct.WeakDataStructureFactory;
|
||||
import ghidra.util.datastruct.WeakSet;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import ghidra.util.task.SwingUpdateManager;
|
||||
import help.HelpService;
|
||||
import resources.Icons;
|
||||
import utilities.util.reflection.ReflectionUtilities;
|
||||
import utility.function.Callback;
|
||||
|
||||
|
@ -112,37 +104,26 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
|||
public static final String FILTER_TEXTFIELD_NAME = "filter.panel.textfield";
|
||||
private static final String FILTER_STATE = "FILTER_STATE";
|
||||
private static final String FILTER_EXTENSION = ".FilterExtension";
|
||||
private static final Icon FILTER_ON_ICON = new GIcon("icon.widget.filterpanel.filter.on");
|
||||
private static final Icon FILTER_OFF_ICON = new GIcon("icon.widget.filterpanel.filter.off");
|
||||
private static final Icon APPLY_FILTER_ICON = Icons.OPEN_FOLDER_ICON;
|
||||
private static final Icon CLEAR_FILTER_ICON = Icons.DELETE_ICON;
|
||||
|
||||
private JTable table;
|
||||
private RowObjectFilterModel<ROW_OBJECT> textFilterModel;
|
||||
private RowObjectFilterModel<ROW_OBJECT> rowObjectFilterModel;
|
||||
private JLabel searchLabel;
|
||||
|
||||
private FilterTextField filterField;
|
||||
private FilterListener filterListener = new GTableFilterListener();
|
||||
|
||||
private WeakSet<Callback> listeners =
|
||||
WeakDataStructureFactory.createSingleThreadAccessWeakSet();
|
||||
|
||||
private FilterOptions filterOptions = new FilterOptions();
|
||||
private TableTextFilterFactory<ROW_OBJECT> filterFactory =
|
||||
new DefaultTableTextFilterFactory<>(filterOptions);
|
||||
private RowFilterTransformer<ROW_OBJECT> transformer;
|
||||
private TableFilter<ROW_OBJECT> secondaryTableFilter;
|
||||
private ColumnBasedTableFilter<ROW_OBJECT> columnTableFilter;
|
||||
private List<ColumnBasedTableFilter<ROW_OBJECT>> savedFilters = new ArrayList<>();
|
||||
private EmptyBorderButton filterStateButton;
|
||||
|
||||
private ColumnFilterManager<ROW_OBJECT> columnFilterManager;
|
||||
|
||||
private String uniquePreferenceKey;
|
||||
|
||||
private MultiStateDockingAction<ColumnBasedTableFilter<ROW_OBJECT>> columnFilterAction;
|
||||
private ColumnFilterDialog<ROW_OBJECT> columnFilterDialog;
|
||||
private ColumnBasedTableFilter<ROW_OBJECT> lastUsedColumnFilter;
|
||||
|
||||
private SwingUpdateManager updateManager = new SwingUpdateManager(250, 1000, () -> {
|
||||
private SwingUpdateManager filterUpdater = new SwingUpdateManager(250, 1000, () -> {
|
||||
String text = filterField.getText();
|
||||
TableFilter<ROW_OBJECT> tableFilter = filterFactory.getTableFilter(text, transformer);
|
||||
|
||||
|
@ -151,8 +132,9 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
|||
// result of a filter, the table does not know this and may update the wrong row data.
|
||||
table.editingCanceled(null);
|
||||
|
||||
textFilterModel.setTableFilter(
|
||||
getCombinedTableFilter(secondaryTableFilter, tableFilter, columnTableFilter));
|
||||
ColumnBasedTableFilter<ROW_OBJECT> columnFilter = columnFilterManager.getCurrentFilter();
|
||||
rowObjectFilterModel.setTableFilter(
|
||||
getCombinedTableFilter(secondaryTableFilter, tableFilter, columnFilter));
|
||||
});
|
||||
|
||||
/** I'm a field so that my weak reference won't go away */
|
||||
|
@ -174,12 +156,12 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
|||
|
||||
@Override
|
||||
public void columnRemoved(TableColumnModelEvent e) {
|
||||
updateTableContents();
|
||||
filterUpdater.updateLater();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void columnAdded(TableColumnModelEvent e) {
|
||||
updateTableContents();
|
||||
filterUpdater.updateLater();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -231,13 +213,16 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
|||
String filterLabel) {
|
||||
this.table = table;
|
||||
|
||||
buildPanel(filterLabel);
|
||||
|
||||
uniquePreferenceKey = createUniqueFilterPreferenceKey(table);
|
||||
|
||||
transformer = new DefaultRowFilterTransformer<>(tableModel, table.getColumnModel());
|
||||
|
||||
textFilterModel = installTableModel(tableModel);
|
||||
rowObjectFilterModel = installTableModel(tableModel);
|
||||
|
||||
columnFilterManager = new ColumnFilterManager<ROW_OBJECT>(table, rowObjectFilterModel,
|
||||
getPreferenceKey(), filterUpdater::updateLater);
|
||||
|
||||
buildPanel(filterLabel);
|
||||
|
||||
TableColumnModel columnModel = table.getColumnModel();
|
||||
columnModel.addColumnModelListener(columnModelListener);
|
||||
|
@ -246,12 +231,7 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
|||
table.addPropertyChangeListener(badProgrammingPropertyChangeListener);
|
||||
|
||||
DockingWindowManager.registerComponentLoadedListener(this,
|
||||
(windowManager, provider) -> initialize(windowManager));
|
||||
}
|
||||
|
||||
private void initialize(DockingWindowManager windowManager) {
|
||||
loadFilterPreference(windowManager);
|
||||
initializeSavedFilters();
|
||||
(windowManager, provider) -> loadFilterPreference(windowManager));
|
||||
}
|
||||
|
||||
private void loadFilterPreference(DockingWindowManager dockingWindowManager) {
|
||||
|
@ -283,7 +263,7 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
|||
if (xmlElement != null) {
|
||||
this.filterOptions = FilterOptions.restoreFromXML(xmlElement);
|
||||
updateFilterFactory();
|
||||
updateTableContents();
|
||||
filterUpdater.updateLater();
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -326,18 +306,7 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
|||
* @param newFilter the ColumnTableFilter to use for filtering this table.
|
||||
*/
|
||||
public void setColumnTableFilter(ColumnBasedTableFilter<ROW_OBJECT> newFilter) {
|
||||
if (Objects.equals(newFilter, this.columnTableFilter)) {
|
||||
return;
|
||||
}
|
||||
if (columnTableFilter != null && !columnTableFilter.isSaved()) {
|
||||
lastUsedColumnFilter = columnTableFilter;
|
||||
}
|
||||
columnTableFilter = newFilter;
|
||||
updateTableContents();
|
||||
updateColumnFilterButton();
|
||||
if (columnFilterDialog != null) {
|
||||
columnFilterDialog.filterChanged(newFilter);
|
||||
}
|
||||
columnFilterManager.setFilter(newFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -351,7 +320,7 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
|||
*/
|
||||
public void setFilterRowTransformer(RowFilterTransformer<ROW_OBJECT> transformer) {
|
||||
this.transformer = transformer;
|
||||
updateTableContents();
|
||||
filterUpdater.updateLater();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -362,7 +331,7 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
|||
*/
|
||||
public void setSecondaryFilter(TableFilter<ROW_OBJECT> tableFilter) {
|
||||
this.secondaryTableFilter = tableFilter;
|
||||
updateTableContents();
|
||||
filterUpdater.updateLater();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -373,7 +342,7 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
|||
public void setFilterOptions(FilterOptions filterOptions) {
|
||||
this.filterOptions = filterOptions;
|
||||
updateFilterFactory();
|
||||
updateTableContents();
|
||||
filterUpdater.updateLater();
|
||||
doSaveState();
|
||||
}
|
||||
|
||||
|
@ -394,7 +363,7 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
|||
add(buildFilterStateButton());
|
||||
if (isTableColumnFilterableModel()) {
|
||||
add(Box.createHorizontalStrut(5));
|
||||
add(buildColumnFilterStateButton());
|
||||
add(columnFilterManager.getConfigureButton());
|
||||
}
|
||||
|
||||
HelpService helpService = DockingWindowManager.getHelpService();
|
||||
|
@ -425,107 +394,6 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
|||
return table.getModel() instanceof RowObjectFilterModel;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private JComponent buildColumnFilterStateButton() {
|
||||
|
||||
RowObjectFilterModel<ROW_OBJECT> tableModel =
|
||||
(RowObjectFilterModel<ROW_OBJECT>) table.getModel();
|
||||
columnFilterAction =
|
||||
new NonToolbarMultiStateAction<>("Column Filter", "GTableFilterPanel") {
|
||||
|
||||
@Override
|
||||
public void actionStateChanged(
|
||||
ActionState<ColumnBasedTableFilter<ROW_OBJECT>> newActionState,
|
||||
EventTrigger trigger) {
|
||||
if (trigger != EventTrigger.GUI_ACTION) {
|
||||
return;
|
||||
}
|
||||
ColumnFilterActionState state = (ColumnFilterActionState) newActionState;
|
||||
state.performAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void actionPerformed() {
|
||||
showFilterDialog(tableModel);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
HelpLocation helpLocation = new HelpLocation("Trees", "Column_Filters");
|
||||
columnFilterAction.setHelpLocation(helpLocation);
|
||||
|
||||
updateFilterFactory();
|
||||
updateColumnFilterButton();
|
||||
JButton button = columnFilterAction.createButton();
|
||||
DockingWindowManager.getHelpService().registerHelp(button, helpLocation);
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
private void initializeSavedFilters() {
|
||||
TableModel model = table.getModel();
|
||||
if (!(model instanceof GDynamicColumnTableModel)) {
|
||||
return;
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
GDynamicColumnTableModel<ROW_OBJECT, ?> dynamicModel =
|
||||
(GDynamicColumnTableModel<ROW_OBJECT, ?>) model;
|
||||
|
||||
ColumnFilterSaveManager<ROW_OBJECT> saveManager =
|
||||
new ColumnFilterSaveManager<>(this, table, dynamicModel, dynamicModel.getDataSource());
|
||||
savedFilters = saveManager.getSavedFilters();
|
||||
Collections.reverse(savedFilters);
|
||||
updateColumnFilterButton();
|
||||
}
|
||||
|
||||
private void updateColumnFilterButton() {
|
||||
List<ActionState<ColumnBasedTableFilter<ROW_OBJECT>>> list = getActionStates();
|
||||
|
||||
columnFilterAction.setActionStates(list);
|
||||
}
|
||||
|
||||
private List<ActionState<ColumnBasedTableFilter<ROW_OBJECT>>> getActionStates() {
|
||||
List<ActionState<ColumnBasedTableFilter<ROW_OBJECT>>> list = new ArrayList<>();
|
||||
if (columnTableFilter == null) {
|
||||
list.add(new CreateFilterActionState());
|
||||
}
|
||||
else {
|
||||
list.add(new EditFilterActionState(columnTableFilter));
|
||||
list.add(new ClearFilterActionState());
|
||||
}
|
||||
if (lastUsedColumnFilter != null) {
|
||||
list.add(new ApplyLastUsedActionState(lastUsedColumnFilter));
|
||||
}
|
||||
for (ColumnBasedTableFilter<ROW_OBJECT> filter : savedFilters) {
|
||||
list.add(new ApplyFilterActionState(filter));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private void showFilterDialog(RowObjectFilterModel<ROW_OBJECT> tableModel) {
|
||||
if (columnFilterDialog == null) {
|
||||
if (ColumnFilterDialog.hasFilterableColumns(table, tableModel)) {
|
||||
DockingWindowManager dockingWindowManager = DockingWindowManager.getInstance(table);
|
||||
loadFilterPreference(dockingWindowManager);
|
||||
columnFilterDialog = new ColumnFilterDialog<>(this, table, tableModel);
|
||||
}
|
||||
else {
|
||||
Msg.showError(this, this, "Column Filter Error",
|
||||
"This table contains no filterable columns!");
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
columnFilterDialog.setCloseCallback(() -> {
|
||||
doSaveState();
|
||||
updateFilterFactory();
|
||||
columnFilterDialog = null;
|
||||
});
|
||||
|
||||
DockingWindowManager.showDialog(GTableFilterPanel.this, columnFilterDialog);
|
||||
}
|
||||
|
||||
private void updateFilterFactory() {
|
||||
filterStateButton.setIcon(filterOptions.getFilterStateIcon());
|
||||
filterStateButton.setToolTipText(filterOptions.getFilterDescription());
|
||||
|
@ -587,17 +455,7 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
|||
}
|
||||
|
||||
public RowObjectFilterModel<ROW_OBJECT> getTableFilterModel() {
|
||||
return textFilterModel;
|
||||
}
|
||||
|
||||
/** Convenience method to refilter the table's contents */
|
||||
private void updateTableContents() {
|
||||
updateManager.updateLater();
|
||||
notifyFilterChanged();
|
||||
}
|
||||
|
||||
private void notifyFilterChanged() {
|
||||
listeners.forEach(callback -> callback.call());
|
||||
return rowObjectFilterModel;
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
|
@ -609,13 +467,11 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
|||
columnModel.removeColumnModelListener(columnModelListener);
|
||||
columnModelListener = null;
|
||||
|
||||
if (columnFilterDialog != null) {
|
||||
columnFilterDialog.dispose();
|
||||
}
|
||||
columnFilterManager.dispose();
|
||||
|
||||
table.removePropertyChangeListener(badProgrammingPropertyChangeListener);
|
||||
|
||||
updateManager.dispose();
|
||||
filterUpdater.dispose();
|
||||
if (table instanceof GTable) {
|
||||
((GTable) table).dispose();
|
||||
}
|
||||
|
@ -696,7 +552,7 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
|||
return viewRow;
|
||||
}
|
||||
|
||||
return textFilterModel.getModelRow(viewRow);
|
||||
return rowObjectFilterModel.getModelRow(viewRow);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -710,7 +566,7 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
|||
* @return the row in the table for the given model row.
|
||||
*/
|
||||
public int getViewRow(int modelRow) {
|
||||
return textFilterModel.getViewRow(modelRow);
|
||||
return rowObjectFilterModel.getViewRow(modelRow);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -720,7 +576,7 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
|||
* @return the row object matching the given index
|
||||
*/
|
||||
public ROW_OBJECT getRowObject(int viewRow) {
|
||||
ROW_OBJECT rowObject = textFilterModel.getRowObject(viewRow);
|
||||
ROW_OBJECT rowObject = rowObjectFilterModel.getRowObject(viewRow);
|
||||
return rowObject;
|
||||
}
|
||||
|
||||
|
@ -736,7 +592,7 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
|||
return;
|
||||
}
|
||||
|
||||
int viewRow = textFilterModel.getViewIndex(t);
|
||||
int viewRow = rowObjectFilterModel.getViewIndex(t);
|
||||
if (viewRow >= 0) {
|
||||
table.setRowSelectionInterval(viewRow, viewRow);
|
||||
scrollToSelectedRow();
|
||||
|
@ -785,7 +641,7 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
|||
if (row < 0) {
|
||||
return null;
|
||||
}
|
||||
return textFilterModel.getRowObject(row);
|
||||
return rowObjectFilterModel.getRowObject(row);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -801,7 +657,7 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
|||
|
||||
List<ROW_OBJECT> list = new ArrayList<>(rows.length);
|
||||
for (int row : rows) {
|
||||
list.add(textFilterModel.getRowObject(row));
|
||||
list.add(rowObjectFilterModel.getRowObject(row));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
@ -814,7 +670,7 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
|||
* @return true if in the view
|
||||
*/
|
||||
public boolean isInView(ROW_OBJECT o) {
|
||||
int rowIndex = textFilterModel.getRowIndex(o);
|
||||
int rowIndex = rowObjectFilterModel.getRowIndex(o);
|
||||
return rowIndex >= 0;
|
||||
}
|
||||
|
||||
|
@ -823,11 +679,48 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
|||
}
|
||||
|
||||
public int getRowCount() {
|
||||
return textFilterModel.getRowCount();
|
||||
return rowObjectFilterModel.getRowCount();
|
||||
}
|
||||
|
||||
public int getUnfilteredRowCount() {
|
||||
return textFilterModel.getUnfilteredRowCount();
|
||||
return rowObjectFilterModel.getUnfilteredRowCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a key used to store user filter configuration state. You can override this
|
||||
* method to generate unique keys yourself. You are required to override this method if
|
||||
* you create multiple versions of a filter panel from the same place in your code, as
|
||||
* multiple instances created in the same place will cause them all to share the same key and
|
||||
* thus to have the same filter settings when they are created initially.
|
||||
* <p>
|
||||
* As an example, consider a plugin that creates <code>n</code> providers. If each provider uses
|
||||
* a filter panel, then each provider will share the same filter settings when that provider
|
||||
* is created. If this is not what you want, then you need to override this method to
|
||||
* generate a unique key for each provider.
|
||||
*
|
||||
* @param jTable the table
|
||||
* @return a key used to store user filter configuration state.
|
||||
*/
|
||||
public String createUniqueFilterPreferenceKey(JTable jTable) {
|
||||
return generateFilterPreferenceKey(jTable, FILTER_EXTENSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ColumnTableFilter that has been set on this GTableFilterPanel or null if there
|
||||
* is none.
|
||||
*
|
||||
* @return the ColumnTableFilter that has been set.
|
||||
*/
|
||||
public ColumnBasedTableFilter<ROW_OBJECT> getColumnTableFilter() {
|
||||
return columnFilterManager.getCurrentFilter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a unique key that can be used to store preferences for this table.
|
||||
* @return a unique key that can be used to store preferences for this table.
|
||||
*/
|
||||
public String getPreferenceKey() {
|
||||
return uniquePreferenceKey;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
|
@ -900,7 +793,7 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
|||
}
|
||||
|
||||
private TableModelEvent translateEventForFilter(TableModelEvent event) {
|
||||
int rowCount = textFilterModel.getUnfilteredRowCount();
|
||||
int rowCount = rowObjectFilterModel.getUnfilteredRowCount();
|
||||
if (rowCount == 0) {
|
||||
return event; // nothing to translate--no data
|
||||
}
|
||||
|
@ -915,14 +808,14 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
|||
|
||||
if (firstRow == 0 && lastRow == rowCount - 1) {
|
||||
firstRow = 0;
|
||||
lastRow = Math.max(0, textFilterModel.getRowCount() - 1);
|
||||
lastRow = Math.max(0, rowObjectFilterModel.getRowCount() - 1);
|
||||
}
|
||||
else {
|
||||
// translate to the filtered view (from the wrapped model's full universe)
|
||||
firstRow = getViewRow(firstRow);
|
||||
lastRow = getViewRow(lastRow);
|
||||
}
|
||||
return new TableModelEvent(textFilterModel, firstRow, lastRow, event.getColumn(),
|
||||
return new TableModelEvent(rowObjectFilterModel, firstRow, lastRow, event.getColumn(),
|
||||
event.getType());
|
||||
}
|
||||
}
|
||||
|
@ -937,8 +830,8 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
|||
}
|
||||
|
||||
isUpdatingModel = true;
|
||||
if (textFilterModel instanceof WrappingTableModel) {
|
||||
WrappingTableModel tableModelWrapper = (WrappingTableModel) textFilterModel;
|
||||
if (rowObjectFilterModel instanceof WrappingTableModel) {
|
||||
WrappingTableModel tableModelWrapper = (WrappingTableModel) rowObjectFilterModel;
|
||||
tableModelWrapper.wrappedModelChangedFromTableChangedEvent();
|
||||
}
|
||||
filterField.alert();
|
||||
|
@ -947,75 +840,16 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
|||
}
|
||||
|
||||
private class GTableFilterListener implements FilterListener {
|
||||
|
||||
@Override
|
||||
public void filterChanged(String text) {
|
||||
updateTableContents();
|
||||
filterUpdater.updateLater();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a key used to store user filter configuration state. You can override this
|
||||
* method to generate unique keys yourself. You are required to override this method if
|
||||
* you create multiple versions of a filter panel from the same place in your code, as
|
||||
* multiple instances created in the same place will cause them all to share the same key and
|
||||
* thus to have the same filter settings when they are created initially.
|
||||
* <p>
|
||||
* As an example, consider a plugin that creates <code>n</code> providers. If each provider uses
|
||||
* a filter panel, then each provider will share the same filter settings when that provider
|
||||
* is created. If this is not what you want, then you need to override this method to
|
||||
* generate a unique key for each provider.
|
||||
*
|
||||
* @param jTable the table
|
||||
* @return a key used to store user filter configuration state.
|
||||
*/
|
||||
public String createUniqueFilterPreferenceKey(JTable jTable) {
|
||||
return generateFilterPreferenceKey(jTable, FILTER_EXTENSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ColumnTableFilter that has been set on this GTableFilterPanel or null if there
|
||||
* is none.
|
||||
*
|
||||
* @return the ColumnTableFilter that has been set.
|
||||
*/
|
||||
public ColumnBasedTableFilter<ROW_OBJECT> getColumnTableFilter() {
|
||||
return columnTableFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a unique key that can be used to store preferences for this table.
|
||||
* @return a unique key that can be used to store preferences for this table.
|
||||
*/
|
||||
public String getPreferenceKey() {
|
||||
return uniquePreferenceKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the "quick filter" multistate button.
|
||||
* @param filter the filter to add or remove.
|
||||
* @param add if true, the filter is added to the quick list. Otherwise, it is removed.
|
||||
*/
|
||||
public void updateSavedFilters(ColumnBasedTableFilter<ROW_OBJECT> filter, boolean add) {
|
||||
if (add) {
|
||||
ArrayList<ColumnBasedTableFilter<ROW_OBJECT>> list = new ArrayList<>();
|
||||
list.add(filter);
|
||||
list.addAll(savedFilters);
|
||||
savedFilters = list;
|
||||
if (filter.isEquivalent(columnTableFilter)) {
|
||||
setColumnTableFilter(filter);
|
||||
}
|
||||
}
|
||||
else {
|
||||
savedFilters.remove(filter);
|
||||
}
|
||||
|
||||
updateColumnFilterButton();
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Static Methods
|
||||
//==================================================================================================
|
||||
|
||||
private static String generateFilterPreferenceKey(JTable jTable, String extension) {
|
||||
|
||||
if (jTable instanceof GTable) {
|
||||
|
@ -1039,78 +873,4 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
|||
String clientName = filteredTrace[0].getClassName();
|
||||
return clientName;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
||||
private abstract class ColumnFilterActionState
|
||||
extends ActionState<ColumnBasedTableFilter<ROW_OBJECT>> {
|
||||
|
||||
ColumnFilterActionState(String name, Icon icon, ColumnBasedTableFilter<ROW_OBJECT> filter) {
|
||||
super(name, icon, filter);
|
||||
}
|
||||
|
||||
abstract void performAction();
|
||||
}
|
||||
|
||||
String getFilterName(ColumnBasedTableFilter<ROW_OBJECT> filter) {
|
||||
String filterName = filter.getName();
|
||||
return filterName == null ? "Unsaved" : filterName;
|
||||
}
|
||||
|
||||
private class ClearFilterActionState extends ColumnFilterActionState {
|
||||
public ClearFilterActionState() {
|
||||
super("Clear Filter", CLEAR_FILTER_ICON, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
void performAction() {
|
||||
setColumnTableFilter(null);
|
||||
}
|
||||
}
|
||||
|
||||
private class CreateFilterActionState extends ColumnFilterActionState {
|
||||
public CreateFilterActionState() {
|
||||
super("Create Column Filter", FILTER_OFF_ICON, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
void performAction() {
|
||||
showFilterDialog(textFilterModel);
|
||||
}
|
||||
}
|
||||
|
||||
private class EditFilterActionState extends ColumnFilterActionState {
|
||||
public EditFilterActionState(ColumnBasedTableFilter<ROW_OBJECT> filter) {
|
||||
super("Edit: " + getFilterName(filter), FILTER_ON_ICON, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
void performAction() {
|
||||
showFilterDialog(textFilterModel);
|
||||
}
|
||||
}
|
||||
|
||||
private class ApplyFilterActionState extends ColumnFilterActionState {
|
||||
public ApplyFilterActionState(ColumnBasedTableFilter<ROW_OBJECT> filter) {
|
||||
super("Apply: " + getFilterName(filter), APPLY_FILTER_ICON, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
void performAction() {
|
||||
setColumnTableFilter(getUserData());
|
||||
}
|
||||
}
|
||||
|
||||
private class ApplyLastUsedActionState extends ColumnFilterActionState {
|
||||
public ApplyLastUsedActionState(ColumnBasedTableFilter<ROW_OBJECT> filter) {
|
||||
super("Apply Last Unsaved", FILTER_ON_ICON, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
void performAction() {
|
||||
setColumnTableFilter(getUserData());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,298 @@
|
|||
/* ###
|
||||
* 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.columnfilter;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.table.TableModel;
|
||||
|
||||
import docking.DockingWindowManager;
|
||||
import docking.menu.*;
|
||||
import docking.widgets.EventTrigger;
|
||||
import docking.widgets.table.GDynamicColumnTableModel;
|
||||
import docking.widgets.table.RowObjectFilterModel;
|
||||
import docking.widgets.table.constraint.dialog.ColumnFilterDialog;
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.Msg;
|
||||
import resources.Icons;
|
||||
import utility.function.Callback;
|
||||
|
||||
/**
|
||||
* A class that manages column filters for a table. This includes creating the UI elements that
|
||||
* allow users to build filters, as well as a means to save and restore filters.
|
||||
*
|
||||
* @param <ROW_OBJECT> the row type
|
||||
*/
|
||||
public class ColumnFilterManager<ROW_OBJECT> {
|
||||
|
||||
public static final String FILTER_EXTENSION = ".FilterExtension";
|
||||
public static final String FILTER_TEXTFIELD_NAME = "filter.panel.textfield";
|
||||
private static final Icon FILTER_ON_ICON = new GIcon("icon.widget.filterpanel.filter.on");
|
||||
private static final Icon FILTER_OFF_ICON = new GIcon("icon.widget.filterpanel.filter.off");
|
||||
private static final Icon APPLY_FILTER_ICON = Icons.OPEN_FOLDER_ICON;
|
||||
private static final Icon CLEAR_FILTER_ICON = Icons.DELETE_ICON;
|
||||
|
||||
private MultiStateDockingAction<ColumnBasedTableFilter<ROW_OBJECT>> columnFilterAction;
|
||||
private JButton configureButton;
|
||||
private ColumnFilterDialog<ROW_OBJECT> columnFilterDialog;
|
||||
|
||||
private ColumnBasedTableFilter<ROW_OBJECT> lastUsedFilter;
|
||||
private ColumnBasedTableFilter<ROW_OBJECT> currentFilter;
|
||||
private List<ColumnBasedTableFilter<ROW_OBJECT>> savedFilters = new ArrayList<>();
|
||||
|
||||
private JTable table;
|
||||
private RowObjectFilterModel<ROW_OBJECT> rowObjectFilterModel;
|
||||
private String preferenceKey;
|
||||
private Callback filterChangedCallback;
|
||||
|
||||
public ColumnFilterManager(JTable table, RowObjectFilterModel<ROW_OBJECT> rowObjectFilterModel,
|
||||
String preferenceKey, Callback filterChangedCallback) {
|
||||
this.table = Objects.requireNonNull(table);
|
||||
this.rowObjectFilterModel = Objects.requireNonNull(rowObjectFilterModel);
|
||||
this.preferenceKey = Objects.requireNonNull(preferenceKey);
|
||||
this.filterChangedCallback = Objects.requireNonNull(filterChangedCallback);
|
||||
|
||||
configureButton = buildColumnFilterStateButton();
|
||||
|
||||
DockingWindowManager.registerComponentLoadedListener(table,
|
||||
(windowManager, provider) -> initializeSavedFilters());
|
||||
}
|
||||
|
||||
private void initializeSavedFilters() {
|
||||
TableModel model = table.getModel();
|
||||
if (!(model instanceof GDynamicColumnTableModel)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
GDynamicColumnTableModel<ROW_OBJECT, ?> dynamicModel =
|
||||
(GDynamicColumnTableModel<ROW_OBJECT, ?>) model;
|
||||
|
||||
ColumnFilterSaveManager<ROW_OBJECT> saveManager = new ColumnFilterSaveManager<>(
|
||||
preferenceKey, table, dynamicModel, dynamicModel.getDataSource());
|
||||
|
||||
savedFilters = saveManager.getSavedFilters();
|
||||
Collections.reverse(savedFilters);
|
||||
updateColumnFilterButton();
|
||||
}
|
||||
|
||||
public ColumnBasedTableFilter<ROW_OBJECT> getCurrentFilter() {
|
||||
return currentFilter;
|
||||
}
|
||||
|
||||
public JButton getConfigureButton() {
|
||||
return configureButton;
|
||||
}
|
||||
|
||||
public String getPreferenceKey() {
|
||||
return preferenceKey;
|
||||
}
|
||||
|
||||
public void setFilter(ColumnBasedTableFilter<ROW_OBJECT> newFilter) {
|
||||
if (Objects.equals(newFilter, this.currentFilter)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentFilter != null && !currentFilter.isSaved()) {
|
||||
lastUsedFilter = currentFilter;
|
||||
}
|
||||
currentFilter = newFilter;
|
||||
|
||||
updateColumnFilterButton();
|
||||
if (columnFilterDialog != null) {
|
||||
columnFilterDialog.filterChanged(newFilter);
|
||||
}
|
||||
|
||||
filterChangedCallback.call();
|
||||
}
|
||||
|
||||
public void updateSavedFilters(ColumnBasedTableFilter<ROW_OBJECT> filter, boolean add) {
|
||||
|
||||
if (add) {
|
||||
ArrayList<ColumnBasedTableFilter<ROW_OBJECT>> list = new ArrayList<>();
|
||||
list.add(filter);
|
||||
list.addAll(savedFilters);
|
||||
savedFilters = list;
|
||||
if (filter.isEquivalent(currentFilter)) {
|
||||
setFilter(filter);
|
||||
}
|
||||
}
|
||||
else {
|
||||
savedFilters.remove(filter);
|
||||
}
|
||||
|
||||
updateColumnFilterButton();
|
||||
|
||||
filterChangedCallback.call();
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
if (columnFilterDialog != null) {
|
||||
columnFilterDialog.dispose();
|
||||
columnFilterDialog = null;
|
||||
}
|
||||
|
||||
filterChangedCallback = Callback.dummy();
|
||||
}
|
||||
|
||||
private JButton buildColumnFilterStateButton() {
|
||||
|
||||
columnFilterAction =
|
||||
new NonToolbarMultiStateAction<>("Column Filter", "GTableFilterPanel") {
|
||||
|
||||
@Override
|
||||
public void actionStateChanged(
|
||||
ActionState<ColumnBasedTableFilter<ROW_OBJECT>> newActionState,
|
||||
EventTrigger trigger) {
|
||||
if (trigger != EventTrigger.GUI_ACTION) {
|
||||
return;
|
||||
}
|
||||
ColumnFilterActionState state = (ColumnFilterActionState) newActionState;
|
||||
state.performAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void actionPerformed() {
|
||||
showFilterDialog(rowObjectFilterModel);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
HelpLocation helpLocation = new HelpLocation("Trees", "Column_Filters");
|
||||
columnFilterAction.setHelpLocation(helpLocation);
|
||||
|
||||
updateColumnFilterButton();
|
||||
JButton button = columnFilterAction.createButton();
|
||||
DockingWindowManager.getHelpService().registerHelp(button, helpLocation);
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
private void updateColumnFilterButton() {
|
||||
List<ActionState<ColumnBasedTableFilter<ROW_OBJECT>>> list = getActionStates();
|
||||
columnFilterAction.setActionStates(list);
|
||||
}
|
||||
|
||||
private List<ActionState<ColumnBasedTableFilter<ROW_OBJECT>>> getActionStates() {
|
||||
List<ActionState<ColumnBasedTableFilter<ROW_OBJECT>>> list = new ArrayList<>();
|
||||
if (currentFilter == null) {
|
||||
list.add(new CreateFilterActionState());
|
||||
}
|
||||
else {
|
||||
list.add(new EditFilterActionState(currentFilter));
|
||||
list.add(new ClearFilterActionState());
|
||||
}
|
||||
if (lastUsedFilter != null) {
|
||||
list.add(new ApplyLastUsedActionState(lastUsedFilter));
|
||||
}
|
||||
for (ColumnBasedTableFilter<ROW_OBJECT> filter : savedFilters) {
|
||||
list.add(new ApplyFilterActionState(filter));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private void showFilterDialog(RowObjectFilterModel<ROW_OBJECT> tableModel) {
|
||||
if (columnFilterDialog == null) {
|
||||
if (ColumnFilterDialog.hasFilterableColumns(table, tableModel)) {
|
||||
columnFilterDialog = new ColumnFilterDialog<>(this, table, rowObjectFilterModel);
|
||||
}
|
||||
else {
|
||||
Msg.showError(this, null, "Column Filter Error",
|
||||
"This table contains no filterable columns!");
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DockingWindowManager.showDialog(table, columnFilterDialog);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
||||
private abstract class ColumnFilterActionState
|
||||
extends ActionState<ColumnBasedTableFilter<ROW_OBJECT>> {
|
||||
|
||||
ColumnFilterActionState(String name, Icon icon, ColumnBasedTableFilter<ROW_OBJECT> filter) {
|
||||
super(name, icon, filter);
|
||||
}
|
||||
|
||||
abstract void performAction();
|
||||
}
|
||||
|
||||
String getFilterName(ColumnBasedTableFilter<ROW_OBJECT> filter) {
|
||||
String filterName = filter.getName();
|
||||
return filterName == null ? "Unsaved" : filterName;
|
||||
}
|
||||
|
||||
private class ClearFilterActionState extends ColumnFilterActionState {
|
||||
public ClearFilterActionState() {
|
||||
super("Clear Filter", CLEAR_FILTER_ICON, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
void performAction() {
|
||||
setFilter(null);
|
||||
}
|
||||
}
|
||||
|
||||
private class CreateFilterActionState extends ColumnFilterActionState {
|
||||
public CreateFilterActionState() {
|
||||
super("Create Column Filter", FILTER_OFF_ICON, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
void performAction() {
|
||||
showFilterDialog(rowObjectFilterModel);
|
||||
}
|
||||
}
|
||||
|
||||
private class EditFilterActionState extends ColumnFilterActionState {
|
||||
public EditFilterActionState(ColumnBasedTableFilter<ROW_OBJECT> filter) {
|
||||
super("Edit: " + getFilterName(filter), FILTER_ON_ICON, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
void performAction() {
|
||||
showFilterDialog(rowObjectFilterModel);
|
||||
}
|
||||
}
|
||||
|
||||
private class ApplyFilterActionState extends ColumnFilterActionState {
|
||||
public ApplyFilterActionState(ColumnBasedTableFilter<ROW_OBJECT> filter) {
|
||||
super("Apply: " + getFilterName(filter), APPLY_FILTER_ICON, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
void performAction() {
|
||||
setFilter(getUserData());
|
||||
}
|
||||
}
|
||||
|
||||
private class ApplyLastUsedActionState extends ColumnFilterActionState {
|
||||
public ApplyLastUsedActionState(ColumnBasedTableFilter<ROW_OBJECT> filter) {
|
||||
super("Apply Last Unsaved", FILTER_ON_ICON, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
void performAction() {
|
||||
setFilter(getUserData());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,7 +23,6 @@ import javax.swing.JTable;
|
|||
import org.jdom.Element;
|
||||
|
||||
import docking.DockingWindowManager;
|
||||
import docking.widgets.table.GTableFilterPanel;
|
||||
import docking.widgets.table.RowObjectTableModel;
|
||||
import ghidra.framework.options.PreferenceState;
|
||||
import ghidra.framework.options.SaveState;
|
||||
|
@ -35,7 +34,7 @@ import ghidra.util.Msg;
|
|||
* @param <R> the row type of the table.
|
||||
*/
|
||||
public class ColumnFilterSaveManager<R> {
|
||||
private static final String COLUMN_FILTER_EXTENSION = ".ColumnFilterExtension";
|
||||
|
||||
private static final String COLUMN_FILTER_STATE = "COLUMN_FILTER_STATE";
|
||||
|
||||
private List<ColumnBasedTableFilter<R>> filters = new ArrayList<>();
|
||||
|
@ -46,14 +45,15 @@ public class ColumnFilterSaveManager<R> {
|
|||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param panel The GTableFilterPanel for the table.
|
||||
* @param tablePreferenceKey the key used to save table settings. This is used to make a
|
||||
* preference key for saving the column filters.
|
||||
* @param table The JTable that is filterable.
|
||||
* @param model the TableModel that supports filtering.
|
||||
* @param dataSource the table's DataSource object.
|
||||
*/
|
||||
public ColumnFilterSaveManager(GTableFilterPanel<R> panel, JTable table,
|
||||
public ColumnFilterSaveManager(String tablePreferenceKey, JTable table,
|
||||
RowObjectTableModel<R> model, Object dataSource) {
|
||||
preferenceKey = panel.getPreferenceKey() + COLUMN_FILTER_EXTENSION;
|
||||
preferenceKey = tablePreferenceKey + ColumnFilterManager.FILTER_EXTENSION;
|
||||
loadFromPreferences(table, model, dataSource);
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,6 @@ import docking.action.*;
|
|||
import docking.widgets.OptionDialog;
|
||||
import docking.widgets.dialogs.InputDialog;
|
||||
import docking.widgets.label.GLabel;
|
||||
import docking.widgets.table.GTableFilterPanel;
|
||||
import docking.widgets.table.RowObjectFilterModel;
|
||||
import docking.widgets.table.columnfilter.*;
|
||||
import docking.widgets.table.constrainteditor.ColumnConstraintEditor;
|
||||
|
@ -50,38 +49,38 @@ import utility.function.Callback;
|
|||
public class ColumnFilterDialog<R> extends ReusableDialogComponentProvider
|
||||
implements TableFilterDialogModelListener {
|
||||
|
||||
private final ColumnFilterDialogModel<R> filterModel;
|
||||
private ColumnFilterManager<R> filterManager;
|
||||
private ColumnFilterDialogModel<R> dialogModel;
|
||||
|
||||
private JTable table;
|
||||
private RowObjectFilterModel<R> tableModel;
|
||||
|
||||
private JPanel filterPanelContainer;
|
||||
private List<ColumnFilterPanel> filterPanels = new ArrayList<>();
|
||||
|
||||
private Callback closeCallback;
|
||||
private GTableFilterPanel<R> gTableFilterPanel;
|
||||
|
||||
private JPanel bottomPanel;
|
||||
|
||||
private JTable table;
|
||||
private RowObjectFilterModel<R> tableModel;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param gTableFilterPanel the GTableFilterPanel that launched this dialog.
|
||||
*
|
||||
* @param filterManager the filter manager
|
||||
* @param table the table being filtered.
|
||||
* @param tableModel the table model.
|
||||
*/
|
||||
public ColumnFilterDialog(GTableFilterPanel<R> gTableFilterPanel, JTable table,
|
||||
public ColumnFilterDialog(ColumnFilterManager<R> filterManager, JTable table,
|
||||
RowObjectFilterModel<R> tableModel) {
|
||||
super("Table Column Filters", WindowUtilities.areModalDialogsVisible(), true, true, false);
|
||||
this.gTableFilterPanel = gTableFilterPanel;
|
||||
this.filterManager = filterManager;
|
||||
this.table = table;
|
||||
this.tableModel = tableModel;
|
||||
|
||||
ColumnBasedTableFilter<R> columnTableFilter = gTableFilterPanel.getColumnTableFilter();
|
||||
ColumnBasedTableFilter<R> columnTableFilter = filterManager.getCurrentFilter();
|
||||
|
||||
filterModel =
|
||||
dialogModel =
|
||||
new ColumnFilterDialogModel<>(tableModel, table.getColumnModel(), columnTableFilter);
|
||||
filterModel.addListener(this);
|
||||
dialogModel.addListener(this);
|
||||
|
||||
setHelpLocation(new HelpLocation("Trees", "Column_Filters"));
|
||||
addWorkPanel(buildMainPanel());
|
||||
|
@ -97,10 +96,9 @@ public class ColumnFilterDialog<R> extends ReusableDialogComponentProvider
|
|||
updateStatus();
|
||||
}
|
||||
|
||||
public static <R> boolean hasFilterableColumns(JTable table,
|
||||
RowObjectFilterModel<R> model) {
|
||||
public static <R> boolean hasFilterableColumns(JTable table, RowObjectFilterModel<R> model) {
|
||||
return !ColumnFilterDialogModel.getAllColumnFilterData(model, table.getColumnModel())
|
||||
.isEmpty();
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
private void addClearFilterButton() {
|
||||
|
@ -119,7 +117,7 @@ public class ColumnFilterDialog<R> extends ReusableDialogComponentProvider
|
|||
DockingAction saveAction = new DockingAction("Save", "Filter") {
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
return !filterModel.getFilterRows().isEmpty() && filterModel.isValid();
|
||||
return !dialogModel.getFilterRows().isEmpty() && dialogModel.isValid();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -140,15 +138,15 @@ public class ColumnFilterDialog<R> extends ReusableDialogComponentProvider
|
|||
};
|
||||
loadAction.setDescription("Load Filter");
|
||||
loadAction.setHelpLocation(new HelpLocation("Trees", "Load_Filter"));
|
||||
loadAction.setToolBarData(
|
||||
new ToolBarData(Icons.OPEN_FOLDER_ICON));
|
||||
loadAction.setToolBarData(new ToolBarData(Icons.OPEN_FOLDER_ICON));
|
||||
addAction(loadAction);
|
||||
}
|
||||
|
||||
private void saveFilter() {
|
||||
ColumnFilterSaveManager<R> filterSaveManager = new ColumnFilterSaveManager<>(
|
||||
gTableFilterPanel, table, tableModel, filterModel.getDataSource());
|
||||
ColumnBasedTableFilter<R> filter = filterModel.getTableColumnFilter();
|
||||
String preferenceKey = filterManager.getPreferenceKey();
|
||||
ColumnFilterSaveManager<R> filterSaveManager = new ColumnFilterSaveManager<>(preferenceKey,
|
||||
table, tableModel, dialogModel.getDataSource());
|
||||
ColumnBasedTableFilter<R> filter = dialogModel.getTableColumnFilter();
|
||||
|
||||
String defaultName = new Date().toString();
|
||||
InputDialog dialog = new InputDialog("Save Filter", "Filter Name: ", defaultName, d -> {
|
||||
|
@ -174,13 +172,14 @@ public class ColumnFilterDialog<R> extends ReusableDialogComponentProvider
|
|||
filter.setName(filterName);
|
||||
filterSaveManager.addFilter(filter);
|
||||
filterSaveManager.save();
|
||||
gTableFilterPanel.updateSavedFilters(filter, true);
|
||||
filterModel.setFilter(filter);
|
||||
filterManager.updateSavedFilters(filter, true);
|
||||
dialogModel.setFilter(filter);
|
||||
}
|
||||
|
||||
private void loadFilter() {
|
||||
ColumnFilterSaveManager<R> filterSaveManager = new ColumnFilterSaveManager<>(
|
||||
gTableFilterPanel, table, tableModel, filterModel.getDataSource());
|
||||
String preferenceKey = filterManager.getPreferenceKey();
|
||||
ColumnFilterSaveManager<R> filterSaveManager = new ColumnFilterSaveManager<>(preferenceKey,
|
||||
table, tableModel, dialogModel.getDataSource());
|
||||
List<ColumnBasedTableFilter<R>> savedFilters = filterSaveManager.getSavedFilters();
|
||||
if (savedFilters.isEmpty()) {
|
||||
Msg.showInfo(this, getComponent(), "No Saved Filters",
|
||||
|
@ -195,7 +194,7 @@ public class ColumnFilterDialog<R> extends ReusableDialogComponentProvider
|
|||
|
||||
ColumnBasedTableFilter<R> selectedFilter = archiveDialog.getSelectedColumnFilter();
|
||||
if (selectedFilter != null) {
|
||||
filterModel.setFilter(selectedFilter);
|
||||
dialogModel.setFilter(selectedFilter);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -223,14 +222,12 @@ public class ColumnFilterDialog<R> extends ReusableDialogComponentProvider
|
|||
bottomPanel = new JPanel(new BorderLayout());
|
||||
JPanel innerPanel = new JPanel(new VerticalLayout(3));
|
||||
|
||||
JButton addAndConditionButton =
|
||||
new JButton("Add AND condition", Icons.ADD_ICON);
|
||||
JButton addAndConditionButton = new JButton("Add AND condition", Icons.ADD_ICON);
|
||||
|
||||
addAndConditionButton.addActionListener(e -> addFilterCondition(LogicOperation.AND));
|
||||
addAndConditionButton.setEnabled(true);
|
||||
|
||||
JButton addOrConditionButton =
|
||||
new JButton("Add OR condition", Icons.ADD_ICON);
|
||||
JButton addOrConditionButton = new JButton("Add OR condition", Icons.ADD_ICON);
|
||||
|
||||
addOrConditionButton.setHorizontalAlignment(SwingConstants.LEFT);
|
||||
addOrConditionButton.addActionListener(e -> addFilterCondition(LogicOperation.OR));
|
||||
|
@ -251,7 +248,7 @@ public class ColumnFilterDialog<R> extends ReusableDialogComponentProvider
|
|||
}
|
||||
sb.append("Column Filter");
|
||||
|
||||
ColumnBasedTableFilter<R> filter = filterModel.getTableColumnFilter();
|
||||
ColumnBasedTableFilter<R> filter = dialogModel.getTableColumnFilter();
|
||||
if (filter != null && filter.getName() != null) {
|
||||
sb.append(": ").append(filter.getName());
|
||||
}
|
||||
|
@ -275,7 +272,7 @@ public class ColumnFilterDialog<R> extends ReusableDialogComponentProvider
|
|||
// * Dialog state is different from applied filter and valid - prompt to apply filter.
|
||||
// * Dialog state is different from applied filter, but invalid - prompt if should really close
|
||||
|
||||
if (!filterModel.hasUnappliedChanges()) {
|
||||
if (!dialogModel.hasUnappliedChanges()) {
|
||||
return true;
|
||||
}
|
||||
if (dialogHasValidFilter()) {
|
||||
|
@ -316,12 +313,12 @@ public class ColumnFilterDialog<R> extends ReusableDialogComponentProvider
|
|||
}
|
||||
|
||||
private boolean dialogHasValidFilter() {
|
||||
return filterModel.getTableColumnFilter() != null;
|
||||
return dialogModel.getTableColumnFilter() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dialogClosed() {
|
||||
filterModel.dispose();
|
||||
dialogModel.dispose();
|
||||
if (closeCallback != null) {
|
||||
closeCallback.call();
|
||||
}
|
||||
|
@ -339,28 +336,29 @@ public class ColumnFilterDialog<R> extends ReusableDialogComponentProvider
|
|||
}
|
||||
|
||||
private void clearFilter() {
|
||||
this.gTableFilterPanel.setColumnTableFilter(null);
|
||||
filterModel.clear();
|
||||
filterManager.setFilter(null);
|
||||
dialogModel.clear();
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
private void applyFilter() {
|
||||
ColumnBasedTableFilter<R> tableColumnFilter = filterModel.getTableColumnFilter();
|
||||
filterModel.setCurrentlyAppliedFilter(tableColumnFilter);
|
||||
this.gTableFilterPanel.setColumnTableFilter(tableColumnFilter);
|
||||
ColumnBasedTableFilter<R> tableColumnFilter = dialogModel.getTableColumnFilter();
|
||||
dialogModel.setCurrentlyAppliedFilter(tableColumnFilter);
|
||||
filterManager.setFilter(tableColumnFilter);
|
||||
}
|
||||
|
||||
private void loadFilterRows() {
|
||||
filterPanelContainer.removeAll();
|
||||
filterPanels.clear();
|
||||
|
||||
List<DialogFilterRow> filterRows = filterModel.getFilterRows();
|
||||
List<DialogFilterRow> filterRows = dialogModel.getFilterRows();
|
||||
for (int i = 0; i < filterRows.size(); i++) {
|
||||
DialogFilterRow filterRow = filterRows.get(i);
|
||||
ColumnFilterPanel panel = new ColumnFilterPanel(filterRow);
|
||||
if (i != 0) {
|
||||
filterPanelContainer.add(
|
||||
createLogicalOperationLabel(filterRow.getLogicOperation()));
|
||||
LogicOperation op = filterRow.getLogicOperation();
|
||||
GLabel label = createLogicalOperationLabel(op);
|
||||
filterPanelContainer.add(label);
|
||||
}
|
||||
filterPanelContainer.add(panel);
|
||||
filterPanels.add(panel);
|
||||
|
@ -382,14 +380,14 @@ public class ColumnFilterDialog<R> extends ReusableDialogComponentProvider
|
|||
headerPanel.add(new GLabel("Filter", SwingConstants.CENTER));
|
||||
headerPanel.add(new GLabel("Filter Value", SwingConstants.CENTER));
|
||||
|
||||
headerPanel.setBorder(new CompoundBorder(
|
||||
BorderFactory.createMatteBorder(0, 0, 1, 0, Colors.BORDER),
|
||||
BorderFactory.createEmptyBorder(4, 0, 4, 0)));
|
||||
headerPanel.setBorder(
|
||||
new CompoundBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Colors.BORDER),
|
||||
BorderFactory.createEmptyBorder(4, 0, 4, 0)));
|
||||
return headerPanel;
|
||||
}
|
||||
|
||||
private void addFilterCondition(LogicOperation logicalOperation) {
|
||||
filterModel.createFilterRow(logicalOperation);
|
||||
dialogModel.createFilterRow(logicalOperation);
|
||||
scrollFilterPanelToBottom();
|
||||
}
|
||||
|
||||
|
@ -410,7 +408,7 @@ public class ColumnFilterDialog<R> extends ReusableDialogComponentProvider
|
|||
void updateStatus() {
|
||||
setStatusText(getStatusMessage());
|
||||
|
||||
boolean isValid = filterModel.isValid();
|
||||
boolean isValid = dialogModel.isValid();
|
||||
setOkEnabled(isValid);
|
||||
setApplyEnabled(isValid);
|
||||
|
||||
|
@ -423,11 +421,11 @@ public class ColumnFilterDialog<R> extends ReusableDialogComponentProvider
|
|||
}
|
||||
|
||||
public void filterChanged(ColumnBasedTableFilter<R> newFilter) {
|
||||
if (Objects.equals(newFilter, filterModel.getTableColumnFilter())) {
|
||||
if (Objects.equals(newFilter, dialogModel.getTableColumnFilter())) {
|
||||
return;
|
||||
}
|
||||
getComponent().requestFocus(); // work around for java parenting bug where dialog appears behind
|
||||
if (filterModel.hasUnappliedChanges()) {
|
||||
if (dialogModel.hasUnappliedChanges()) {
|
||||
int result = OptionDialog.showYesNoDialog(getComponent(), "Filter Changed",
|
||||
"The filter has been changed externally.\n" +
|
||||
" Do you want to update this editor and lose your current changes?");
|
||||
|
@ -435,14 +433,14 @@ public class ColumnFilterDialog<R> extends ReusableDialogComponentProvider
|
|||
return;
|
||||
}
|
||||
}
|
||||
filterModel.setFilter(newFilter);
|
||||
dialogModel.setFilter(newFilter);
|
||||
}
|
||||
|
||||
private String getStatusMessage() {
|
||||
if (filterModel.isEmpty()) {
|
||||
if (dialogModel.isEmpty()) {
|
||||
return "Please add a filter condition!";
|
||||
}
|
||||
if (!filterModel.isValid()) {
|
||||
if (!dialogModel.isValid()) {
|
||||
return "One or more filter values are invalid!";
|
||||
}
|
||||
return "";
|
||||
|
@ -473,8 +471,6 @@ public class ColumnFilterDialog<R> extends ReusableDialogComponentProvider
|
|||
}
|
||||
|
||||
void filterRemoved(ColumnBasedTableFilter<R> filter) {
|
||||
gTableFilterPanel.updateSavedFilters(filter, false);
|
||||
|
||||
filterManager.updateSavedFilters(filter, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -193,6 +193,12 @@ public class ConcurrentQBuilder<I, R> {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the final {@link ConcurrentQ}.
|
||||
*
|
||||
* @param callback the callback for processing each job
|
||||
* @return the new queue
|
||||
*/
|
||||
public ConcurrentQ<I, R> build(QCallback<I, R> callback) {
|
||||
|
||||
ConcurrentQ<I, R> concurrentQ = new ConcurrentQ<>(callback, getQueue(), getThreadPool(),
|
||||
|
|
|
@ -17,7 +17,7 @@ package ghidra.util;
|
|||
|
||||
/**
|
||||
* Ghidra synchronization lock. This class allows creation of named locks for
|
||||
* synchroniing modification of multiple tables in the Ghidra database.
|
||||
* synchronizing modification of multiple tables in the Ghidra database.
|
||||
*/
|
||||
public class Lock {
|
||||
private Thread owner;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue