package jeql.workbench.ui.data;
import java.awt.Color;
import java.awt.Component;
import java.awt.SystemColor;
import java.awt.event.MouseEvent;
import java.util.HashMap;
import java.util.Map;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.event.TableColumnModelListener;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;
import jeql.api.row.RowSchema;
import jeql.monitor.MonitorRowList;
import jeql.style.StyleConstants;
import jeql.util.ClassUtil;
import jeql.util.ColorUtil;
import jeql.util.MethodUtil;
import jeql.workbench.Workbench;
import jeql.workbench.WorkbenchConstants;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.io.WKTWriter;
class RowListTable extends JTable
{
private static Map<String, RowListColumnStyle> colStyleMap = new HashMap<String, RowListColumnStyle>();
private static RowListColumnStyle findColStyle(RowSchema schema)
{
String key = RowListColumnStyle.columnKey(schema);
//System.out.println("Finding style for " + key);
RowListColumnStyle cs = colStyleMap.get(key);
if (cs == null) {
cs = new RowListColumnStyle(key, schema);
colStyleMap.put(key, cs);
//System.out.println("Creating style...");
}
return cs;
}
private static final Color LIGHT_GRAY = new Color(220, 220, 220);
private boolean useMonospacedFont = true;
private boolean[] isColorColumn;
private RowListColumnStyle colStyle;
public RowListTable(MonitorRowList mrl)
{
setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
// setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
setModel(new RowListTableModel(mrl));
int numCols = mrl.getSchema().size();
isColorColumn = colorColumns(mrl.getSchema());
init();
// allow sorting if available in Java version
MethodUtil.invokeVoidSafe(this, "setAutoCreateRowSorter", boolean.class, Boolean.TRUE);
//setAutoCreateRowSorter(true);
}
public void setMonospaced(boolean useMonospacedFont)
{
this.useMonospacedFont = useMonospacedFont;
}
private static final int ROW_NUM_COLUMN_WIDTH = 60;
private void init()
{
// getColumnModel().setColumnMargin(4);
RowListTableModel model = (RowListTableModel) getModel();
colStyle = findColStyle(model.getSchema());
for (int i = 0; i < model.getColumnCount(); i++) {
TableColumn col = getColumnModel().getColumn(i);
// first column is row number
if (i == 0) {
col.setMinWidth(ROW_NUM_COLUMN_WIDTH);
col.setPreferredWidth(ROW_NUM_COLUMN_WIDTH);
col.setMaxWidth(ROW_NUM_COLUMN_WIDTH);
}
else if (i < model.getColumnCount()) {
// don't set width for last column, to allow it to stretch
//col.setPreferredWidth(100);
col.setPreferredWidth(colStyle.width(i - 1));
//System.out.print("col " + (i-1) + " to width " + colStyle.width(i - 1) + " -style " + colStyle.getKey());
}
col.setHeaderRenderer(new RowListTableHeaderRenderer(getTableHeader()
.getDefaultRenderer()));
if (model.getColumnClass(i) == Double.class) {
col.setCellRenderer(new DoubleFormatRenderer());
}
}
getColumnModel().addColumnModelListener(new TableColumnModelListener() {
public void columnMarginChanged(ChangeEvent arg0)
{
updateColStyle();
}
public void columnAdded(TableColumnModelEvent arg0) { }
public void columnMoved(TableColumnModelEvent arg0) {}
public void columnRemoved(TableColumnModelEvent arg0) {}
public void columnSelectionChanged(ListSelectionEvent arg0){}
});
addMouseListener(new java.awt.event.MouseAdapter() {
@Override
public void mouseClicked(java.awt.event.MouseEvent evt) {
if (evt.getClickCount() < 2)
return;
int row = rowAtPoint(evt.getPoint());
int col = columnAtPoint(evt.getPoint());
if (row >= 0 && col >= 0) {
Object val = ((RowListTableModel) getModel()).getRawValueAt(row, col);
//System.out.println(val);
Workbench.controller().inspect(inspectString(val));
}
}
});
}
private static String inspectString(Object o)
{
if (o == null) return "";
if (o instanceof Geometry) {
WKTWriter writer = new WKTWriter();
writer.setMaxCoordinatesPerLine(2);
String str = writer.writeFormatted((Geometry) o);
return str;
}
return o.toString();
}
private void updateColStyle()
{
for (int i = 0; i < getModel().getColumnCount(); i++) {
if (i > 0) {
TableColumn col = getColumnModel().getColumn(i);
colStyle.setWidth(i - 1, col.getPreferredWidth());
}
}
}
//Implement table header tool tips.
protected JTableHeader createDefaultTableHeader() {
return new JTableHeader(columnModel) {
public String getToolTipText(MouseEvent e) {
String tip = null;
java.awt.Point p = e.getPoint();
int colIndex = columnModel.getColumnIndexAtX(p.x);
if (colIndex < 0) return "";
int realIndex = columnModel.getColumn(colIndex).getModelIndex();
return ClassUtil.classname(getModel().getColumnClass(realIndex));
}
};
}
/**
* Override <tt>prepareRenderer()</tt> to set the font size in the instances
* of the cell renderers returned by the Look-And-Feel's renderer.
*/
public Component prepareRenderer(TableCellRenderer renderer, int row,
int col)
{
Component c = super.prepareRenderer(renderer, row, col);
if (getModel().getColumnClass(col) == String.class && useMonospacedFont)
c.setFont(WorkbenchConstants.FONT_LUCIDA_CONSOLE);
return c;
}
public TableCellRenderer getCellRenderer(int row, int col)
{
// System.out.println(" getCellRenderer " + row + ", " + column);
JLabel renderer = (JLabel) super.getCellRenderer(row, col);
//-------- Row striping
if (col == 0) {
// row number column
renderer.setBackground(SystemColor.control);
}
else {
// data column
renderer.setBackground(((row % 2) == 0) ? Color.white : LIGHT_GRAY);
}
//--------- Show repeated column values
if ( ((RowListTableModel) getModel()).isShowSpaces()
&& isRepeatedColumnValue(row, col)) {
renderer.setBackground(Color.GRAY);
}
// show strings in different colour
if (getModel().getColumnClass(col) == String.class) {
renderer.setForeground(WorkbenchConstants.CLR_DARK_BLUE);
}
else if (getModel().getColumnClass(col) == Geometry.class) {
renderer.setForeground(WorkbenchConstants.CLR_DARK_GREEN);
}
if (col > 0 && isColorColumn[col - 1]) {
Color clr = ColorUtil.RGBAtoColor((String) getModel().getValueAt(row, col));
if (clr == null) {
clr = Color.white;
}
renderer.setBackground(clr);
renderer.setForeground(readableForeground(clr));
}
return (TableCellRenderer) renderer;
}
private boolean isRepeatedColumnValue(int row, int col)
{
if (row == 0) return false;
Object o = getModel().getValueAt(row, col);
if (o == null) return false;
Object oprev = getModel().getValueAt(row-1, col);
if (o.equals(oprev))
return true;
return false;
}
private static Color readableForeground(Color background)
{
//float[] hsv = ColorUtil.toHSV(background);
//boolean isDark = hsv[1] > 0.8 && hsv[2] < 0.5;
boolean isDark = background.getRed() + background.getGreen() + background.getBlue() > 384; // = 3 * 128
if (isDark) return Color.BLACK;
return Color.WHITE;
}
private static boolean[] colorColumns(RowSchema schema)
{
int numCols = schema.size();
boolean[] isColorColumn = new boolean[numCols];
for (int i = 0; i < numCols; i++) {
setColorCol(StyleConstants.COLOR, schema, isColorColumn);
setColorCol(StyleConstants.STROKE, schema, isColorColumn);
setColorCol(StyleConstants.FONT_COLOR, schema, isColorColumn);
setColorCol(StyleConstants.HALO_COLOR, schema, isColorColumn);
setColorCol(StyleConstants.FILL, schema, isColorColumn);
setColorCol(StyleConstants.COL_MARKER_COLOR, schema, isColorColumn);
}
return isColorColumn;
}
private static void setColorCol(String clrColName, RowSchema schema, boolean[] isColorColumn)
{
int i = schema.getColIndex(clrColName);
if (i < 0) return;
isColorColumn[i] = true;
}
}
class RowListTableHeaderRenderer implements TableCellRenderer
{
TableCellRenderer originalRenderer;
RowListTableHeaderRenderer(TableCellRenderer originalRenderer)
{
this.originalRenderer = originalRenderer;
}
// This method is called each time a column header
// using this renderer needs to be rendered.
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int col)
{
JLabel label = (JLabel) originalRenderer.getTableCellRendererComponent(
table, value, isSelected, hasFocus, row, col);
// Configure the component with the specified value
label.setText(value.toString());
// Set tool tip if desired
// setToolTipText((String)value);
label.setBackground(SystemColor.control);
// setForeground(SystemColor.control);
// Since the renderer is a component, return itself
return label;
}
// The following methods override the defaults for performance reasons
public void validate()
{
}
public void revalidate()
{
}
protected void firePropertyChange(String propertyName, Object oldValue,
Object newValue)
{
}
public void firePropertyChange(String propertyName, boolean oldValue,
boolean newValue)
{
}
}