package com.revolsys.swing.table.record.editor; import java.awt.Component; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.util.EventObject; import java.util.concurrent.Callable; import javax.swing.AbstractCellEditor; import javax.swing.BorderFactory; import javax.swing.ComboBoxEditor; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JOptionPane; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.event.CellEditorListener; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import javax.swing.table.TableCellEditor; import com.revolsys.awt.WebColors; import com.revolsys.datatype.DataType; import com.revolsys.record.schema.RecordDefinition; import com.revolsys.swing.SwingUtil; import com.revolsys.swing.field.AbstractRecordQueryField; import com.revolsys.swing.field.Field; import com.revolsys.swing.field.TextField; import com.revolsys.swing.listener.MouseListeners; import com.revolsys.swing.listener.MouseListenersBase; import com.revolsys.swing.menu.BaseJPopupMenu; import com.revolsys.swing.menu.ShowMenuMouseListener; import com.revolsys.swing.table.BaseJTable; import com.revolsys.swing.table.record.model.AbstractRecordTableModel; public class RecordTableCellEditor extends AbstractCellEditor implements TableCellEditor, KeyListener, TableModelListener { private static final long serialVersionUID = 1L; private int columnIndex; private DataType dataType; private JComponent editorComponent; private String fieldName; private final MouseListeners mouseListeners = new MouseListenersBase(); private Object oldValue; private Callable<BaseJPopupMenu> popupMenuFactory = null; private int rowIndex; private BaseJTable table; private ShowMenuMouseListener popupMenuListener; public RecordTableCellEditor(final BaseJTable table) { this.table = table; table.getModel().addTableModelListener(this); } public synchronized void addMouseListener(final MouseListener l) { this.mouseListeners.addMouseListener(l); } public void close() { this.editorComponent = null; this.mouseListeners.clearMouseListeners(); this.popupMenuFactory = null; this.popupMenuListener = null; this.table = null; for (final CellEditorListener listener : getCellEditorListeners()) { removeCellEditorListener(listener); } } private void editCellRelative(final int rowDelta, final int columnDelta) { final int rowIndex = this.table.convertRowIndexToView(this.rowIndex) + rowDelta; if (rowIndex >= 0 && rowIndex <= this.table.getRowCount()) { final int columnIndex = this.table.convertColumnIndexToView(this.columnIndex) + columnDelta; if (columnIndex >= 0 && columnIndex < this.table.getColumnCount()) { this.table.editCell(rowIndex, columnIndex); } } } @Override public Object getCellEditorValue() { final Object value = SwingUtil.getValue(this.editorComponent); return value; } public JComponent getEditorComponent() { return this.editorComponent; } public String getFieldName() { return this.fieldName; } public Object getOldValue() { return this.oldValue; } protected RecordDefinition getRecordDefinition() { final AbstractRecordTableModel tableModel = getTableModel(); return tableModel.getRecordDefinition(); } @Override public Component getTableCellEditorComponent(final JTable table, final Object value, final boolean isSelected, int rowIndex, int columnIndex) { rowIndex = table.convertRowIndexToModel(rowIndex); columnIndex = table.convertColumnIndexToModel(columnIndex); this.oldValue = value; final AbstractRecordTableModel model = getTableModel(); this.fieldName = model.getColumnFieldName(rowIndex, columnIndex); final RecordDefinition recordDefinition = model.getRecordDefinition(); this.dataType = recordDefinition.getFieldType(this.fieldName); final Field field = newField(this.fieldName); this.editorComponent = (JComponent)field; if (this.editorComponent instanceof JTextField) { final JTextField textField = (JTextField)this.editorComponent; textField.setBorder( BorderFactory.createCompoundBorder(BorderFactory.createLineBorder(WebColors.LightSteelBlue), BorderFactory.createEmptyBorder(1, 2, 1, 2))); } else if (this.editorComponent instanceof AbstractRecordQueryField) { final AbstractRecordQueryField queryField = (AbstractRecordQueryField)this.editorComponent; queryField.setSearchFieldBorder( BorderFactory.createCompoundBorder(BorderFactory.createLineBorder(WebColors.LightSteelBlue), BorderFactory.createEmptyBorder(1, 2, 1, 2))); } this.editorComponent.setOpaque(false); SwingUtil.setFieldValue(this.editorComponent, value); this.rowIndex = rowIndex; this.columnIndex = columnIndex; this.editorComponent.addKeyListener(this); this.editorComponent.addMouseListener(this.mouseListeners); if (this.editorComponent instanceof JComboBox) { final JComboBox<?> comboBox = (JComboBox<?>)this.editorComponent; final ComboBoxEditor editor = comboBox.getEditor(); final Component comboEditorComponent = editor.getEditorComponent(); comboEditorComponent.addKeyListener(this); comboEditorComponent.addMouseListener(this.mouseListeners); } else if (this.editorComponent instanceof AbstractRecordQueryField) { final AbstractRecordQueryField queryField = (AbstractRecordQueryField)this.editorComponent; final TextField searchField = queryField.getSearchField(); searchField.addKeyListener(this); searchField.addMouseListener(this.mouseListeners); } this.popupMenuListener = ShowMenuMouseListener.addListener(this.editorComponent, this.popupMenuFactory); return this.editorComponent; } protected AbstractRecordTableModel getTableModel() { return (AbstractRecordTableModel)this.table.getModel(); } @Override public boolean isCellEditable(final EventObject event) { if (event == null) { return true; } else { if (event instanceof MouseEvent) { final MouseEvent mouseEvent = (MouseEvent)event; if (SwingUtil.isLeftButtonAndNoModifiers(mouseEvent)) { return true; } } } return false; } @Override public void keyPressed(final KeyEvent event) { final int keyCode = event.getKeyCode(); if (keyCode == KeyEvent.VK_ENTER) { stopCellEditing(); if (SwingUtil.isShiftDown(event)) { editCellRelative(-1, 0); } else { editCellRelative(1, 0); } event.consume(); } else if (keyCode == KeyEvent.VK_TAB) { stopCellEditing(); if (SwingUtil.isShiftDown(event)) { editCellRelative(0, -1); } else { editCellRelative(0, 1); } event.consume(); } } @Override public void keyReleased(final KeyEvent e) { } @Override public void keyTyped(final KeyEvent e) { } protected Field newField(final String fieldName) { final RecordDefinition recordDefinition = getRecordDefinition(); return SwingUtil.newField(recordDefinition, fieldName, true); } public synchronized void removeMouseListener(final MouseListener listener) { this.mouseListeners.removeMouseListener(listener); } public void setPopupMenu(final Callable<BaseJPopupMenu> popupMenuFactory) { this.popupMenuFactory = popupMenuFactory; } @Override public boolean stopCellEditing() { boolean stopped = false; try { if (this.editorComponent instanceof Field) { final Field field = (Field)this.editorComponent; field.updateFieldValue(); } stopped = super.stopCellEditing(); } catch (final IndexOutOfBoundsException e) { return true; } catch (final Throwable t) { final int result = JOptionPane.showConfirmDialog(this.editorComponent, "<html><p><b>'" + getCellEditorValue() + "' is not a valid " + this.dataType.getValidationName() + ".</b></p><p>Discard changes (Yes) or edit field (No).</p></html>", "Invalid value", JOptionPane.YES_NO_OPTION, JOptionPane.ERROR_MESSAGE); if (result == JOptionPane.YES_OPTION) { cancelCellEditing(); return true; } else { return false; } } finally { if (stopped) { if (this.editorComponent != null) { this.editorComponent.removeMouseListener(this.mouseListeners); this.editorComponent.removeKeyListener(this); if (this.editorComponent instanceof JComboBox) { final JComboBox<?> comboBox = (JComboBox<?>)this.editorComponent; final ComboBoxEditor editor = comboBox.getEditor(); final Component comboEditorComponent = editor.getEditorComponent(); comboEditorComponent.removeKeyListener(this); comboEditorComponent.removeMouseListener(this.mouseListeners); } else if (this.editorComponent instanceof AbstractRecordQueryField) { final AbstractRecordQueryField queryField = (AbstractRecordQueryField)this.editorComponent; final TextField searchField = queryField.getSearchField(); searchField.removeKeyListener(this); searchField.removeMouseListener(this.mouseListeners); } if (this.popupMenuListener != null) { this.popupMenuListener.close(); this.popupMenuListener = null; } } } } return stopped; } @Override public void tableChanged(final TableModelEvent e) { if (e.getFirstRow() <= this.rowIndex && this.rowIndex <= e.getLastRow()) { if (e.getColumn() == TableModelEvent.ALL_COLUMNS) { cancelCellEditing(); } } } }