/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenFlexo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openflexo.fib.view.widget.table;
import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.logging.Logger;
import javax.swing.AbstractCellEditor;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import org.openflexo.antar.binding.BindingVariable;
import org.openflexo.antar.binding.TypeUtils;
import org.openflexo.antar.expr.NullReferenceException;
import org.openflexo.antar.expr.TypeMismatchException;
import org.openflexo.fib.controller.FIBController;
import org.openflexo.fib.model.DataBinding;
import org.openflexo.fib.model.FIBCustom;
import org.openflexo.fib.model.FIBCustom.FIBCustomComponent;
import org.openflexo.fib.model.FIBCustomColumn;
import org.openflexo.fib.model.FIBCustomColumn.FIBCustomAssignment;
import org.openflexo.swing.CustomPopup.ApplyCancelListener;
import org.openflexo.toolbox.ToolBox;
/**
* Please comment this class
*
* @author sguerin
*
*/
public class CustomColumn<T extends Object> extends AbstractColumn<T> implements EditableColumn<T>, ApplyCancelListener {
private static final Logger logger = Logger.getLogger(CustomColumn.class.getPackage().getName());
private FIBCustomColumn _customColumn;
private FIBCustomComponent<T, ?> _viewCustomWidget;
private FIBCustomComponent<T, ?> _editCustomWidget;
private boolean useCustomViewForCellRendering;
private boolean disableTerminateEditOnFocusLost;
public CustomColumn(FIBCustomColumn columnModel, FIBTableModel tableModel, FIBController controller) {
super(columnModel, tableModel, controller);
_customColumn = columnModel;
_viewCustomWidget = makeCustomComponent((Class<FIBCustomComponent<T, ?>>) columnModel.getComponentClass(),
(Class<T>) columnModel.getDataClass());
_editCustomWidget = makeCustomComponent((Class<FIBCustomComponent<T, ?>>) columnModel.getComponentClass(),
(Class<T>) columnModel.getDataClass());
if (_editCustomWidget != null) {
_editCustomWidget.addApplyCancelListener(this);
}
_customCellRenderer = new CustomCellRenderer();
_customCellEditor = new CustomCellEditor();
useCustomViewForCellRendering = columnModel.customRendering;
disableTerminateEditOnFocusLost = columnModel.disableTerminateEditOnFocusLost;
}
@Override
public FIBCustomColumn getColumnModel() {
return (FIBCustomColumn) super.getColumnModel();
}
private FIBCustomComponent<T, ?> makeCustomComponent(Class<FIBCustomComponent<T, ?>> customComponentClass, Class<T> dataClass) {
Class[] types = new Class[1];
types[0] = dataClass;
try {
boolean found = false;
Constructor<FIBCustomComponent<T, ?>> constructor = null;
while (!found && types[0] != null && customComponentClass != null) {
try {
constructor = customComponentClass.getConstructor(types);
found = true;
} catch (NoSuchMethodException e) {
types[0] = types[0].getSuperclass();
}
}
if (constructor == null) {
logger.warning("Cound not instanciate class " + customComponentClass + " : no valid constructor found");
return null;
}
Object[] args = new Object[1];
args[0] = null;
return constructor.newInstance(args);
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
@Override
public Class<T> getValueClass() {
return TypeUtils.getBaseClass(getColumnModel().getDataClass());
}
@Override
public boolean isCellEditableFor(Object object) {
return true;
}
@Override
public String toString() {
return "CustomColumn " + "[" + getTitle() + "]" + Integer.toHexString(hashCode());
}
// Returns true as cell renderer is required here
@Override
public boolean requireCellRenderer() {
return true;
}
@Override
public TableCellRenderer getCellRenderer() {
return _customCellRenderer;
}
private CustomCellRenderer _customCellRenderer;
protected class CustomCellRenderer extends FIBTableCellRenderer<T> {
public CustomCellRenderer() {
super(CustomColumn.this);
setFont(CustomColumn.this.getFont());
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
Component c;
if (isSelected && hasFocus || useCustomViewForCellRendering) {
FIBCustomComponent<T, ?> customWidgetView = getViewCustomWidget(elementAt(row));
if (customWidgetView != null) {
c = customWidgetView.getJComponent();
} else {
c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
}
Color fg = null;
Color bg = null;
if (isSelected) {
c.setForeground(fg == null ? table.getSelectionForeground() : fg);
c.setBackground(bg == null ? table.getSelectionBackground() : bg);
} else {
c.setForeground(table.getForeground());
c.setBackground(table.getBackground());
}
setFont(table.getFont());
if (c instanceof JComponent) {
if (hasFocus) {
Border border = null;
if (isSelected) {
border = UIManager.getBorder("Table.focusSelectedCellHighlightBorder");
}
if (border == null) {
border = UIManager.getBorder("Table.focusCellHighlightBorder");
}
((JComponent) c).setBorder(border);
}
if (hasFocus && !isSelected && table.isCellEditable(row, column)) {
Color col;
col = UIManager.getColor("Table.focusCellForeground");
if (col != null) {
c.setForeground(col);
}
col = UIManager.getColor("Table.focusCellBackground");
if (col != null) {
c.setBackground(col);
}
}
}
} else {
Component returned = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (returned instanceof JLabel) {
((JLabel) returned).setText(renderValue((T) value));
if (ToolBox.isMacOSLaf()) {
((JLabel) returned).setForeground(getColorFor(value));
}
((JLabel) returned).setFont(CustomColumn.this.getFont());
}
c = returned;
}
if (c instanceof JComponent) {
((JComponent) c).setToolTipText(getTooltip(elementAt(row)));
}
return c;
}
}
protected Color getColorFor(Object value) {
return Color.black;
// return _viewCustomWidget.getColorForObject(value);
}
protected FIBCustomComponent<T, ?> getViewCustomWidget(Object rowObject) {
if (_viewCustomWidget != null) {
T value = getValueFor(rowObject);
_viewCustomWidget.setEditedObject(value);
_viewCustomWidget.setRevertValue(value);
logger.fine("Return _viewCustomWidget for model rowObject=" + rowObject + " value=" + value);
}
return _viewCustomWidget;
}
protected FIBCustomComponent<T, ?> getEditCustomWidget(Object rowObject) {
if (_editCustomWidget != null) {
T value = getValueFor(rowObject);
_editCustomWidget.setEditedObject(value);
_editCustomWidget.setRevertValue(value);
logger.fine("Return _editCustomWidget for model rowObject=" + rowObject + " value=" + value);
// setEditedRowObject(value);
}
return _editCustomWidget;
}
// Returns true as cell editor is required here
@Override
public boolean requireCellEditor() {
return true;
}
// Must be overriden if required
@Override
public TableCellEditor getCellEditor() {
return _customCellEditor;
}
private CustomCellEditor _customCellEditor;
protected class CustomCellEditor extends AbstractCellEditor implements TableCellEditor, ActionListener {
FIBCustomComponent<T, ?> _customWidget;
public CustomCellEditor() {
_customWidget = getEditCustomWidget(null);
}
@Override
public void actionPerformed(ActionEvent e) {
fireEditingStopped();
}
@Override
protected void fireEditingCanceled() {
// if (_customWidget != null) _customWidget.fireEditingCanceled();
super.fireEditingCanceled();
}
@Override
protected void fireEditingStopped() {
// if (_customWidget != null) _customWidget.fireEditingStopped();
super.fireEditingStopped();
}
// Implement the one CellEditor method that AbstractCellEditor doesn't.
@Override
public Object getCellEditorValue() {
if (_customWidget == null) {
return null;
}
return _customWidget.getEditedObject();
}
// Implement the one method defined by TableCellEditor.
@Override
public Component getTableCellEditorComponent(final JTable table, Object value, boolean isSelected, int row, int column) {
// logger.info("elementAt(row)="+elementAt(row));
if (disableTerminateEditOnFocusLost) {
table.putClientProperty("terminateEditOnFocusLost", Boolean.FALSE);
}
setEditedRowObject(elementAt(row));
_customWidget.setEditedObject(getValueFor(elementAt(row)));
if (disableTerminateEditOnFocusLost) {
_customWidget.addApplyCancelListener(new ApplyCancelListener() {
@Override
public void fireApplyPerformed() {
table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
}
@Override
public void fireCancelPerformed() {
table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
}
});
}
JComponent jComponent = _customWidget.getJComponent();
jComponent.setBorder(null);
return jComponent;
}
}
protected Object _editedRowObject;
protected void setEditedRowObject(Object anObject) {
// logger.info("setEditedRowObject with " + anObject);
_editedRowObject = anObject;
for (FIBCustomAssignment assign : getColumnModel().getAssignments()) {
DataBinding variableDB = assign.getVariable();
DataBinding valueDB = assign.getValue();
// logger.info("Assignment");
// logger.info("variableDB="+variableDB+" valid="+variableDB.getBinding().isBindingValid());
// logger.info("valueDB="+valueDB+" valid="+valueDB.getBinding().isBindingValid());
if (valueDB.getBinding().isBindingValid()) {
Object value = null;
try {
value = valueDB.getBinding().getBindingValue(this);
} catch (TypeMismatchException e) {
e.printStackTrace();
} catch (NullReferenceException e) {
e.printStackTrace();
}
// logger.info("value="+value);
if (variableDB.getBinding().isBindingValid()) {
// System.out.println("Assignment "+assign+" set value with "+value);
variableDB.getBinding().setBindingValue(value, this);
}
}
}
}
@Override
public Object getValue(BindingVariable variable) {
if (variable.getVariableName().equals(FIBCustom.COMPONENT_NAME)) {
return _editCustomWidget;
} else {
return super.getValue(variable);
}
}
@Override
public void fireApplyPerformed() {
logger.fine("fireApplyPerformed() for " + _editedRowObject);
setValueFor(_editedRowObject, _editCustomWidget.getEditedObject());
notifyValueChangedFor(_editedRowObject, _editCustomWidget.getEditedObject());
}
@Override
public void fireCancelPerformed() {
}
protected String renderValue(T value) {
if (value == null) {
return "";
}
return getStringRepresentation(value);
}
}