Skip to content

Commit 7cca674

Browse files
committed
JTable: Fix absence of horizontal scrollbar and add popup menu actions
This ensures horizontal scrollbar is displayed when contents of table too small to fit in view port. In addition, it implements a contextual menu hosting some basic actions: - Copy and Select All command (from the JTable action map) - Delete selected rows command - Resize command that enlarges column widths to largest cell in the column Fixes #62
1 parent 8414686 commit 7cca674

File tree

1 file changed

+128
-0
lines changed

1 file changed

+128
-0
lines changed

src/main/java/org/scijava/ui/swing/viewer/table/SwingTableDisplayPanel.java

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,25 @@
2929

3030
package org.scijava.ui.swing.viewer.table;
3131

32+
import java.awt.Component;
33+
import java.awt.Toolkit;
34+
import java.awt.event.ActionEvent;
35+
import java.awt.event.ComponentAdapter;
36+
import java.awt.event.ComponentEvent;
37+
import java.awt.event.KeyEvent;
38+
import java.util.Arrays;
39+
40+
import javax.swing.AbstractAction;
41+
import javax.swing.Action;
42+
import javax.swing.JComponent;
43+
import javax.swing.JMenuItem;
44+
import javax.swing.JPopupMenu;
3245
import javax.swing.JScrollPane;
3346
import javax.swing.JTable;
47+
import javax.swing.KeyStroke;
3448
import javax.swing.table.AbstractTableModel;
49+
import javax.swing.table.TableCellRenderer;
50+
import javax.swing.table.TableColumn;
3551

3652
import org.scijava.table.Table;
3753
import org.scijava.table.TableDisplay;
@@ -65,9 +81,25 @@ public SwingTableDisplayPanel(final TableDisplay display,
6581
this.window = window;
6682
nullModel = new NullTableModel();
6783
table = makeTable();
84+
6885
table.setAutoCreateRowSorter(true);
86+
table.setRowSelectionAllowed(true);
87+
new TablePopupMenu().install();
88+
89+
//table.setPreferredScrollableViewportSize(getPreferredSize());
90+
addComponentListener(new ComponentAdapter() {
91+
@Override
92+
public void componentResized(final ComponentEvent e) {
93+
if (table.getPreferredSize().width < getWidth()) {
94+
table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
95+
} else {
96+
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
97+
}
98+
}
99+
});
69100
setViewportView(table);
70101
window.setContent(this);
102+
71103
}
72104

73105
// -- TableDisplayPanel methods --
@@ -133,6 +165,7 @@ private JTable makeTable() {
133165

134166
// -- Helper classes --
135167

168+
@SuppressWarnings("serial")
136169
public static class NullTableModel extends AbstractTableModel {
137170

138171
@Override
@@ -209,6 +242,14 @@ public void setValueAt(final Object value, final int row, final int col) {
209242
fireTableCellUpdated(row, col);
210243
}
211244

245+
public void removeRows(int[] indices) {
246+
Arrays.sort(indices);
247+
for (int i = indices.length - 1; i >= 0; i--) {
248+
tab.removeRow(indices[i]);
249+
fireTableRowsDeleted(indices[i], indices[i]);
250+
}
251+
}
252+
212253
// -- Helper methods --
213254

214255
private <T> void set(final Table<?, T> table,
@@ -221,4 +262,91 @@ private <T> void set(final Table<?, T> table,
221262

222263
}
223264

265+
@SuppressWarnings("serial")
266+
class TablePopupMenu extends JPopupMenu {
267+
public TablePopupMenu() {
268+
super();
269+
JMenuItem mi;
270+
mi= new JMenuItem(new ActionMapAction("Copy", table, "copy"));
271+
final int MASK = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
272+
mi.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, MASK));
273+
add(mi);
274+
mi = new JMenuItem(new ActionMapAction("Select All", table, "selectAll"));
275+
mi.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, MASK));
276+
add(mi);
277+
addSeparator();
278+
mi = new JMenuItem("Delete Selected Row(s)");
279+
mi.addActionListener(e -> {
280+
final int[] selectedRows = table.getSelectedRows();
281+
if (selectedRows.length > 0)
282+
((TableModel) table.getModel()).removeRows(selectedRows);
283+
});
284+
add(mi);
285+
mi = new JMenuItem("Resize Column Widths");
286+
mi.addActionListener(e -> resizeColumns());
287+
add(mi);
288+
}
289+
290+
private void resizeColumns() {
291+
// https://stackoverflow.com/a/30355804
292+
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
293+
table.getTableHeader();
294+
for (int column = 0; column < table.getColumnCount(); column++) {
295+
final TableColumn tableColumn = table.getColumnModel().getColumn(column);
296+
int preferredWidth = Math.max(tableColumn.getMinWidth(), getColumnHeaderWidth(tableColumn, column));
297+
final int maxWidth = tableColumn.getMaxWidth();
298+
for (int row = 0; row < table.getRowCount(); row++) {
299+
final TableCellRenderer cellRenderer = table.getCellRenderer(row, column);
300+
final Component c = table.prepareRenderer(cellRenderer, row, column);
301+
final int width = c.getPreferredSize().width + table.getIntercellSpacing().width;
302+
preferredWidth = Math.max(preferredWidth, width);
303+
if (preferredWidth >= maxWidth) {
304+
preferredWidth = maxWidth;
305+
break;
306+
}
307+
}
308+
tableColumn.setPreferredWidth(preferredWidth);
309+
}
310+
}
311+
312+
private int getColumnHeaderWidth(final TableColumn tableColumn, final int column) {
313+
final Object value = tableColumn.getHeaderValue();
314+
TableCellRenderer renderer = tableColumn.getHeaderRenderer();
315+
if (renderer == null) {
316+
renderer = table.getTableHeader().getDefaultRenderer();
317+
}
318+
final Component c = renderer.getTableCellRendererComponent(table, value, false, false, -1, column);
319+
return c.getPreferredSize().width;
320+
}
321+
322+
void install() {
323+
table.setComponentPopupMenu(this);
324+
SwingTableDisplayPanel.this.setComponentPopupMenu(this);
325+
}
326+
327+
}
328+
329+
@SuppressWarnings("serial")
330+
private class ActionMapAction extends AbstractAction {
331+
332+
private final Action originalAction;
333+
private final JComponent component;
334+
private final String actionCommand = "";
335+
336+
ActionMapAction(final String name, final JComponent component, final String actionKey) {
337+
super(name);
338+
originalAction = component.getActionMap().get(actionKey);
339+
if (originalAction == null) {
340+
throw new IllegalArgumentException("No Action for action key: " + actionKey);
341+
}
342+
this.component = component;
343+
}
344+
345+
@Override
346+
public void actionPerformed(ActionEvent e) {
347+
e = new ActionEvent(component, ActionEvent.ACTION_PERFORMED, actionCommand, e.getWhen(), e.getModifiers());
348+
originalAction.actionPerformed(e);
349+
}
350+
351+
}
224352
}

0 commit comments

Comments
 (0)