mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
GT-3629 - Table Copying - updated tables so that the Copy action will
copy the table's cell text as it is renderered to the user
This commit is contained in:
parent
d648dd3ef8
commit
edc19158fd
8 changed files with 193 additions and 92 deletions
|
@ -635,10 +635,7 @@ class MemoryMapProvider extends ComponentProviderAdapter {
|
||||||
memManager.mergeBlocks(blocks);
|
memManager.mergeBlocks(blocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void setCursor(Cursor cursor) {
|
||||||
* @param cursor
|
|
||||||
*/
|
|
||||||
public void setCursor(Cursor cursor) {
|
|
||||||
tool.getToolFrame().setCursor(cursor);
|
tool.getToolFrame().setCursor(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -657,9 +654,9 @@ class MemoryMapProvider extends ComponentProviderAdapter {
|
||||||
return plugin.getTool();
|
return plugin.getTool();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================================================================================================
|
// ==================================================================================================
|
||||||
// Inner Classes
|
// Inner Classes
|
||||||
// ==================================================================================================
|
// ==================================================================================================
|
||||||
|
|
||||||
private class MemoryMapTable extends GhidraTable {
|
private class MemoryMapTable extends GhidraTable {
|
||||||
MemoryMapTable(TableModel model) {
|
MemoryMapTable(TableModel model) {
|
||||||
|
@ -670,7 +667,7 @@ class MemoryMapProvider extends ComponentProviderAdapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected <T> SelectionManager createSelectionManager(TableModel model) {
|
protected <T> SelectionManager createSelectionManager() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.functionwindow;
|
package ghidra.app.plugin.core.functionwindow;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.*;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
@ -24,6 +25,8 @@ import javax.swing.table.*;
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
import docking.ComponentProvider;
|
import docking.ComponentProvider;
|
||||||
|
import docking.action.DockingActionIf;
|
||||||
|
import docking.tool.ToolConstants;
|
||||||
import docking.widgets.combobox.GComboBox;
|
import docking.widgets.combobox.GComboBox;
|
||||||
import docking.widgets.dialogs.SettingsDialog;
|
import docking.widgets.dialogs.SettingsDialog;
|
||||||
import docking.widgets.table.GTable;
|
import docking.widgets.table.GTable;
|
||||||
|
@ -134,6 +137,46 @@ public class FunctionWindowPluginTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
assertNotEquals("Changing the format did not change the view", startValue, endValue);
|
assertNotEquals("Changing the format did not change the view", startValue, endValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCopyingFunctionSignature() throws Exception {
|
||||||
|
|
||||||
|
int row = 0;
|
||||||
|
int column = getColumnIndex("Function Signature");
|
||||||
|
select(row);
|
||||||
|
|
||||||
|
String signatureText = getRenderedTableCellValue(functionTable, row, column);
|
||||||
|
|
||||||
|
DockingActionIf copyAction = getAction(tool, ToolConstants.SHARED_OWNER, "Table Data Copy");
|
||||||
|
performAction(copyAction);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Note: we cannot make this call:
|
||||||
|
// String clipboardText = getClipboardText();
|
||||||
|
//
|
||||||
|
// The copy action of the table uses Java's built-in copy code. That code uses the system
|
||||||
|
// clipboard, which we cannot rely on in a testing environment. So, we will just call
|
||||||
|
// the code under test directly.
|
||||||
|
//
|
||||||
|
|
||||||
|
// flag to trigger copy code
|
||||||
|
setInstanceField("copying", functionTable, Boolean.TRUE);
|
||||||
|
String copyText = getCopyText(row, column);
|
||||||
|
assertThat(copyText, containsString(signatureText));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getCopyText(int row, int column) {
|
||||||
|
Object value = runSwing(() -> functionTable.getValueAt(row, column));
|
||||||
|
assertNotNull(value);
|
||||||
|
return value.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void select(int row) {
|
||||||
|
runSwing(() -> {
|
||||||
|
functionTable.clearSelection();
|
||||||
|
functionTable.addRowSelectionInterval(row, row);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private int getFormatRow(SettingsDialog dialog) {
|
private int getFormatRow(SettingsDialog dialog) {
|
||||||
GTable table = dialog.getTable();
|
GTable table = dialog.getTable();
|
||||||
int column = getColumnIndex(table, "Name");
|
int column = getColumnIndex(table, "Name");
|
||||||
|
|
|
@ -31,6 +31,7 @@ import org.junit.*;
|
||||||
import docking.ActionContext;
|
import docking.ActionContext;
|
||||||
import docking.ComponentProvider;
|
import docking.ComponentProvider;
|
||||||
import docking.action.DockingAction;
|
import docking.action.DockingAction;
|
||||||
|
import docking.dnd.GClipboard;
|
||||||
import edu.uci.ics.jung.algorithms.layout.Layout;
|
import edu.uci.ics.jung.algorithms.layout.Layout;
|
||||||
import edu.uci.ics.jung.visualization.VisualizationModel;
|
import edu.uci.ics.jung.visualization.VisualizationModel;
|
||||||
import edu.uci.ics.jung.visualization.VisualizationViewer;
|
import edu.uci.ics.jung.visualization.VisualizationViewer;
|
||||||
|
@ -393,7 +394,7 @@ public class FunctionGraphPlugin1Test extends AbstractFunctionGraphTest {
|
||||||
//
|
//
|
||||||
// Initialize the clipboard with known data
|
// Initialize the clipboard with known data
|
||||||
//
|
//
|
||||||
Clipboard systemClipboard = tool.getToolFrame().getToolkit().getSystemClipboard();
|
Clipboard systemClipboard = GClipboard.getSystemClipboard();
|
||||||
systemClipboard.setContents(DUMMY_TRANSFERABLE, null);
|
systemClipboard.setContents(DUMMY_TRANSFERABLE, null);
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
|
|
||||||
|
@ -446,7 +447,7 @@ public class FunctionGraphPlugin1Test extends AbstractFunctionGraphTest {
|
||||||
//
|
//
|
||||||
// Initialize the clipboard with known data
|
// Initialize the clipboard with known data
|
||||||
//
|
//
|
||||||
Clipboard systemClipboard = tool.getToolFrame().getToolkit().getSystemClipboard();
|
Clipboard systemClipboard = GClipboard.getSystemClipboard();
|
||||||
systemClipboard.setContents(DUMMY_TRANSFERABLE, null);
|
systemClipboard.setContents(DUMMY_TRANSFERABLE, null);
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,7 @@
|
||||||
package ghidra.feature.vt.gui.provider.matchtable;
|
package ghidra.feature.vt.gui.provider.matchtable;
|
||||||
|
|
||||||
import static ghidra.feature.vt.gui.actions.TableSelectionTrackingState.*;
|
import static ghidra.feature.vt.gui.actions.TableSelectionTrackingState.*;
|
||||||
import static ghidra.feature.vt.gui.plugin.VTPlugin.FILTERED_ICON;
|
import static ghidra.feature.vt.gui.plugin.VTPlugin.*;
|
||||||
import static ghidra.feature.vt.gui.plugin.VTPlugin.UNFILTERED_ICON;
|
|
||||||
import static ghidra.feature.vt.gui.util.VTOptionDefines.*;
|
import static ghidra.feature.vt.gui.util.VTOptionDefines.*;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
@ -695,24 +694,26 @@ public class VTMatchTableProvider extends ComponentProviderAdapter
|
||||||
"Markup items that are incomplete (for example, no destination address is specified) " +
|
"Markup items that are incomplete (for example, no destination address is specified) " +
|
||||||
"should become ignored by applying a match.");
|
"should become ignored by applying a match.");
|
||||||
|
|
||||||
vtOptions.getOptions(APPLY_MARKUP_OPTIONS_NAME).registerOptionsEditor(
|
vtOptions.getOptions(APPLY_MARKUP_OPTIONS_NAME)
|
||||||
|
.registerOptionsEditor(
|
||||||
new ApplyMarkupPropertyEditor(controller));
|
new ApplyMarkupPropertyEditor(controller));
|
||||||
vtOptions.getOptions(DISPLAY_APPLY_MARKUP_OPTIONS).setOptionsHelpLocation(
|
vtOptions.getOptions(DISPLAY_APPLY_MARKUP_OPTIONS)
|
||||||
|
.setOptionsHelpLocation(
|
||||||
new HelpLocation("VersionTracking", "Apply Markup Options"));
|
new HelpLocation("VersionTracking", "Apply Markup Options"));
|
||||||
|
|
||||||
HelpLocation applyOptionsHelpLocation =
|
HelpLocation applyOptionsHelpLocation =
|
||||||
new HelpLocation(VTPlugin.HELP_TOPIC_NAME, "Version_Tracking_Apply_Options");
|
new HelpLocation(VTPlugin.HELP_TOPIC_NAME, "Version_Tracking_Apply_Options");
|
||||||
HelpLocation acceptMatchOptionsHelpLocation =
|
|
||||||
new HelpLocation(VTPlugin.HELP_TOPIC_NAME, "Match_Accept_Options");
|
|
||||||
HelpLocation applyMatchOptionsHelpLocation =
|
HelpLocation applyMatchOptionsHelpLocation =
|
||||||
new HelpLocation(VTPlugin.HELP_TOPIC_NAME, "Match_Apply_Options");
|
new HelpLocation(VTPlugin.HELP_TOPIC_NAME, "Match_Apply_Options");
|
||||||
|
|
||||||
vtOptions.setOptionsHelpLocation(applyOptionsHelpLocation);
|
vtOptions.setOptionsHelpLocation(applyOptionsHelpLocation);
|
||||||
|
|
||||||
vtOptions.getOptions(ACCEPT_MATCH_OPTIONS_NAME).setOptionsHelpLocation(
|
vtOptions.getOptions(ACCEPT_MATCH_OPTIONS_NAME)
|
||||||
|
.setOptionsHelpLocation(
|
||||||
applyMatchOptionsHelpLocation);
|
applyMatchOptionsHelpLocation);
|
||||||
|
|
||||||
vtOptions.getOptions(APPLY_MARKUP_OPTIONS_NAME).setOptionsHelpLocation(
|
vtOptions.getOptions(APPLY_MARKUP_OPTIONS_NAME)
|
||||||
|
.setOptionsHelpLocation(
|
||||||
applyMatchOptionsHelpLocation);
|
applyMatchOptionsHelpLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -783,9 +784,9 @@ public class VTMatchTableProvider extends ComponentProviderAdapter
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
// this is our table model--we know its real type
|
// this is our table model--we know its real type
|
||||||
@Override
|
@Override
|
||||||
protected SelectionManager createSelectionManager(TableModel tableModel) {
|
protected SelectionManager createSelectionManager() {
|
||||||
return new VTMatchTableSelectionManager(this,
|
return new VTMatchTableSelectionManager(this,
|
||||||
(AbstractSortedTableModel<VTMatch>) tableModel);
|
(AbstractSortedTableModel<VTMatch>) getModel());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -810,7 +811,7 @@ public class VTMatchTableProvider extends ComponentProviderAdapter
|
||||||
private final int row;
|
private final int row;
|
||||||
private final VTMatch match;
|
private final VTMatch match;
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* (see the class header for details) {@link SelectionOverrideMemento}
|
* (see the class header for details) {@link SelectionOverrideMemento}
|
||||||
*/
|
*/
|
||||||
SelectionOverrideMemento(int row, VTMatch match) {
|
SelectionOverrideMemento(int row, VTMatch match) {
|
||||||
|
|
|
@ -22,9 +22,10 @@ import java.awt.datatransfer.Clipboard;
|
||||||
* Provides a place for clients to retrieve the Clipboard they should be using. This class
|
* Provides a place for clients to retrieve the Clipboard they should be using. This class
|
||||||
* provides a level of indirection that allows us to inject clipboards as needed.
|
* provides a level of indirection that allows us to inject clipboards as needed.
|
||||||
*
|
*
|
||||||
* <P>Note: if a test needs to check the contents of a native Java action, which will use the
|
* <P>Note: if a test needs to check the contents of the native clipboard, such as after
|
||||||
* system clipboard, then that cannot rely on the contents of the system clipboard. That test
|
* executing a native Java action that uses the system clipboard, then that test must use some
|
||||||
* will have to use some other mechanism to know that the native action was executed.
|
* other mechanism to know that the native action was executed. This is due to the fact that
|
||||||
|
* the system clipboard is potentially used by multiple Java test processes at once.
|
||||||
*/
|
*/
|
||||||
public class GClipboard {
|
public class GClipboard {
|
||||||
|
|
||||||
|
|
|
@ -17,10 +17,8 @@ package docking.widgets.table;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import javax.swing.JLabel;
|
|
||||||
import javax.swing.table.TableColumnModel;
|
import javax.swing.table.TableColumnModel;
|
||||||
|
|
||||||
import ghidra.docking.settings.Settings;
|
|
||||||
import ghidra.util.table.column.GColumnRenderer;
|
import ghidra.util.table.column.GColumnRenderer;
|
||||||
import ghidra.util.table.column.GColumnRenderer.ColumnConstraintFilterMode;
|
import ghidra.util.table.column.GColumnRenderer.ColumnConstraintFilterMode;
|
||||||
|
|
||||||
|
@ -63,44 +61,7 @@ public class DefaultRowFilterTransformer<ROW_OBJECT> implements RowFilterTransfo
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// note: this call can be slow when columns dynamically calculate values from the database
|
return TableUtils.getTableCellStringValue(model, rowObject, column);
|
||||||
Object value = model.getColumnValueForRow(rowObject, column);
|
|
||||||
if (value == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Methods for turning the cell value into a string to be filtered (in preference order):
|
|
||||||
1) Use the dynamic column's renderer (if applicable), as this is the most
|
|
||||||
direct way for clients to specify the filter value
|
|
||||||
2) See if the value is an instance of DisplayStringProvider, which describes how
|
|
||||||
it should be rendered
|
|
||||||
3) See if it is a label (this is uncommon)
|
|
||||||
4) Rely on the toString(); this works as intended for Strings. This is the
|
|
||||||
default way that built-in table cell renderers will generate display text
|
|
||||||
*/
|
|
||||||
|
|
||||||
// 1)
|
|
||||||
String renderedString = getRenderedColumnValue(value, column);
|
|
||||||
if (renderedString != null) {
|
|
||||||
return renderedString;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2) special plug-in point where clients can specify a value object that can return
|
|
||||||
// its display string
|
|
||||||
if (value instanceof DisplayStringProvider) {
|
|
||||||
return ((DisplayStringProvider) value).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3
|
|
||||||
if (value instanceof JLabel) { // some models do this odd thing
|
|
||||||
JLabel label = (JLabel) value;
|
|
||||||
String valueString = label.getText();
|
|
||||||
return valueString == null ? "" : valueString;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4)
|
|
||||||
return value.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean columnUsesConstraintFilteringOnly(int column) {
|
private boolean columnUsesConstraintFilteringOnly(int column) {
|
||||||
|
@ -119,24 +80,6 @@ public class DefaultRowFilterTransformer<ROW_OBJECT> implements RowFilterTransfo
|
||||||
return mode == ColumnConstraintFilterMode.ALLOW_CONSTRAINTS_FILTER_ONLY;
|
return mode == ColumnConstraintFilterMode.ALLOW_CONSTRAINTS_FILTER_ONLY;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getRenderedColumnValue(Object columnValue, int columnIndex) {
|
|
||||||
|
|
||||||
if (!(model instanceof DynamicColumnTableModel)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
DynamicColumnTableModel<ROW_OBJECT> columnBasedModel =
|
|
||||||
(DynamicColumnTableModel<ROW_OBJECT>) model;
|
|
||||||
GColumnRenderer<Object> renderer = getColumnRenderer(columnBasedModel, columnIndex);
|
|
||||||
if (renderer == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Settings settings = columnBasedModel.getColumnSettings(columnIndex);
|
|
||||||
String s = renderer.getFilterString(columnValue, settings);
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
private GColumnRenderer<Object> getColumnRenderer(
|
private GColumnRenderer<Object> getColumnRenderer(
|
||||||
DynamicColumnTableModel<ROW_OBJECT> columnBasedModel, int columnIndex) {
|
DynamicColumnTableModel<ROW_OBJECT> columnBasedModel, int columnIndex) {
|
||||||
DynamicTableColumn<ROW_OBJECT, ?, ?> column = columnBasedModel.getColumn(columnIndex);
|
DynamicTableColumn<ROW_OBJECT, ?, ?> column = columnBasedModel.getColumn(columnIndex);
|
||||||
|
|
|
@ -225,7 +225,15 @@ public class GTable extends JTable {
|
||||||
|
|
||||||
initializeRowHeight();
|
initializeRowHeight();
|
||||||
|
|
||||||
selectionManager = createSelectionManager(dataModel);
|
selectionManager = createSelectionManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected <T> SelectionManager createSelectionManager() {
|
||||||
|
RowObjectTableModel<Object> rowModel = getRowObjectTableModel();
|
||||||
|
if (rowModel != null) {
|
||||||
|
return new RowObjectSelectionManager<>(this, rowModel);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@ -233,9 +241,10 @@ public class GTable extends JTable {
|
||||||
// an arbitrary type T defined here. So, T doesn't really exist and therefore the cast isn't
|
// an arbitrary type T defined here. So, T doesn't really exist and therefore the cast isn't
|
||||||
// really casting to anything. The SelectionManager will take on the type of the given model.
|
// really casting to anything. The SelectionManager will take on the type of the given model.
|
||||||
// The T is just there on the SelectionManager to make its internal methods consistent.
|
// The T is just there on the SelectionManager to make its internal methods consistent.
|
||||||
protected <T> SelectionManager createSelectionManager(TableModel model) {
|
private <T> RowObjectTableModel<T> getRowObjectTableModel() {
|
||||||
|
TableModel model = getModel();
|
||||||
if (model instanceof RowObjectTableModel) {
|
if (model instanceof RowObjectTableModel) {
|
||||||
return new RowObjectSelectionManager<>(this, (RowObjectTableModel<T>) model);
|
return (RowObjectTableModel<T>) model;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -994,16 +1003,27 @@ public class GTable extends JTable {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Object getValueAt(int row, int column) {
|
public Object getValueAt(int row, int column) {
|
||||||
Object value = super.getValueAt(row, column);
|
|
||||||
|
|
||||||
if (!copying) {
|
if (!copying) {
|
||||||
return value;
|
return super.getValueAt(row, column);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object value = getCellValue(row, column);
|
||||||
Object updated = maybeConvertValue(value);
|
Object updated = maybeConvertValue(value);
|
||||||
return updated;
|
return updated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Object getCellValue(int row, int column) {
|
||||||
|
RowObjectTableModel<Object> rowModel = getRowObjectTableModel();
|
||||||
|
if (rowModel == null) {
|
||||||
|
Object value = super.getValueAt(row, column);
|
||||||
|
return maybeConvertValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object rowObject = rowModel.getRowObject(row);
|
||||||
|
String stringValue = TableUtils.getTableCellStringValue(rowModel, rowObject, column);
|
||||||
|
return maybeConvertValue(stringValue);
|
||||||
|
}
|
||||||
|
|
||||||
private Object maybeConvertValue(Object value) {
|
private Object maybeConvertValue(Object value) {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -15,14 +15,109 @@
|
||||||
*/
|
*/
|
||||||
package docking.widgets.table;
|
package docking.widgets.table;
|
||||||
|
|
||||||
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JTable;
|
import javax.swing.JTable;
|
||||||
import javax.swing.table.*;
|
import javax.swing.table.*;
|
||||||
|
|
||||||
|
import ghidra.docking.settings.Settings;
|
||||||
|
import ghidra.util.table.column.GColumnRenderer;
|
||||||
|
import ghidra.util.table.column.GColumnRenderer.ColumnConstraintFilterMode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A utility class for JTables used in Ghidra.
|
* A utility class for JTables used in Ghidra.
|
||||||
*/
|
*/
|
||||||
public class TableUtils {
|
public class TableUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the given row-based table model, row object and column index to determine what the
|
||||||
|
* String value should be for that cell.
|
||||||
|
*
|
||||||
|
* <P>This is used to provide a means for filtering on the text that is displayed to the user.
|
||||||
|
*
|
||||||
|
* @param <ROW_OBJECT> The model's row object type
|
||||||
|
* @param model the model
|
||||||
|
* @param rowObject the row object for the row being queried
|
||||||
|
* @param column the column index
|
||||||
|
* @return the string value; null if no value can be fabricated
|
||||||
|
*/
|
||||||
|
public static <ROW_OBJECT> String getTableCellStringValue(RowObjectTableModel<ROW_OBJECT> model,
|
||||||
|
ROW_OBJECT rowObject,
|
||||||
|
int column) {
|
||||||
|
|
||||||
|
// note: this call can be slow when columns dynamically calculate values from the database
|
||||||
|
Object value = model.getColumnValueForRow(rowObject, column);
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Methods for turning the cell value into the display value (in preference order):
|
||||||
|
1) Use the dynamic column's renderer (if applicable), as this is the most
|
||||||
|
direct way for clients to specify the display value
|
||||||
|
2) See if the value is an instance of DisplayStringProvider, which describes how
|
||||||
|
it should be rendered
|
||||||
|
3) See if it is a label (this is uncommon)
|
||||||
|
4) Rely on the toString(); this works as intended for Strings. This is the
|
||||||
|
default way that built-in table cell renderers will generate display text
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 1)
|
||||||
|
String renderedString = getRenderedColumnValue(model, value, column);
|
||||||
|
if (renderedString != null) {
|
||||||
|
return renderedString;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) special plug-in point where clients can specify a value object that can return
|
||||||
|
// its display string
|
||||||
|
if (value instanceof DisplayStringProvider) {
|
||||||
|
return ((DisplayStringProvider) value).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3
|
||||||
|
if (value instanceof JLabel) { // some models do this odd thing
|
||||||
|
JLabel label = (JLabel) value;
|
||||||
|
String valueString = label.getText();
|
||||||
|
return valueString == null ? "" : valueString;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4)
|
||||||
|
return value.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <ROW_OBJECT> String getRenderedColumnValue(RowObjectTableModel<ROW_OBJECT> model,
|
||||||
|
Object columnValue, int columnIndex) {
|
||||||
|
|
||||||
|
if (!(model instanceof DynamicColumnTableModel)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
DynamicColumnTableModel<ROW_OBJECT> columnBasedModel =
|
||||||
|
(DynamicColumnTableModel<ROW_OBJECT>) model;
|
||||||
|
GColumnRenderer<Object> renderer = getColumnRenderer(columnBasedModel, columnIndex);
|
||||||
|
if (renderer == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnConstraintFilterMode mode = renderer.getColumnConstraintFilterMode();
|
||||||
|
if (mode == ColumnConstraintFilterMode.ALLOW_CONSTRAINTS_FILTER_ONLY) {
|
||||||
|
// this is a renderer that does not know how to create its own display string
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Settings settings = columnBasedModel.getColumnSettings(columnIndex);
|
||||||
|
String s = renderer.getFilterString(columnValue, settings);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <ROW_OBJECT> GColumnRenderer<Object> getColumnRenderer(
|
||||||
|
DynamicColumnTableModel<ROW_OBJECT> columnBasedModel, int columnIndex) {
|
||||||
|
DynamicTableColumn<ROW_OBJECT, ?, ?> column = columnBasedModel.getColumn(columnIndex);
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
GColumnRenderer<Object> columnRenderer =
|
||||||
|
(GColumnRenderer<Object>) column.getColumnRenderer();
|
||||||
|
return columnRenderer;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to sort the given table based upon the given column index. If the {@link TableModel}
|
* Attempts to sort the given table based upon the given column index. If the {@link TableModel}
|
||||||
* of the given table is not a {@link SortedTableModel}, then this method will do nothing.
|
* of the given table is not a {@link SortedTableModel}, then this method will do nothing.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue