001 package org.springframework.richclient.widget.table; 002 003 004 import ca.odell.glazedlists.swing.EventTableModel; 005 import org.springframework.binding.form.FieldMetadata; 006 import org.springframework.binding.form.FormModel; 007 import org.springframework.binding.value.ValueModel; 008 import org.springframework.richclient.command.ActionCommand; 009 import org.springframework.richclient.form.AbstractForm; 010 011 import javax.swing.*; 012 import javax.swing.table.TableCellEditor; 013 import javax.swing.table.TableModel; 014 import javax.swing.text.JTextComponent; 015 import java.awt.*; 016 import java.util.EventObject; 017 018 /** 019 * {@link javax.swing.table.TableCellEditor} that uses a backing {@link FormModel} to determine the editing capabilities and 020 * committing of the value. 021 * 022 * <p> 023 * NOTE: the CellEditor will get the first event, only afterwards the row selection changes. This has the 024 * effect that editing a cell will switch values upon entering. Additionally the cell should always be marked 025 * editable and the binding that acts as the editor should disable itself if needed. If we were to rely on 026 * {@link FieldMetadata#isReadOnly()}, the previous value would determine if the cell is editable because 027 * the underlying form object isn't changed yet. 028 * </p> 029 * 030 * @author Jan Hoskens 031 * 032 */ 033 public class ValueModelTableCellEditor extends AbstractCellEditor implements TableCellEditor 034 { 035 036 private final FormModel formModel; 037 038 private final ActionCommand commitCommand; 039 040 private final FieldMetadata fieldMetaData; 041 042 private final ValueModel valueModel; 043 044 private final JComponent editor; 045 046 /** 047 * Creates a TableCellEditor. The property will be used to retrieve the {@link ValueModel} and 048 * {@link FieldMetadata}. The first is used while getting the initial value, the latter is used to 049 * determine if the cell is editable. Note that the given editor should be bound already. 050 * 051 * @param formModel 052 * model to use when committing/reverting. 053 * @param propertyName 054 * name of the property. 055 * @param editor 056 * JComponent bound to that property used in the editable state. 057 */ 058 public ValueModelTableCellEditor(FormModel formModel, String propertyName, JComponent editor) 059 { 060 this(formModel, formModel.getFieldMetadata(propertyName), formModel.getValueModel(propertyName), 061 editor); 062 } 063 064 public ValueModelTableCellEditor(AbstractForm form, String propertyName) 065 { 066 this(form.getFormModel(), propertyName, form.getBindingFactory().createBinding(propertyName).getControl()); 067 } 068 069 public ValueModelTableCellEditor(FormModel formModel, String propertyName, JComponent editor, ActionCommand commitCommand) 070 { 071 this(formModel, formModel.getFieldMetadata(propertyName), formModel.getValueModel(propertyName), 072 editor, commitCommand); 073 } 074 075 public ValueModelTableCellEditor(FormModel formModel, FieldMetadata fieldMetadata, ValueModel valueModel, 076 JComponent editor) 077 { 078 this(formModel, fieldMetadata, valueModel, editor, null); 079 } 080 081 /** 082 * Creates a TableCellEditor. The {@link ValueModel} is used while getting the initial value, the 083 * {@link FieldMetadata} is used to determine if the cell is editable. Note that the given editor should 084 * be bound already. 085 * 086 * @param formModel 087 * model to use when committing/reverting. 088 * @param valueModel 089 * valueModel to retrieve the initial value. 090 * @param fieldMetadata 091 * metaData to determine if the cell is editable. 092 * @param editor 093 * JComponent bound to that property used in the editable state. 094 */ 095 public ValueModelTableCellEditor(FormModel formModel, FieldMetadata fieldMetadata, ValueModel valueModel, 096 JComponent editor, ActionCommand commitCommand) 097 { 098 this.formModel = formModel; 099 this.valueModel = valueModel; 100 this.fieldMetaData = fieldMetadata; 101 this.editor = editor; 102 this.commitCommand = commitCommand; 103 } 104 105 public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, 106 int column) 107 { 108 TableModel tableModel = table.getModel(); 109 if (tableModel instanceof EventTableModel<?>) 110 { 111 formModel.setFormObject(((EventTableModel<?>)tableModel).getElementAt(row)); 112 } 113 if (editor instanceof JTextComponent) 114 ((JTextComponent)editor).selectAll(); 115 return editor; 116 } 117 118 @Override 119 public void cancelCellEditing() 120 { 121 formModel.revert(); 122 super.cancelCellEditing(); 123 } 124 125 public Object getCellEditorValue() 126 { 127 return valueModel.getValue(); 128 } 129 130 /** 131 * We cannot rely on the {@link FieldMetadata} because the form object is replaced AFTER this method 132 * is queried. We would be returning the previous value instead of the current one. 133 * Instead rely on the binding's component to set the editor read-only. 134 */ 135 @Override 136 public boolean isCellEditable(EventObject anEvent) 137 { 138 return true; 139 } 140 141 @Override 142 public boolean stopCellEditing() 143 { 144 if (!formModel.isDirty()) 145 return super.stopCellEditing(); 146 147 if (formModel.isCommittable()) 148 { 149 // if you've specified a commitCommand, all handling is left to you, otherwise just commit the form 150 if (commitCommand != null) 151 commitCommand.execute(); 152 else 153 formModel.commit(); 154 155 return super.stopCellEditing(); 156 } 157 158 return false; 159 } 160 161 }