/*
 * ECatalog is a database front-end, with two main features:
 * 1. Use of preferences
 *  A preference-based approach, where the user is allowed to define the importance of each criterion.
 *  Then the items are ranked accordingly to his criteria.
 * 2. Trade-off analysis
 *  A cooperative database approach, where the system "argues" with the user about his criteria.
 *  When there are no matching items, the system explains the minimal conflicting set and
 *  give some possible strong and weak relaxations about his criteria.
 * This package also containts the software and the set-up details used for our User Study,
 * comparing the use or not of the two previous features mentioned above.
 *
 * Copyright (C) 2006 David Portabella Clotet, Artificial Intelligence Laboratory, EPFL
 * 
 * This file is part of ecatalog-1.0.zip
 * 
 * ECatalog is free software and a free user study set-up;
 * 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 2 of the License, or
 * (at your option) any later version.
 * 
 * ECatalog 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 ECatalog; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 * @version 1.0
 * @author David Portabella
 * To contact the author:
 * email: david@portabella.name and david.portabella@epfl.ch
 * 
 * More information about ECatalog:
 *  http://sourceforge.net/projects/ecatalog/
 *  http://icwww.epfl.ch/~portabel/ecatalogs/
 */

package utils.itemTable;

import utils.ArrowIcon;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.*;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.EventObject;

public class ItemTable extends JTable {
    ItemTableModel itemModel;
    ItemTableRenderer itemRenderer;
    ItemTableCellRenderer defaultItemTableCellRenderer;
    ItemTableCellEditor defaultItemTableCellEditor;
    
    LinkedList<ItemTableListener> itemTableListeners = new LinkedList<ItemTableListener>();

    MyTableModel tableModel;
    int[] itemRowsHeight;
    SelectorCellRenderer selectorCellRenderer;
    SelectorCellEditor selectorCellEditor;

    int selectedItemIndex = -1;

    CellIndex sortCellIndex;
    ItemTableModel.SortDirection sortDirection;

    int defaultHeaderComponentHeight;

    public ItemTable(ItemTableModel itemModel, ItemTableRenderer itemRenderer) {
	super();

	defaultHeaderComponentHeight = getDefaultHeaderComponentHeight();
        setUI(new MyUI());

	this.itemModel = itemModel;
	this.itemRenderer = itemRenderer;

	tableModel = new MyTableModel();
	setModel(tableModel);

	selectorCellRenderer = new SelectorCellRenderer();
	selectorCellEditor = new SelectorCellEditor();
	defaultItemTableCellRenderer = new DefaultItemTableCellRenderer();
	defaultItemTableCellEditor = new DefaultItemTableCellEditor();

	setColumnSelectionAllowed(false);
	setRowSelectionAllowed(true);
	setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
	setShowVerticalLines(true);
	setShowHorizontalLines(false);
	setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
	getSelectionModel().addListSelectionListener(new MyListSelectionListener());

        setTableHeader(new ItemTableHeader());

	computeRowsHeight();
	adjustColsWidth();
    }

    /* Add a listener */
    public void addItemTableListener(ItemTableListener l) { itemTableListeners.add(l); }

    /* Remove a listener */
    public void removeItemTableListener(ItemTableListener l) { itemTableListeners.remove(l); }

    public ItemTableModel getItemTableModel() { return itemModel; }

    public ItemTableRenderer getItemTableRenderer() { return itemRenderer; }

    /* Returns the selected item index, or -1 if none is selected */
    public int getSelectedItemIndex() {
	return selectedItemIndex;
    }

    /* Returns the selected item index, or -1 if none is selected */
    public Object getSelectedItem() {
	if (selectedItemIndex == -1)
	    return null;

	return itemModel.getItem(selectedItemIndex);
    }

    public void selectItem(int itemIndex) {
	selectItem(itemIndex, false, false);
    }

    boolean filterSwingCall = false;
    public void selectItem(int itemIndex, boolean notifyListeners, boolean swingCall) {
	if (selectedItemIndex != -1)
	    tableModel.fireTableRowsUpdated(selectedItemIndex * itemRenderer.getNbrItemRows(), 
					    (selectedItemIndex + 1) * itemRenderer.getNbrItemRows()-1);

	if (!swingCall) {
	    filterSwingCall = true;
	    clearSelection();
	    filterSwingCall = false;
	}

	selectedItemIndex = itemIndex;

	if (selectedItemIndex != -1) {
	    tableModel.fireTableRowsUpdated(selectedItemIndex * itemRenderer.getNbrItemRows(), 
					    (selectedItemIndex + 1) * itemRenderer.getNbrItemRows()-1);
	    
	    if (notifyListeners) {
		for (ItemTableListener itemTableListener : itemTableListeners)
		    itemTableListener.itemSelected(selectedItemIndex);
	    }
	}
    }
    
    public void sortBy(CellIndex cellIndex, ItemTableModel.SortDirection sortDirection) {
	int attributeIndex = (cellIndex == null) ? -1 : itemRenderer.getItemAttributeIndex(cellIndex.itemRow, cellIndex.tableColumn);
	
	if (attributeIndex != -1 && itemModel.isSortableByAttribute(attributeIndex)) {
	    sortCellIndex = cellIndex;
	    sortDirection = ItemTableModel.SortDirection.ASCENDING;
	} else {
	    sortCellIndex = null;
	}
	itemModel.sortBy(attributeIndex, sortDirection);
	getTableHeader().repaint();
    }


    public void update() {
	//changingStructure = true;
	scrollTableToBeginning();
	selectedItemIndex = -1;

	tableModel.fireTableDataChanged();

	adjustRowsHeight();

	//changingStructure = false;
    }


    public void setDefaultItemTableCellRenderer(ItemTableCellRenderer defaultItemTableCellRenderer) {
	this.defaultItemTableCellRenderer = defaultItemTableCellRenderer;
    }

    public void setDefaultItemTableCellEditor(ItemTableCellEditor defaultItemTableCellEditor) {
	this.defaultItemTableCellEditor = defaultItemTableCellEditor;
    }

    
    public TableCellRenderer getCellRenderer(int tableRow, int tableCol) {
	return selectorCellRenderer;
    }

    public TableCellEditor getCellEditor(int tableRow, int tableCol) {
	selectorCellEditor.init(tableRow, tableCol);
	return selectorCellEditor;
    }

    public ItemTableCellRenderer getItemTableCellRenderer(int itemIndex, int itemRow, int tableCol) {
	ItemTableCellRenderer r = itemRenderer.getItemTableCellRenderer(itemIndex, itemRow, tableCol);
	return (r != null) ? r : defaultItemTableCellRenderer;
    }

    public ItemTableCellEditor getItemTableCellEditor(int itemIndex, int itemRow, int tableCol) {
	ItemTableCellEditor r = itemRenderer.getItemTableCellEditor(itemIndex, itemRow, tableCol);
	return (r != null) ? r : defaultItemTableCellEditor;
    }


    
    void computeRowsHeight() {
	int nbrItemRows = itemRenderer.getNbrItemRows();
	int nbrTableCols = itemRenderer.getNbrColumns();

	itemRowsHeight = new int[nbrItemRows];

	for (int itemRow = 0; itemRow < nbrItemRows; itemRow++) {
	    int rowHeight = -2;
	    for (int tableCol = 0; tableCol < nbrTableCols; tableCol++) {
		CellIndex cellIndex = itemRenderer.getVisibleCell(itemRow, tableCol);
		if (cellIndex.itemRow != itemRow)
		    continue;
		CellSpan cellSpan = itemRenderer.getCellSpan(itemRow, cellIndex.tableColumn);
		if (cellSpan.numRows != 1)
		    continue;
		CellSize cellSize = itemRenderer.getCellSize(itemRow, cellIndex.tableColumn);
		if (cellSize == null || cellSize.height == -1) {
		    if (rowHeight == -2)
			rowHeight = -1;
		} else if (cellSize.height > rowHeight) {
		    rowHeight = cellSize.height;
		}
	    }
	    if (rowHeight == -2)
		throw new Error("At least a cell must have cellSpan.numRows == 1 for each row");
	    itemRowsHeight[itemRow] = rowHeight;
	}
    }

    void adjustRowsHeight() {
	int nbrTableRows = itemModel.getNbrItems() * itemRenderer.getNbrItemRows();
	int nbrItemRows = itemRenderer.getNbrItemRows();
	for (int tableRow = 0; tableRow < nbrTableRows; tableRow++) {
	    int height = itemRowsHeight[tableRow % nbrItemRows];
	    if (height != -1)
		setRowHeight(tableRow, height);
	}
    }

    void adjustColsWidth() {
	int nbrItemRows = itemRenderer.getNbrItemRows();
	int nbrTableCols = itemRenderer.getNbrColumns();

	for (int tableCol = 0; tableCol < nbrTableCols; tableCol++) {
	    int colWidth = -2;
	    for (int itemRow = 0; itemRow < nbrItemRows; itemRow++) {
		CellIndex cellIndex = itemRenderer.getVisibleCell(itemRow, tableCol);
		if (cellIndex.tableColumn != tableCol)
		    continue;
		CellSpan cellSpan = itemRenderer.getCellSpan(cellIndex.itemRow, tableCol);
		if (cellSpan.numColumns != 1)
		    continue;
		CellSize cellSize = itemRenderer.getCellSize(cellIndex.itemRow, tableCol);
		if (cellSize == null || cellSize.width == -1) {
		    if (colWidth == -2)
			colWidth = -1;
		} else if (cellSize.width > colWidth) {
		    colWidth = cellSize.width;
		}
	    }
	    if (colWidth == -2)
		throw new Error("At least a cell must have cellSpan.numRows == 1 for each row");

	    if (colWidth != -1)
		getColumnModel().getColumn(tableCol).setPreferredWidth(colWidth);

	    getColumnModel().getColumn(tableCol).setMinWidth(0); 
	}
    }

    void scrollTableToBeginning() {
	//JViewport viewport = (JViewport)itemsTable.getParent();
	//viewport.scrollRectToVisible(new Rectangle(0,0,1,1));
	JViewport viewport = (JViewport)getParent();
	if (viewport != null)
	    viewport.setViewPosition(new Point(0, 0));
    }
    
    int getItemIndex(int tableRow) {
	return tableRow / itemRenderer.getNbrItemRows();
    }
    
    int getItemRow(int tableRow) {
	return tableRow % itemRenderer.getNbrItemRows();
    }

    boolean isCellVisible(int itemRow, int tableColumn) {
	CellIndex cellIndex = itemRenderer.getVisibleCell(itemRow, tableColumn);
	return (itemRow == cellIndex.itemRow && tableColumn == cellIndex.tableColumn);
    }

    class MyListSelectionListener implements ListSelectionListener { 
	public void valueChanged(ListSelectionEvent e) {
	    if (filterSwingCall)
		return;

	    //e.getValueIsAdjusting() returns true when pressed the mouse, and false when unpressed the mouse
	    if (e.getValueIsAdjusting())
		return;

	    ListSelectionModel lsm = (ListSelectionModel)e.getSource();
	    if (!lsm.isSelectionEmpty()) {
		int tableRow = lsm.getMinSelectionIndex();
		int itemIndex = getItemIndex(tableRow);
		selectItem(itemIndex, true, true);
	    } else {
		selectItem(-1, false, true);
	    }
	}
    }

    class MyTableModel extends AbstractTableModel {
	public int getRowCount() {
	    return itemModel.getNbrItems() * itemRenderer.getNbrItemRows(); 
	}

	public int getColumnCount() { 
	    return itemRenderer.getNbrColumns(); 
	}

	/* This function it is not used anymore */
	public String getColumnName(int tableCol) {
	    return null;
	}

	public Object getValueAt(int tableRow, int tableCol) {
	    int itemIndex = getItemIndex(tableRow);
	    int itemRow = getItemRow(tableRow);

	    Object item = itemModel.getItem(itemIndex);
	    return itemRenderer.getValueAt(item, itemRow, tableCol);
	}

	public boolean isCellEditable(int row, int col) {
	    return itemRenderer.isCellEditable(getItemIndex(row), getItemRow(row), col);
	}
    }



    class SelectorCellRenderer implements TableCellRenderer {
	public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int tableRow, int tableCol) {
	    int itemRow = getItemRow(tableRow);
	    int itemIndex = getItemIndex(tableRow);

	    //System.out.println("paint: value="+value+",itemIndex="+itemIndex+",itemRow="+itemRow+",tableCol="+tableCol);
	    ItemTableCellRenderer r = getItemTableCellRenderer(itemIndex, itemRow, tableCol);
	    Component c = r.getItemTableCellRendererComponent((ItemTable)table, value, isSelected, hasFocus, itemIndex, itemRow, tableCol);
	    if (c != null) {
		//c.setBackground((itemIndex == selectedItemIndex) ? table.getSelectionBackground() : table.getBackground());
		if (itemIndex == selectedItemIndex) {
		    c.setBackground(table.getSelectionBackground());
		}
	    }

	    return c;
	}
    }

    class SelectorCellEditor implements TableCellEditor {
	ItemTableCellEditor r;

	void init(int tableRow, int tableCol) {
	    int itemRow = getItemRow(tableRow);
	    int itemIndex = getItemIndex(tableRow);

	    r = getItemTableCellEditor(itemIndex, itemRow, tableCol);
	}

	public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int tableRow, int tableCol) {
	    int itemRow = getItemRow(tableRow);
	    int itemIndex = getItemIndex(tableRow);

	    Component c = r.getItemTableCellEditorComponent((ItemTable)table, value, isSelected, itemIndex, itemRow, tableCol);

	    if (itemIndex == selectedItemIndex) {
		c.setBackground(table.getSelectionBackground());
	    }
	    return c;
	}

	public Object getCellEditorValue() { return r.getCellEditorValue(); }
	public boolean isCellEditable(EventObject anEvent) { return r.isCellEditable(anEvent); }
	public boolean shouldSelectCell(EventObject anEvent) { return r.shouldSelectCell(anEvent); }
	public boolean stopCellEditing() { return r.stopCellEditing(); }
	public void cancelCellEditing() { r.cancelCellEditing(); }
	public void addCellEditorListener(CellEditorListener l) { r.addCellEditorListener(l); }
	public void removeCellEditorListener(CellEditorListener l) { r.removeCellEditorListener(l); }
    }



    /**
     * Define the cell rectangle for a given cell.
     *
     * @param row            row coordinate
     * @param column         column coordinate
     * @param includeSpacing flag
     * @return					a rectangle describing the visible area of the cell
     */
    public Rectangle getCellRect(int row, int column, boolean includeSpacing) {
	// required because getCellRect is used in JTable constructor
	if (itemRenderer == null)
	    return super.getCellRect(row, column, includeSpacing);
	int itemIndex = getItemIndex(row);
	int itemRow = getItemRow(row);
	CellIndex cellIndex = itemRenderer.getVisibleCell(itemRow, column);
	Rectangle r1 = super.getCellRect(cellIndex.itemRow + itemIndex * itemRenderer.getNbrItemRows(), cellIndex.tableColumn, includeSpacing);

	CellSpan cellSpan = itemRenderer.getCellSpan(itemRow, column);

	// add widths of all spanned logical cells
	for (int i = 1; i < cellSpan.numColumns; i++)
	    r1.width += getColumnModel().getColumn(cellIndex.tableColumn + i).getWidth();

	// add height of all spanned logical cells
	for (int i = 1; i < cellSpan.numRows; i++)
	    r1.height += getRowHeight(cellIndex.itemRow + i);

	//System.out.println("getCellRect(row="+row+",column="+column+",includeSpacing="+includeSpacing+", r1="+r1);
	return r1;
    }

    /**
     * Map screen coordinates to column information
     *
     * @param p
     * @return
     */
    /*
    public int columnAtPoint(Point p) {
	int x = super.columnAtPoint(p);

	// -1 is returned by columnAtPoint if the point is not in the table
	if (x < 0)
	    return x;
	int y = super.rowAtPoint(p);
	return itemRenderer.getVisibleCell(getItemRow(y), x).tableColumn;
    }
    */

    /**
     * Map screen coordinates to column information
     *
     * @param p
     * @return
     */
    /*
    public int rowAtPoint(Point p) {
	int row = super.rowAtPoint(p);
	// -1 is returned by columnAtPoint if the point is not in the table
	if (row < 0)
	    return row;

	int col = super.columnAtPoint(p);
	return itemRenderer.getVisibleCell(getItemRow(row), col).itemRow + getItemIndex(row) * itemRenderer.getNbrItemRows();
    }
    */

    /**
     * Ensure graphic consistency on selection changes
     *
     * @param e
     */
    public void columnSelectionChanged(ListSelectionEvent e) {
	repaint();
    }

    /**
     * Ensure graphic consistency on selection changed
     *
     * @param e
     */
    public void valueChanged(ListSelectionEvent e) {
	int firstIndex = e.getFirstIndex();
	int lastIndex = e.getLastIndex();
	if (firstIndex == -1 && lastIndex == -1) { // Selection cleared.
	    repaint();
	}
	Rectangle dirtyRegion = getCellRect(firstIndex, 0, false);
	int numCoumns = getColumnCount();
	int index = firstIndex;
	for (int i = 0; i < numCoumns; i++) {
	    dirtyRegion.add(getCellRect(index, i, false));
	}
	index = lastIndex;
	for (int i = 0; i < numCoumns; i++) {
	    dirtyRegion.add(getCellRect(index, i, false));
	}
	repaint(dirtyRegion.x, dirtyRegion.y, dirtyRegion.width, dirtyRegion.height);
    }


    class MyUI extends BasicTableUI {

	public class TableCellIndex {
	    public int row, col;
	    public TableCellIndex(int row, int col) { this.row = row; this.col = col; }
	    public boolean equals(Object obj) {
		if (!(obj instanceof TableCellIndex))
		    return false;
		return (row == ((TableCellIndex)obj).row && col == ((TableCellIndex)obj).col);
	    }
	}

	public void paint(Graphics g, JComponent c) {
	    Rectangle r = g.getClipBounds();
	    int firstRow = table.rowAtPoint(new Point(0, r.y));
	    int lastRow = table.rowAtPoint(new Point(0, r.y + r.height));

	    // -1 is a flag that the ending point is outside the table
	    if (lastRow < 0)
		lastRow = table.getRowCount() - 1;
	    HashSet<TableCellIndex> toPaint = new HashSet<TableCellIndex>();

	    for (int row = firstRow; row <= lastRow; row++) {
		for (int col = 0; col < table.getColumnCount(); col++) {
		    CellIndex itemCellIndex = itemRenderer.getVisibleCell(getItemRow(row), col);
		    int tableRow = itemCellIndex.itemRow + getItemIndex(row) * itemRenderer.getNbrItemRows();
		    toPaint.add(new TableCellIndex(tableRow, itemCellIndex.tableColumn));
		}
	    }
	    
	    for (TableCellIndex tableCellIndex : toPaint) {
		Rectangle r1 = table.getCellRect(tableCellIndex.row, tableCellIndex.col, true);
		if (r1.intersects(r))
		    paintCell(tableCellIndex.row, tableCellIndex.col, g, r1);
	    }
	}

	private void paintCell(int row, int column, Graphics g, Rectangle area) {
	    //System.out.println("paintCell(row="+row+",column="+column);
	    int verticalMargin = table.getRowMargin();
	    int horizontalMargin = table.getColumnModel().getColumnMargin();

	    Color c = g.getColor();
	    g.setColor(table.getGridColor());
	    //g.drawRect(area.x, area.y, area.width - 1, area.height - 1);
	    g.drawRect(area.x-1, area.y-1, area.width, area.height);
	    g.setColor(c);

	    area.setBounds(area.x + horizontalMargin / 2,
			   area.y + verticalMargin / 2,
			   area.width - horizontalMargin,
			   area.height - verticalMargin);

	    if (table.isEditing() && table.getEditingRow() == row &&
		table.getEditingColumn() == column) {
		Component component = table.getEditorComponent();
		component.setBounds(area);
		component.validate();
	    } else {
		TableCellRenderer renderer = table.getCellRenderer(row, column);
		Component component = table.prepareRenderer(renderer, row, column);
		if (component != null) {
		    //if (component.getParent() == null)
		    //    rendererPane.add(component);
		    rendererPane.paintComponent(g, component, table, area.x, area.y,
					    area.width, area.height, true);
		    //rendererPane.remove(component); //TODO minor: If I don't call this, swing does not free this object. Java bug???
		}
		component = null; 
	    }
	}
    }




    class ItemTableHeader extends JTableHeader {
	int lastX=-1, lastY=-1;

	public ItemTableHeader() {
	    super(ItemTable.this.getColumnModel());
	    setUI(new ItemTableHeaderUI());
	    setReorderingAllowed(false);

	    setDefaultRenderer(new MyRenderer(getDefaultRenderer()));
	    addMouseListener(new MyMouseListener());
	    addMouseMotionListener(new MyMouseMotionListener());
	}

	public void setReorderingAllowed(boolean b) {
	    super.setReorderingAllowed(false);
	}


	CellIndex getVisibleHeaderCellIndexAt(int x, int y) {
	    TableColumnModel columnModel = getColumnModel();
	    int viewColumn = columnModel.getColumnIndexAtX(x);
	    int column = columnModel.getColumn(viewColumn).getModelIndex();
	    if (column == -1)
		return null;
	    int itemRow = ((ItemTableHeaderUI)getUI()).getItemRowAtY(y);
	    return itemRenderer.getVisibleCell(itemRow, column);
	}
		
	class MyMouseListener extends MouseAdapter {
	    public void mouseClicked(MouseEvent e) {
		CellIndex cellIndex = getVisibleHeaderCellIndexAt(e.getX(), e.getY());
		if (cellIndex == null)
		    return;
		int attributeIndex = itemRenderer.getItemAttributeIndex(cellIndex.itemRow, cellIndex.tableColumn);
		
		if (sortCellIndex != null && sortCellIndex.itemRow == cellIndex.itemRow && sortCellIndex.tableColumn == cellIndex.tableColumn) {
		    if (sortDirection == ItemTableModel.SortDirection.ASCENDING) {
			sortDirection = ItemTableModel.SortDirection.DESCENDING;
		    } else {
			sortCellIndex = null;
			attributeIndex = -1;
		    }
		} else if (attributeIndex != -1 && itemModel.isSortableByAttribute(attributeIndex)) {
		    sortCellIndex = cellIndex;
		    sortDirection = ItemTableModel.SortDirection.ASCENDING;
		} else {
		    sortCellIndex = null;
		}
		itemModel.sortBy(attributeIndex, sortDirection);
		ItemTableHeader.this.repaint();
	    }
	}

	class MyMouseMotionListener implements MouseMotionListener {
	    public void mouseDragged(MouseEvent e) {}
	    public void mouseMoved(MouseEvent e) { lastX = e.getX(); lastY = e.getY(); }
	}

	class MyRenderer extends DefaultTableCellRenderer {
	    private TableCellRenderer tableCellRenderer;
	    public MyRenderer(TableCellRenderer tableCellRenderer) {
		this.tableCellRenderer = tableCellRenderer;
		setToolTipText("...");
	    }

	    public Component getItemTableHeaderRendererComponent(JTable table, Object value, int itemRow, int column) {
		Component c = tableCellRenderer.getTableCellRendererComponent(table, value, false, false, -1, column);

		if (c instanceof JLabel) {
		    JLabel l = (JLabel) c;
		    l.setHorizontalAlignment(JLabel.LEFT);
		    l.setHorizontalTextPosition(JLabel.LEFT);

		    Icon icon = (sortCellIndex != null && sortCellIndex.itemRow == itemRow && sortCellIndex.tableColumn == column)
			? new ArrowIcon((sortDirection == ItemTableModel.SortDirection.DESCENDING), l.getFont().getSize(), 0)
			: null;
		    l.setIcon(icon);
		    ((JLabel)c).setToolTipText((value==null) ? null : value.toString());
		}
		return c;
	    }

	    //public JToolTip createToolTip() { return MainGui.createToolTip(); }  //TODO minor: how to do this externally??...
	    public String getToolTipText(MouseEvent event) {
		CellIndex cellIndex = getVisibleHeaderCellIndexAt(lastX, lastY);  //event.getX() gives invalid results.
		if (cellIndex == null)
		    return null;
		return itemRenderer.getColumnName(cellIndex.itemRow, cellIndex.tableColumn);
	    }
	}
    }


    class ItemTableHeaderUI extends BasicTableHeaderUI {
	public void paint(Graphics g, JComponent c) {
	    int nbrRows = itemRenderer.getNbrItemRows();
	    int nbrCols = itemRenderer.getNbrColumns();  //header.getColumnModel().getColumnCount();
	    for (int row = 0; row < nbrRows; row++) {
		int startX = 0;
		for (int col = 0; col < nbrCols; ) {
		    if (!isCellVisible(row, col)) {
			startX += header.getColumnModel().getColumn(col).getWidth();
			col++;
			continue;
		    }
		    CellSpan cellSpan = itemRenderer.getCellSpan(row, col); 
		    int colWidth = 0;
		    for (int i = 0; i < cellSpan.numColumns; i++)
			colWidth += header.getColumnModel().getColumn(col+i).getWidth();
		    
		    Rectangle cellRect = new Rectangle(startX, row * defaultHeaderComponentHeight, 
						       colWidth, defaultHeaderComponentHeight * cellSpan.numRows);

		    ItemTableHeader.MyRenderer renderer = (ItemTableHeader.MyRenderer) header.getDefaultRenderer();
		    Component component = renderer.getItemTableHeaderRendererComponent(header.getTable(), itemRenderer.getColumnName(row, col), row, col);

		    rendererPane.add(component);
		    rendererPane.paintComponent(g, component, header, cellRect.x, cellRect.y, cellRect.width, cellRect.height, true);

		    startX += colWidth;
		    col += cellSpan.numColumns;
		}	    
	    }
	    rendererPane.removeAll();
	}

	public int getItemRowAtY(int y) {
	    return y/defaultHeaderComponentHeight;
	}

	public Dimension getPreferredSize(JComponent c) {
	    return new Dimension(super.getPreferredSize(c).width, defaultHeaderComponentHeight*itemRenderer.getNbrItemRows());
	}
    }

    static int getDefaultHeaderComponentHeight() {
	return new JTableHeader().getDefaultRenderer().getTableCellRendererComponent(null, "test", false, false, -1, 0).
	    getPreferredSize().height;
    }
}

