package org.appwork.utils.swing.table;
import java.awt.Color;
import java.awt.Component;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.regex.Pattern;
import javax.swing.AbstractCellEditor;
import javax.swing.JComponent;
import javax.swing.JPopupMenu;
import javax.swing.JTable;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.text.JTextComponent;
import org.appwork.utils.logging.Log;
import org.appwork.utils.swing.EDTHelper;
/**
* ExtColums define a single column of an extended Table. It contains all
* columndetails including renderer
*
* @author $Author: unknown$
*/
public abstract class ExtColumn<E> extends AbstractCellEditor implements TableCellEditor, TableCellRenderer {
protected static Color background = null;
protected static Color backgroundselected = null;
protected static Color foreground = null;
protected static Color foregroundselected = null;
private static final long serialVersionUID = -2662459732650363059L;
/**
* If this colum is editable, this parameter says how many clicks are
* required to start edit mode
*/
private int clickcount = 2;
/**
* The model this column belongs to
*/
private ExtTableModel<E> model;
/**
* The columns Title.
*/
private final String name;
/**
* A toggle to select the next sortingorder. ASC or DESC
*/
private boolean sortOrderToggle = true;
/**
* Sorting algorithms run in an own thread
*/
private Thread sortThread = null;
private ExtDefaultRowSorter<E> rowSorter;
private String id;
private TableColumn tableColumn;
/**
* Create a new ExtColum.
*
* @param name
* @param table
* @param database
*/
public ExtColumn(final String name, final ExtTableModel<E> table) {
this.name = name;
this.model = table;
if (this.model != null) {
this.id = this.getClass().getSuperclass().getSimpleName() + "." + this.getClass().getName() + "." + (this.model.getColumnCount() + 1);
}
// sort function
this.rowSorter = new ExtDefaultRowSorter<E>();
}
/**
* @param value
* @param isSelected
* @param hasFocus
* @param row
*/
protected void adaptRowHighlighters(final E value, final JComponent comp, final boolean isSelected, final boolean hasFocus, final int row) {
comp.setOpaque(false);
// important for synthetica textcomponents
if (comp instanceof JTextComponent) {
comp.putClientProperty("Synthetica.opaque", Boolean.TRUE);
}
try {
for (final ExtComponentRowHighlighter<E> rh : this.getModel().getExtComponentRowHighlighters()) {
if (rh.highlight(this, comp, value, isSelected, hasFocus, row)) {
break;
}
}
} catch (final Throwable e) {
Log.exception(e);
}
}
/**
* @return
*/
public JPopupMenu createHeaderPopup() {
// TODO Auto-generated method stub
return null;
}
protected void doSort(final Object obj) {
if (this.sortThread != null) { return; }
this.sortThread = new Thread("TableSorter " + this.getID()) {
@Override
public void run() {
// get selections before sorting
final ArrayList<E> selections = ExtColumn.this.model.getSelectedObjects();
try {
// sort data
ExtColumn.this.sortOrderToggle = !ExtColumn.this.sortOrderToggle;
ExtColumn.this.getModel().sort(ExtColumn.this, ExtColumn.this.sortOrderToggle);
} catch (final Exception e) {
}
// switch toggle
ExtColumn.this.sortThread = null;
// Do this in EDT
new EDTHelper<Object>() {
@Override
public Object edtRun() {
// inform model about structure change
ExtColumn.this.model.fireTableStructureChanged();
// restore selection
ExtColumn.this.model.setSelectedObjects(selections);
return null;
}
}.start();
}
};
this.sortThread.start();
}
/**
* @param popup
*/
public void extendControlButtonMenu(final JPopupMenu popup) {
// TODO Auto-generated method stub
}
public abstract Object getCellEditorValue();
/**
* @return the {@link ExtColumn#clickcount}
* @see ExtColumn#clickcount
*/
public int getClickcount() {
return this.clickcount;
}
/**
* @return
*/
public int getDefaultWidth() {
return 100;
}
public JComponent getEditorComponent(final ExtTable<E> table, final E value, final boolean isSelected, final int row, final int column) {
return (JComponent) table.getLafCellEditor(row, column).getTableCellEditorComponent(table, value, isSelected, row, column);
}
/**
* @param jTableHeader
* @return
*/
public ExtTableHeaderRenderer getHeaderRenderer(final JTableHeader jTableHeader) {
// TODO Auto-generated method stub
return null;
}
/**
* The storageID for this column. Override this if you have a selfdefined
* column class which is used by several of your columns.
*
* @return
*/
public String getID() {
return this.id;
}
/**
* Should be overwritten when there should be a maximal width for this
* column (e.g. for checkboxes)
*/
protected int getMaxWidth() {
return -1;
}
/**
* @return
*/
public int getMinWidth() {
return 0;
}
/**
* @return the {@link ExtColumn#model}
* @see ExtColumn#model
*/
public ExtTableModel<E> getModel() {
return this.model;
}
/**
* @return the {@link ExtColumn#name}
* @see ExtColumn#name
*/
public String getName() {
return this.name;
}
public JComponent getRendererComponent(final ExtTable<E> table, final E value, final boolean isSelected, final boolean hasFocus, final int row, final int column) {
return (JComponent) table.getLafCellRenderer(row, column).getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
}
/**
* Returns null or a sorting comperator for this column
*
* @param sortToggle
* @return
*/
public ExtDefaultRowSorter<E> getRowSorter(final boolean sortOrderToggle) {
this.rowSorter.setSortOrderToggle(sortOrderToggle);
return this.rowSorter;
}
@SuppressWarnings("unchecked")
@Override
final public Component getTableCellEditorComponent(final JTable table, final Object value, final boolean isSelected, final int row, final int column) {
final JComponent ret = this.getEditorComponent((ExtTable<E>) table, (E) value, isSelected, row, column);
ret.setEnabled(true);
this.adaptRowHighlighters((E) value, ret, isSelected, true, row);
return ret;
}
@SuppressWarnings("unchecked")
@Override
final public Component getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, final boolean hasFocus, final int row, final int column) {
final JComponent ret = this.getRendererComponent((ExtTable<E>) table, (E) value, isSelected, hasFocus, row, column);
ret.setEnabled(this.isEnabled((E) value));
this.adaptRowHighlighters((E) value, ret, isSelected, hasFocus, row);
return ret;
}
public int getWidth() {
return this.tableColumn.getWidth();
}
@Override
public boolean isCellEditable(final EventObject evt) {
if (evt instanceof MouseEvent) { return ((MouseEvent) evt).getClickCount() >= this.getClickcount() && this.getClickcount() > 0; }
return true;
}
/**
* Returns if the cell is editable. Do NOT override this. Use
* {@link #isEditable(Object)} instead
*
* @param rowIndex
* @param columnIndex
* @return
*/
public boolean isCellEditable(final int rowIndex, final int columnIndex) {
final E obj = this.model.getValueAt(rowIndex, columnIndex);
if (obj == null) { return false; }
return this.isEditable(obj, this.isEnabled(obj));
}
public boolean isDefaultVisible() {
return true;
}
/**
* returns true if the column is editable for the object obj
*
* @param obj
* @return
*/
public abstract boolean isEditable(E obj);
/**
* override this to enable cell editing if the cell is disabled
*
* @param obj
* @param enabled
* @return if the row with obj is editable
*/
protected boolean isEditable(final E obj, final boolean enabled) {
return enabled && this.isEditable(obj);
}
/**
* returns if the cell defined by this column and the object is enabled or
* disabled
*
* @param obj
* @return
*/
abstract public boolean isEnabled(E obj);
/**
* returns if this column is allowed to be hidden
*
* @return
*/
public boolean isHidable() {
return true;
}
/**
* If you want to use only an icon in the table header, you can override
* this and let the method return false. This only works if
* {@link #getHeaderIcon()} returns an icon
*
* @return
*/
public boolean isPaintHeaderText() {
return true;
}
/**
* returns true if this column is sortable. if the call origin is an object,
* the object is passed in obj parameter. if the caller origin is the column
* header, obj is null
*
* @param obj
* @return
*/
abstract public boolean isSortable(E obj);
/**
* @return the {@link ExtColumn#sortOrderToggle}
* @see ExtColumn#sortOrderToggle
*/
protected boolean isSortOrderToggle() {
return this.sortOrderToggle;
}
public boolean matchSearch(final E object, final Pattern pattern) {
return false;
}
/**
* @param clickcount
* the {@link ExtColumn#clickcount} to set
* @see ExtColumn#clickcount
*/
public void setClickcount(final int clickcount) {
this.clickcount = Math.max(0, clickcount);
}
/**
* @param model
* the {@link ExtColumn#model} to set
* @see ExtColumn#model
*/
public void setModel(final ExtTableModel<E> model) {
this.model = model;
this.id = this.getClass().getSuperclass().getSimpleName() + "." + this.getClass().getName() + "." + model.getColumnCount();
}
/**
* @param rowSorter
* the {@link ExtColumn#rowSorter} to set
* @see ExtColumn#rowSorter
*/
public void setRowSorter(final ExtDefaultRowSorter<E> rowSorter) {
this.rowSorter = rowSorter;
}
/**
* Sets the real tableColumn.
*
* @param tableColumn
*/
public void setTableColumn(final TableColumn tableColumn) {
this.tableColumn = tableColumn;
}
/**
* USe this method to catch changed values.
*
* @param value
* the new value
* @param object
* the concerned object
*/
public abstract void setValue(Object value, E object);
public void setValueAt(final Object value, final int rowIndex, final int columnIndex) {
final E obj = this.model.getValueAt(rowIndex, columnIndex);
if (obj == null) { return; }
this.setValue(value, obj);
}
@Override
public boolean shouldSelectCell(final EventObject anEvent) {
return true;
}
}