/**
* Copyright (C) 2015 Valkyrie RCP
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.valkyriercp.widget.table;
import ca.odell.glazedlists.swing.EventTableModel;
import org.valkyriercp.binding.form.FieldMetadata;
import org.valkyriercp.binding.form.FormModel;
import org.valkyriercp.binding.value.ValueModel;
import org.valkyriercp.command.support.ActionCommand;
import org.valkyriercp.form.AbstractForm;
import javax.swing.*;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableModel;
import javax.swing.text.JTextComponent;
import java.awt.*;
import java.util.EventObject;
/**
* {@link javax.swing.table.TableCellEditor} that uses a backing {@link FormModel} to determine the editing capabilities and
* committing of the value.
*
* <p>
* NOTE: the CellEditor will get the first event, only afterwards the row selection changes. This has the
* effect that editing a cell will switch values upon entering. Additionally the cell should always be marked
* editable and the binding that acts as the editor should disable itself if needed. If we were to rely on
* {@link FieldMetadata#isReadOnly()}, the previous value would determine if the cell is editable because
* the underlying form object isn't changed yet.
* </p>
*
* @author Jan Hoskens
*
*/
public class ValueModelTableCellEditor extends AbstractCellEditor implements TableCellEditor
{
private final FormModel formModel;
private final ActionCommand commitCommand;
private final FieldMetadata fieldMetaData;
private final ValueModel valueModel;
private final JComponent editor;
/**
* Creates a TableCellEditor. The property will be used to retrieve the {@link ValueModel} and
* {@link FieldMetadata}. The first is used while getting the initial value, the latter is used to
* determine if the cell is editable. Note that the given editor should be bound already.
*
* @param formModel
* model to use when committing/reverting.
* @param propertyName
* name of the property.
* @param editor
* JComponent bound to that property used in the editable state.
*/
public ValueModelTableCellEditor(FormModel formModel, String propertyName, JComponent editor)
{
this(formModel, formModel.getFieldMetadata(propertyName), formModel.getValueModel(propertyName),
editor);
}
public ValueModelTableCellEditor(AbstractForm form, String propertyName)
{
this(form.getFormModel(), propertyName, form.getBindingFactory().createBinding(propertyName).getControl());
}
public ValueModelTableCellEditor(FormModel formModel, String propertyName, JComponent editor, ActionCommand commitCommand)
{
this(formModel, formModel.getFieldMetadata(propertyName), formModel.getValueModel(propertyName),
editor, commitCommand);
}
public ValueModelTableCellEditor(FormModel formModel, FieldMetadata fieldMetadata, ValueModel valueModel,
JComponent editor)
{
this(formModel, fieldMetadata, valueModel, editor, null);
}
/**
* Creates a TableCellEditor. The {@link ValueModel} is used while getting the initial value, the
* {@link FieldMetadata} is used to determine if the cell is editable. Note that the given editor should
* be bound already.
*
* @param formModel
* model to use when committing/reverting.
* @param valueModel
* valueModel to retrieve the initial value.
* @param fieldMetadata
* metaData to determine if the cell is editable.
* @param editor
* JComponent bound to that property used in the editable state.
*/
public ValueModelTableCellEditor(FormModel formModel, FieldMetadata fieldMetadata, ValueModel valueModel,
JComponent editor, ActionCommand commitCommand)
{
this.formModel = formModel;
this.valueModel = valueModel;
this.fieldMetaData = fieldMetadata;
this.editor = editor;
this.commitCommand = commitCommand;
}
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row,
int column)
{
TableModel tableModel = table.getModel();
if (tableModel instanceof EventTableModel<?>)
{
formModel.setFormObject(((EventTableModel<?>)tableModel).getElementAt(row));
}
if (editor instanceof JTextComponent)
((JTextComponent)editor).selectAll();
return editor;
}
@Override
public void cancelCellEditing()
{
formModel.revert();
super.cancelCellEditing();
}
public Object getCellEditorValue()
{
return valueModel.getValue();
}
/**
* We cannot rely on the {@link FieldMetadata} because the form object is replaced AFTER this method
* is queried. We would be returning the previous value instead of the current one.
* Instead rely on the binding's component to set the editor read-only.
*/
@Override
public boolean isCellEditable(EventObject anEvent)
{
return true;
}
@Override
public boolean stopCellEditing()
{
if (!formModel.isDirty())
return super.stopCellEditing();
if (formModel.isCommittable())
{
// if you've specified a commitCommand, all handling is left to you, otherwise just commit the form
if (commitCommand != null)
commitCommand.execute();
else
formModel.commit();
return super.stopCellEditing();
}
return false;
}
}