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 }