package com.revolsys.swing.table; import java.awt.Color; import java.awt.Component; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.util.Arrays; import javax.swing.BorderFactory; import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JTable; import javax.swing.KeyStroke; import javax.swing.RowFilter; import javax.swing.RowSorter; import javax.swing.event.RowSorterListener; import javax.swing.event.TableModelEvent; import javax.swing.table.JTableHeader; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableModel; import org.jdesktop.swingx.JXTable; import org.jdesktop.swingx.decorator.BorderHighlighter; import org.jdesktop.swingx.decorator.ColorHighlighter; import org.jdesktop.swingx.decorator.ComponentAdapter; import org.jdesktop.swingx.decorator.HighlightPredicate; import org.jdesktop.swingx.decorator.Highlighter; import org.jdesktop.swingx.renderer.DefaultTableRenderer; import org.jdesktop.swingx.table.ColumnFactory; import org.jdesktop.swingx.table.TableColumnExt; import com.revolsys.awt.WebColors; import com.revolsys.swing.SwingUtil; import com.revolsys.swing.parallel.Invoke; public class BaseJTable extends JXTable { private static final long serialVersionUID = 1L; private boolean initializingColumnWidths = false; public BaseJTable() { } public BaseJTable(final AbstractTableModel model) { super(model, model.newTableColumnModel(), model.newListSelectionModel()); setAutoResizeMode(JTable.AUTO_RESIZE_OFF); setGridColor(new Color(191, 191, 191)); setShowGrid(false, true); addHighlighter(new ColorHighlighter(HighlightPredicate.ODD, new Color(223, 223, 223), WebColors.Black, WebColors.Navy, WebColors.White)); addHighlighter(new ColorHighlighter(HighlightPredicate.EVEN, WebColors.White, WebColors.Black, WebColors.Blue, WebColors.White)); addLastRowBorderPredicate(); final TableCellRenderer headerRenderer = new SortableTableCellHeaderRenderer(); final JTableHeader tableHeader = getTableHeader(); tableHeader.setDefaultRenderer(headerRenderer); tableHeader.setReorderingAllowed(true); setFont(SwingUtil.FONT); final InputMap inputMap = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_DOWN_MASK), "copy"); inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.META_DOWN_MASK), "copy"); SwingUtil.addAction(this, KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.SHIFT_DOWN_MASK), "selectPreviousColumnCell", () -> editRelativeCell(0, -1)); SwingUtil.addAction(this, KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), "selectNextColumnCell", () -> editRelativeCell(0, 1)); SwingUtil.addAction(this, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.SHIFT_DOWN_MASK), "selectPreviousRowCell", () -> editRelativeCell(-1, 0)); SwingUtil.addAction(this, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "enterPressed", () -> editRelativeCell(1, 0)); putClientProperty("terminateEditOnFocusLost", Boolean.TRUE); } protected void addLastRowBorderPredicate() { final HighlightPredicate lastPredicate = (final Component renderer, final ComponentAdapter adapter) -> { final int row = adapter.row; final int lastRowIndex = getRowCount() - 1; return row == lastRowIndex; }; addHighlighter(new BorderHighlighter(lastPredicate, BorderFactory.createMatteBorder(0, 0, 1, 0, new Color(191, 191, 191)))); } public void addRowSorterListener(final RowSorterListener listener) { super.getRowSorter().addRowSorterListener(listener); } @Override protected void adjustComponentOrientation(final Component stamp) { if (stamp != null) { super.adjustComponentOrientation(stamp); } } @Override public int convertColumnIndexToModel(final int columnIndex) { try { return super.convertColumnIndexToModel(columnIndex); } catch (final IndexOutOfBoundsException e) { return columnIndex; } } @Override public int convertColumnIndexToView(final int columnIndex) { try { return super.convertColumnIndexToView(columnIndex); } catch (final IndexOutOfBoundsException e) { return columnIndex; } } @Override public int convertRowIndexToModel(final int rowIndex) { try { return super.convertRowIndexToModel(rowIndex); } catch (final IndexOutOfBoundsException e) { return rowIndex; } } @Override public int convertRowIndexToView(final int rowIndex) { try { return super.convertRowIndexToView(rowIndex); } catch (final IndexOutOfBoundsException e) { return rowIndex; } } @Override protected void createDefaultRenderers() { super.createDefaultRenderers(); setDefaultRenderer(Object.class, new DefaultTableRenderer(new StringConverterValue())); } @Override protected RowSorter<? extends TableModel> createDefaultRowSorter() { final TableModel model = getModel(); return new BaseRowSorter(model); } public void dispose() { for (final Highlighter highlighter : getHighlighters()) { removeHighlighter(highlighter); } } public void editCell(final int rowIndex, final int columnIndex) { if (rowIndex >= 0 && rowIndex < getRowCount() && columnIndex >= 0 && columnIndex < getColumnCount()) { requestFocusInWindow(); changeSelection(rowIndex, columnIndex, false, false); editCellAt(rowIndex, columnIndex); final Component editor = getEditorComponent(); if (editor != null) { editor.requestFocusInWindow(); } } } public void editRelativeCell(final int rowDelta, final int columnDelta) { final int selectedRow = getSelectedRow(); final int selectedColumn = getSelectedColumn(); final int rowIndex = selectedRow + rowDelta; final int columnIndex = selectedColumn + columnDelta; editCell(rowIndex, columnIndex); } public int getPreferedSize(TableCellRenderer renderer, final Class<?> columnClass, final Object value) { if (renderer == null) { renderer = getDefaultRenderer(columnClass); } final Component comp = renderer.getTableCellRendererComponent(this, value, false, false, 0, -1); final int width = comp.getPreferredSize().width; return width; } @Override public RowSorter<? extends TableModel> getRowSorter() { if (isSortable()) { return super.getRowSorter(); } else { return null; } } public int getSelectedRowInModel() { int selectedRow = getSelectedRow(); if (selectedRow != -1) { selectedRow = convertRowIndexToModel(selectedRow); } return selectedRow; } public int getSelectedRowInView() { int selectedRow = getSelectedRow(); if (selectedRow != -1) { selectedRow = convertRowIndexToView(selectedRow); } return selectedRow; } public int[] getSelectedRowsInModel() { final int[] selectedRows = getSelectedRows(); for (int i = 0; i < selectedRows.length; i++) { final int row = selectedRows[i]; selectedRows[i] = convertRowIndexToModel(row); } Arrays.sort(selectedRows); return selectedRows; } @SuppressWarnings("unchecked") public <V extends TableModel> V getTableModel() { return (V)getModel(); } @Override public void initializeColumnWidths() { try { this.initializingColumnWidths = true; super.initializeColumnWidths(); } finally { this.initializingColumnWidths = false; } } @Override public boolean isCellEditable(final int row, final int column) { try { return super.isCellEditable(row, column); } catch (final IndexOutOfBoundsException e) { return false; } } public boolean isEditingCurrentCell() { if (isEditing()) { final int eventRow = TablePanel.getEventRow(); final int eventColumn = TablePanel.getEventColumn(); if (eventRow > -1 && eventRow == getEditingRow()) { if (eventColumn > -1 && eventColumn == getEditingColumn()) { return true; } } } return false; } public boolean isInitializingColumnWidths() { return this.initializingColumnWidths; } @Override public Component prepareRenderer(final TableCellRenderer renderer, final int row, final int column) { try { return super.prepareRenderer(renderer, row, column); } catch (final IndexOutOfBoundsException e) { return new JLabel("..."); } } public void removeRowSorterListener(final RowSorterListener listener) { super.getRowSorter().removeRowSorterListener(listener); } @Override public void repaint() { Invoke.later(super::repaint); } public void resizeColumnsToContent() { final TableModel model = getModel(); final int columnCount = getColumnCount(); for (int columnIndex = 0; columnIndex < columnCount; columnIndex++) { final TableColumnExt column = getColumnExt(columnIndex); final TableCellRenderer headerRenderer = column.getHeaderRenderer(); final String columnName = model.getColumnName(columnIndex); int maxPreferedWidth = getPreferedSize(headerRenderer, String.class, columnName); for (int rowIndex = 0; rowIndex < model.getRowCount(); rowIndex++) { final Object value = model.getValueAt(rowIndex, columnIndex); if (value != null) { final TableCellRenderer renderer = column.getCellRenderer(); final Class<?> columnClass = model.getColumnClass(columnIndex); final int width = getPreferedSize(renderer, columnClass, value); if (width > maxPreferedWidth) { maxPreferedWidth = width; } } } column.setMinWidth(maxPreferedWidth + 25); column.setPreferredWidth(maxPreferedWidth + 25); } } @Override public void setColumnFactory(final ColumnFactory columnFactory) { super.setColumnFactory(columnFactory); final TableModelEvent e = new TableModelEvent(getModel(), TableModelEvent.HEADER_ROW); tableChanged(e); } public void setColumnWidth(final int i, final int width) { final TableColumnExt column = getColumnExt(i); column.setMinWidth(width); column.setWidth(width); column.setMaxWidth(width); } public void setMinWidth(final int columnIndex, final int width) { final TableColumnExt column = getColumnExt(columnIndex); column.setMinWidth(width); } @Override public void setModel(final TableModel model) { if (model instanceof AbstractTableModel) { final AbstractTableModel tableModel = (AbstractTableModel)model; tableModel.setTable(this); } final boolean createColumns = getAutoCreateColumnsFromModel(); if (createColumns) { setAutoCreateColumnsFromModel(false); } try { super.setModel(model); } finally { if (createColumns) { setAutoCreateColumnsFromModel(true); } if (isSortable()) { setRowSorter(createDefaultRowSorter()); } } initializeColumnWidths(); } @Override public <R extends TableModel> void setRowFilter( final RowFilter<? super R, ? super Integer> filter) { try { super.setRowFilter(filter); } catch (final NegativeArraySizeException e) { } firePropertyChange("rowFilterChanged", false, true); } @Override public void setSortable(final boolean sortable) { super.setSortable(sortable); if (sortable) { RowSorter<? extends TableModel> rowSorter = getRowSorter(); if (rowSorter == null) { rowSorter = createDefaultRowSorter(); setRowSorter(rowSorter); } } else { setRowSorter(null); } } }