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    }