swingx-master/swingx-core/src/test/java/org/jdesktop/swingx/renderer/HighlighterClientVisualCheck.java
/*
 * $Id$
 *
 * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle,
 * Santa Clara, California 95054, U.S.A. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */
package org.jdesktop.swingx.renderer;

import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.geom.Point2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import java.util.regex.Pattern;

import javax.swing.AbstractAction;
import javax.swing.AbstractListModel;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.ListModel;
import javax.swing.ToolTipManager;
import javax.swing.UIManager;
import javax.swing.border.MatteBorder;
import javax.swing.table.TableModel;

import org.jdesktop.swingx.InteractiveTestCase;
import org.jdesktop.swingx.JXFrame;
import org.jdesktop.swingx.JXList;
import org.jdesktop.swingx.JXSearchPanel;
import org.jdesktop.swingx.JXTable;
import org.jdesktop.swingx.JXTree;
import org.jdesktop.swingx.JXTreeTable;
import org.jdesktop.swingx.decorator.AbstractHighlighter;
import org.jdesktop.swingx.decorator.BorderHighlighter;
import org.jdesktop.swingx.decorator.ColorHighlighter;
import org.jdesktop.swingx.decorator.ComponentAdapter;
import org.jdesktop.swingx.decorator.CompoundHighlighter;
import org.jdesktop.swingx.decorator.HighlightPredicate;
import org.jdesktop.swingx.decorator.Highlighter;
import org.jdesktop.swingx.decorator.HighlighterFactory;
import org.jdesktop.swingx.decorator.PatternPredicate;
import org.jdesktop.swingx.decorator.ShadingColorHighlighter;
import org.jdesktop.swingx.decorator.HighlightPredicate.ColumnHighlightPredicate;
import org.jdesktop.swingx.painter.MattePainter;
import org.jdesktop.swingx.painter.AbstractLayoutPainter.HorizontalAlignment;
import org.jdesktop.swingx.renderer.RelativePainterHighlighter.NumberRelativizer;
import org.jdesktop.swingx.rollover.RolloverProducer;
import org.jdesktop.swingx.search.PatternMatcher;
import org.jdesktop.swingx.treetable.FileSystemModel;
import org.jdesktop.swingx.util.PaintUtils;
import org.jdesktop.test.AncientSwingTeam;

/**
 * visual checks of highlighter clients. Mostly by example of JXTable.
 * 
 * @author Jeanette Winzenburg
 */
public class HighlighterClientVisualCheck extends InteractiveTestCase {
    
    @SuppressWarnings("unused")
    private static final Logger LOG = Logger
            .getLogger(HighlighterClientVisualCheck.class.getName());
    
    protected TableModel tableModel;
    protected Color background = Color.RED;
    protected Color foreground = Color.BLUE;
    
    
    public static void main(String args[]) {
//        UIManager.put("Nimbus.keepAlternateRowColor", Boolean.TRUE);
//      setSystemLF(true);
      HighlighterClientVisualCheck test = new HighlighterClientVisualCheck();
      try {
//          setLookAndFeel("Nimbus");
         test.runInteractiveTests();
//          test.runInteractive("JP");
//          test.runInteractiveTests(".*Striping.*");
//         test.runInteractiveTests(".*ToolTip.*");
//         test.runInteractiveTests("interactive.*Search.*");
//         test.runInteractiveTests("interactive.*BorderHighlighter.*");
      } catch (Exception e) {
          System.err.println("exception when executing interactive tests:");
          e.printStackTrace();
      }
  }

    /**
     * Issue #1295-swingx: JXTable rolloverController repaint incomplete.
     * Column not repainted.
     */
    public void interactiveBorderHighlighterOnRollover() {
        JXTable table = new JXTable(tableModel) {
            int count;
            @Override
            protected void paintComponent(Graphics g) {
                // crude check if the logic of column/row painting
                // in TableRolloverController is working (looks so)
//                LOG.info("paint clip: " + count++ + g.getClip());
                super.paintComponent(g);
            }
            
        };
        final AbstractHighlighter hlRow = new BorderHighlighter(HighlightPredicate.ROLLOVER_ROW, 
//                BorderFactory.createCompoundBorder(
                new MatteBorder(1, 0, 1, 0, Color.lightGray)
//                , BorderFactory.createEmptyBorder(0, 1, 0, 1))
                , true, false );  
        final AbstractHighlighter hlColumn = new ColorHighlighter(HighlightPredicate.ROLLOVER_COLUMN, 
                Color.LIGHT_GRAY, null);
        table.setHighlighters(hlRow, hlColumn);
        JXFrame frame = wrapWithScrollingInFrame(table, "borderHighlgihter on rollover");
        Action toggleCell = new AbstractAction("toggle cell-rollover") {
            
            @Override
            public void actionPerformed(ActionEvent e) {
                if (hlRow.getHighlightPredicate() == HighlightPredicate.ROLLOVER_ROW) {
                    hlRow.setHighlightPredicate(HighlightPredicate.NEVER);
                    hlColumn.setHighlightPredicate(HighlightPredicate.ROLLOVER_CELL);
                } else {
                    hlRow.setHighlightPredicate(HighlightPredicate.ROLLOVER_ROW);
                    hlColumn.setHighlightPredicate(HighlightPredicate.ROLLOVER_COLUMN);
                }
                
            }
        };
        addAction(frame, toggleCell);
        show(frame);
    }
    /**
     * Show variants of border Highlighters.
     *
     */
    public void interactiveTableBorderHighlighter() {
        JXTable table = new JXTable(tableModel);
        table.setVisibleRowCount(table.getRowCount());
        table.setVisibleColumnCount(7);
        table.packAll();
        table.setColumnControlVisible(true);
        
        BorderHighlighter outer = new BorderHighlighter(new HighlightPredicate.ColumnHighlightPredicate(1),
                BorderFactory.createLineBorder(Color.RED, 3));
        BorderHighlighter inner = new BorderHighlighter(new HighlightPredicate.ColumnHighlightPredicate(2),
                BorderFactory.createLineBorder(Color.RED, 3), true, true);
        BorderHighlighter replace = new BorderHighlighter(new HighlightPredicate.ColumnHighlightPredicate(0),
                BorderFactory.createLineBorder(Color.RED, 3), false, true);
        table.setHighlighters(outer, inner, replace);
        showWithScrollingInFrame(table, "Border Highlighters");
    }
    
    /**
     * Show variants of border Highlighters.
     *
     */
    public void interactiveTreeBorderHighlighter() {
        JXTree tree = new JXTree();
        tree.expandAll();
        tree.setVisibleRowCount(tree.getRowCount());
        // need SwingX highlighter
        tree.setCellRenderer(new DefaultTreeRenderer());
        tree.setHighlighters(new BorderHighlighter(BorderFactory.createLineBorder(Color.GREEN, 1)));
        showWithScrollingInFrame(tree, "Border Highlighters");
    }

    /**
     * Multiple Highlighters (shown as example in Javapolis 2007).
     * 
     */
    public void interactiveTablePatternHighlighterJP() {
        JXTable table = new JXTable(tableModel);
        table.setVisibleRowCount(table.getRowCount());
        table.setVisibleColumnCount(7);
        table.packAll();
        table.setColumnControlVisible(true);

        Font font = table.getFont().deriveFont(Font.BOLD | Font.ITALIC);
        Highlighter simpleStriping = HighlighterFactory.createSimpleStriping();
        PatternPredicate patternPredicate = new PatternPredicate("^M", 1);
        ColorHighlighter magenta = new ColorHighlighter(patternPredicate, null,
                Color.MAGENTA, null, Color.MAGENTA);
        FontHighlighter derivedFont = new FontHighlighter(patternPredicate,
                font);
        AbstractHighlighter gradient = createRelativeGradientHighlighter(HorizontalAlignment.LEFT, AncientSwingTeam.INTEGER_COLUMN);
        gradient.setHighlightPredicate(new HighlightPredicate.ColumnHighlightPredicate(AncientSwingTeam.INTEGER_COLUMN));
        
        LOG.info("" + (table.getValueAt(0, 3) instanceof Number));
        Highlighter shading = new ShadingColorHighlighter(
                new HighlightPredicate.ColumnHighlightPredicate(1));

        table.setHighlighters(simpleStriping, magenta, derivedFont, shading //);
                , gradient);
        showWithScrollingInFrame(table, "Multiple Highlighters");
    }

    /**
     * Simulates table by one-list per column.
     * 
     * NOTE: the only purpose is to demonstrate the similarity
     * of highlighter usage across collection components!
     * (shown as example in Javapolis 2007)
     * 
     * @see #interactiveTablePatternHighlighterJP()
     */
    public void interactiveListPatternHighlighterJP() {
        JXTable source = new JXTable(tableModel);
        source.toggleSortOrder(3);
        Font font = source.getFont().deriveFont(Font.BOLD | Font.ITALIC);
        Highlighter simpleStriping = HighlighterFactory.createSimpleStriping();
        String pattern = "^M";
        PatternPredicate patternPredicate = new PatternPredicate(pattern, 0);
        ColorHighlighter magenta = new ColorHighlighter(patternPredicate, null,
                Color.MAGENTA, null, Color.MAGENTA);
        FontHighlighter derivedFont = new FontHighlighter(patternPredicate,
                font);
        Highlighter gradient = createRelativeGradientHighlighter(
                HorizontalAlignment.LEFT, 0);
        Highlighter shading = new ShadingColorHighlighter(
                new HighlightPredicate.ColumnHighlightPredicate(0));
        // create and configure one JXList per column.
        List<JXList> lists = new ArrayList<JXList>();
        // first name
        JXList list0 = new JXList(createListModel(source, 0));
        list0.setHighlighters(simpleStriping);
        lists.add(list0);
        // last name
        JXList list1 = new JXList(createListModel(source, 1));
        list1.setHighlighters(simpleStriping, magenta, derivedFont, shading);
        lists.add(list1);

        // color
        JXList listc = new JXList(createListModel(source, 2));
        listc.setHighlighters(simpleStriping);
        lists.add(listc);

        // number
        JXList listn = new JXList(createListModel(source, AncientSwingTeam.INTEGER_COLUMN));
        listn.setCellRenderer(new DefaultListRenderer(
                StringValues.NUMBER_TO_STRING, JLabel.RIGHT));
        listn.setHighlighters(simpleStriping, gradient);
        lists.add(listn);

        // boolean
        JXList listb = new JXList(createListModel(source, 4));
        listb.setCellRenderer(new DefaultListRenderer(new CheckBoxProvider()));
        listb.setFixedCellHeight(list0.getPreferredSize().height
                / source.getRowCount());
        listb.setHighlighters(simpleStriping, magenta, derivedFont, gradient);
        lists.add(listb);

        JComponent panel = Box.createHorizontalBox();
        for (JXList l : lists) {
            listb.setVisibleRowCount(source.getRowCount());
            l.setFont(source.getFont());
            panel.add(new JScrollPane(l));
        }
        showInFrame(panel, "Multiple Highlighters");
    }

    /**
     * @param right
     * @return
     */
    private RelativePainterHighlighter createRelativeGradientHighlighter(
            HorizontalAlignment right, int column) {
        Color startColor = PaintUtils.setAlpha(Color.RED, 130);
        Color endColor = PaintUtils.setAlpha(Color.RED.brighter(), 0);
        boolean isRightAligned = HorizontalAlignment.RIGHT == right;
        GradientPaint paint = new GradientPaint(new Point2D.Double(0, 0),
                isRightAligned ? endColor : startColor, 
                new Point2D.Double(100, 0), 
                isRightAligned ? startColor : endColor);
        MattePainter painter = new MattePainter(paint);
        painter.setPaintStretched(true);
        RelativePainterHighlighter p = new RelativePainterHighlighter(painter);
        p.setHorizontalAlignment(right);
        p.setRelativizer(new NumberRelativizer(column, 100, 100));
        return p;
    }
    
    /**
     * @param tableModel2
     * @param i
     * @return
     */
    private ListModel createListModel(final JXTable tableModel, final int i) {
        ListModel listModel = new AbstractListModel(){

            @Override
            public Object getElementAt(int index) {
                return tableModel.getValueAt(index, i);
            }

            @Override
            public int getSize() {
                return tableModel.getRowCount();
            }};
        return listModel ;
    }


    public static class FontHighlighter extends AbstractHighlighter {
        
        private Font font;
        public FontHighlighter(HighlightPredicate predicate, Font font) {
            super(predicate);
            this.font = font;
        }
        @Override
        protected Component doHighlight(Component component,
                ComponentAdapter adapter) {
            component.setFont(font);
            return component;
        }
        
    }
    /**
     * Example from forum requirement: highlight all rows of a given group
     * if mouse if over one of them.
     * 
     * PENDING: need to track row view-model coordinates mapping after
     * filtering/sorting.
     *
     */
    public void interactiveRolloverRowGroup() {
        TableModel model = new AncientSwingTeam();
       final List<Integer> rowSet = new ArrayList<Integer>();
       for (int i = 0; i < model.getRowCount(); i++) {
         if ((Integer)model.getValueAt(i, 3) > 10) {
             rowSet.add(i);
         }
       }
       final HighlightPredicate predicate = new HighlightPredicate() {

        @Override
        public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
            return rowSet.contains(adapter.row);
        }
           
       };
       final ColorHighlighter highlighter = new ColorHighlighter(HighlightPredicate.NEVER, Color.YELLOW, 
               null);
       JXTable table = new JXTable(model);
       table.addHighlighter(highlighter);
       PropertyChangeListener l = new PropertyChangeListener() {

        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            Point location = (Point) evt.getNewValue();
            int row = -1;
            if (location != null) {
                row = location.y;
            }
            if (rowSet.contains(row)) {
                highlighter.setHighlightPredicate(predicate);
            } else {
                highlighter.setHighlightPredicate(HighlightPredicate.NEVER);
            }
        }
           
       };
       table.addPropertyChangeListener(RolloverProducer.ROLLOVER_KEY, l);
       showWithScrollingInFrame(table, "rollover highlight of row groups");
    }
    
    /**
     * Example to highlight against a value/color map.
     * Here the control is in predicate. <p>
     * 
     */
    public void interactiveColorValueMappedHighlighterPredicate() {
        JXTable table = new JXTable(new AncientSwingTeam());
        // build a quick color lookup to simulate multi-value value-based
        // coloring
        final int numberColumn = 3;
        table.toggleSortOrder(numberColumn);
        Color[] colors = new Color[] { Color.YELLOW, Color.CYAN, Color.MAGENTA,
                Color.GREEN };
        int rowsPerColor = (table.getRowCount() - 5) / colors.length;
        Map<Color, HighlightPredicate> map = new HashMap<Color, HighlightPredicate>();
        for (int i = 0; i < colors.length; i++) {
            List<Integer> values = new ArrayList<Integer>();
            for (int j = 0; j < rowsPerColor; j++) {
                values.add((Integer) table.getValueAt(i * rowsPerColor + j, numberColumn));
            }
            map.put(colors[i], new ValueMappedHighlightPredicate(values, numberColumn));
        }
        // create one ColorHighlighter for each color/predicate pair and 
        // add to a compoundHighlighter
        CompoundHighlighter chl = new CompoundHighlighter();
        for (Color color : colors) {
            chl.addHighlighter(new ColorHighlighter(map.get(color), color, null));
        }
        table.resetSortOrder();
        table.addHighlighter(chl);
        showWithScrollingInFrame(table,
                "compound highlighter with value-based color mapping predicate");
    }
    
    /**
     * Custom predicate which returns true if the filtered cell value
     * of a given testColumn is contained in a list of values.
     * PENDING: logic similar to search/pattern, enough to abstract?
     */
    public static class ValueMappedHighlightPredicate implements HighlightPredicate {

        private List<?> values;
        private int testColumn;
        public ValueMappedHighlightPredicate(List<?> values, int testColumn) {
            this.values = values;
            this.testColumn = testColumn;
        }
        @Override
        public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
            return values.contains(adapter.getValue(testColumn));
        }
        
    }
    /**
     * Example to highlight against a value/color map. <p>
     * Here the Highlighter takes full control. Which is a bit 
     * on the border line of 
     * the intended distribution of responsibility between
     * Highlighter and HighlighterPredicate.
     */
    public void interactiveColorValueMappedHighlighter() {
        JXTable table = new JXTable(new AncientSwingTeam());
        // build a quick color lookup to simulate multi-value value-based
        // coloring
        final int numberColumn = 3;
        table.toggleSortOrder(numberColumn);
        final Map<Integer, Color> lookup = new HashMap<Integer, Color>();
        Color[] colors = new Color[] { Color.YELLOW, Color.CYAN, Color.MAGENTA,
                Color.GREEN };
        int rowsPerColor = (table.getRowCount() - 5) / colors.length;
        for (int i = 0; i < colors.length; i++) {
            for (int j = 0; j < rowsPerColor; j++) {
                lookup.put((Integer) table.getValueAt(i * rowsPerColor + j,
                        numberColumn), colors[i]);
            }
        }
        table.resetSortOrder();
        // PENDING JW: add and use HighlightPredicate.SELECTED/UN_SELECTED 
        Highlighter hl = new ColorHighlighter() {

            @Override
            protected void applyBackground(Component renderer, ComponentAdapter adapter) {
                if (adapter.isSelected()) return;
                Color background = lookup.get(adapter.getValue(numberColumn));
                if (background != null) {
                    renderer.setBackground(background);
                }
            }
        };
        table.addHighlighter(hl);
        showWithScrollingInFrame(table,
                "conditional highlighter with value-based color mapping");
    }

    /**
     * test to see searchPanel functionality in new Highlighter api
     * 
     */
    public void interactiveSearchPanel() {
        JXTable table = new JXTable(tableModel);
        final ColorHighlighter cl = new ColorHighlighter(new PatternPredicate((Pattern) null, 0), null,
                Color.RED);
        table.addHighlighter(cl);
        JXSearchPanel searchPanel = new JXSearchPanel();
        PatternMatcher patternMatcher = new PatternMatcher() {
            @Override
            public Pattern getPattern() {
                return getPatternPredicate().getPattern();
            }

            @Override
            public void setPattern(Pattern pattern) {
                PatternPredicate old = getPatternPredicate();
                cl.setHighlightPredicate(new PatternPredicate(pattern, old
                        .getTestColumn(), old.getHighlightColumn()));
            }
            
            private PatternPredicate getPatternPredicate() {
                return (PatternPredicate) cl.getHighlightPredicate();
            }

        };
        searchPanel.addPatternMatcher(patternMatcher);
        JXFrame frame = wrapWithScrollingInFrame(table,
                "Pattern highlighting col 0");
        addStatusComponent(frame, searchPanel);
        show(frame);
    }
    
//----------------- custom PatternMatcher
    
    /**
     * columm shading (was: hierarchicalColumnHighlighter)
     *
     */
    public void interactiveColumnShading() {
        JXTreeTable treeTable = new JXTreeTable(new FileSystemModel());
        // simulate hierarchicalColumnHighlighter
        int hierarchicalColumn = 0;
        for (int i = 0; i < treeTable.getColumnCount(); i++) {
            if (treeTable.isHierarchical(i)) {
                hierarchicalColumn = i;
                break;
            }
        }
        treeTable.addHighlighter(new ShadingColorHighlighter(new ColumnHighlightPredicate(hierarchicalColumn)));
        showWithScrollingInFrame(treeTable, "hierarchical column");
        
    }
    
    /**
     * Classic lineprinter striping and hyperlink (LF only, no action).
     * 
     */
    public void interactiveTableAlternateAndHyperlink() {
        JXTable table = new JXTable(tableModel);
        table.setRowHeight(22);
        table.getColumn(1).setCellRenderer(
                new DefaultTableRenderer(new HyperlinkProvider()));
        table.addHighlighter(HighlighterFactory
                .createSimpleStriping(HighlighterFactory.CLASSIC_LINE_PRINTER));
        JFrame frame = wrapWithScrollingInFrame(table,
                "classic lineprinter and hyperlink on column 1");
        frame.setVisible(true);
    }


    /**
     * LinePrinter striping and rollover.
     *
     */
    public void interactiveTableAlternateAndRollover() {
        JXTable table = new JXTable(tableModel);
        table.setRowHeight(22);
        table.setHighlighters(
            HighlighterFactory.createSimpleStriping(HighlighterFactory.LINE_PRINTER),
            new ColorHighlighter(HighlightPredicate.ROLLOVER_ROW, Color.YELLOW, null));
        showWithScrollingInFrame(table, "LinePrinter plus yellow rollover");
    }

    /**
     * Foreground highlight on column 1 and 3.
     *
     */
    public void interactiveColumnForeground() {
        JXTable table = new JXTable(tableModel);
        HighlightPredicate predicate = new ColumnHighlightPredicate(1, 3);
        table.addHighlighter(new ColorHighlighter(predicate, null, Color.BLUE));
        showWithScrollingInFrame(table, "Foreground highlight col 1 and 3");
    }


    /**
     * ColorHighlighter with pattern predicate
     *
     */
    public void interactiveTablePatternHighlighter() {
        JXTable table = new JXTable(tableModel);
        table.setColumnControlVisible(true);
        table.addHighlighter(new ColorHighlighter(new PatternPredicate("^M", 1), null, 
                Color.red));
        showWithScrollingInFrame(table, "Pattern: highlight row if ^M col 1");
    }

    /**
     * Issue #258-swingx: Background LegacyHighlighter must not change custom
     * foreground.
     * <p>
     * 
     * Use SwingX extended default renderer.
     */
    public void interactiveTableCustomRendererColor() {
        TableModel model = new AncientSwingTeam();
        JXTable table = new JXTable(model);
        DefaultTableRenderer renderer = new DefaultTableRenderer();
        renderer.setForeground(foreground);
        renderer.setBackground(background);
        table.addHighlighter(HighlighterFactory.createAlternateStriping(Color.WHITE, HighlighterFactory.GENERIC_GRAY));
        table.setDefaultRenderer(Object.class, renderer);
        JXTable nohighlight = new JXTable(model);
        nohighlight.setDefaultRenderer(Object.class, renderer);
        showWithScrollingInFrame(table, nohighlight,
                "ext: custom colored renderer with bg highlighter <--> shared without highl");
    }
    
    /**
     * Requirement from forum: value based color and tooltip.
     * 
     * Here the tooltip is regarded as visual decoration and 
     * set in a specialized Highlighter. 
     *
     */
    public void interactiveValueBasedToolTipAndColorOnHighlighter() {
        ToolTipManager t = ToolTipManager.sharedInstance();
        LOG.info("initial/reshow/dismiss" + t.getInitialDelay() + " / " + t.getReshowDelay()
                + " / " + t.getDismissDelay()
                + " / " + UIManager.getString("ToolTipManager.enableToolTipMode"));
        final JXTable table = new JXTable(new AncientSwingTeam()) {

//            @Override
//            public Point getToolTipLocation(MouseEvent event) {
//                int column = columnAtPoint(event.getPoint());
//                int row = rowAtPoint(event.getPoint());
//                Rectangle cellRect = getCellRect(row, column, false);
//                if (!getComponentOrientation().isLeftToRight()) {
//                    cellRect.translate(cellRect.width, 0);
//                }
//                // PENDING JW: otherwise we get a small (borders only) tooltip for null
//                // core issue? Yeh, the logic in tooltipManager is crooked.
//                // but this here is ehem ... rather arbitrary. 
////                return getValueAt(row, column) == null ? null : cellRect.getLocation();
//                return getToolTipText(event) == null ? null : cellRect.getLocation();
////                return null;
//            }
//            

        };
        t.unregisterComponent(table);
        table.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke('q'), "dummy");
        t.registerComponent(table);
        table.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke("F5"), "hideTip");
        LOG.info("hideTip? " + table.getActionMap().get("hideTip"));
        
        PropertyChangeListener l = new PropertyChangeListener() {
            
            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                LOG.info("evt.getP" + evt.getPropertyName());
                ToolTipManager.sharedInstance().mousePressed(null);
            }
        };
        table.addPropertyChangeListener(RolloverProducer.ROLLOVER_KEY, l);
//        table.addMouseMotionListener(new MouseMotionAdapter() {
//            int prevRow = -1;
//            int prevCol = -1;
//          
//            public void mouseMoved(MouseEvent e) {
//               int row = table.rowAtPoint(e.getPoint());
//               int col = table.columnAtPoint(e.getPoint());
//               if (row == prevRow && col == prevCol)
//                  return;
//               ToolTipManager.sharedInstance().mousePressed(e);
////               ToolTipManager.sharedInstance().mouseEntered(e);
//               prevRow = row;
//               prevCol = col;
//            }
//         });
        HighlightPredicate predicate = new HighlightPredicate() {

            @Override
            public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
                if (!(adapter.getValue() instanceof Number)) return false;
                return ((Number) adapter.getValue()).intValue() < 10;
            }
            
        };
        ColorHighlighter hl = new ColorHighlighter(
                predicate, null, Color.RED, null, Color.RED);
        // THINK this is possible, but maybe not the correct place 
        // ... more on the what-side of "what vs. how" ?
        Highlighter tl = new AbstractHighlighter(predicate) {

            @Override
            protected Component doHighlight(Component component, ComponentAdapter adapter) {
                String text = "low on luck: " + ((JLabel) component).getText();
                ((JComponent) component).setToolTipText(text);
                return component;
            }
            @Override
            protected boolean canHighlight(Component component,
                    ComponentAdapter adapter) {
                return component instanceof JLabel;
            }
            
        };
        table.setHighlighters(HighlighterFactory.createSimpleStriping(HighlighterFactory.GENERIC_GRAY),
                hl, tl);
        showWithScrollingInFrame(table, "Value-based Tooltip ... on Highlighter");
    }

    /**
     * Requirement from forum: value based color and tooltip.<p>
     * 
     * Here the tooltip is regarded as belonging more to the "what"
     * of rendering and set in a custom provider. The implication
     * is that the logic (whether to show the tooltip or not) is
     * duplicated (in the predicate and the provider.
     * 
     *
     */
    public void interactiveValueBasedToolTipAndColorOnProvider() {
        JXTable table = new JXTable(new AncientSwingTeam());
        HighlightPredicate predicate = new HighlightPredicate() {

            @Override
            public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
                if (!(adapter.getValue() instanceof Number)) return false;
                return ((Number) adapter.getValue()).intValue() < 10;
            }
            
        };
        ColorHighlighter hl = new ColorHighlighter(
                predicate, null, Color.RED, null, Color.RED);
        table.setHighlighters(HighlighterFactory.createSimpleStriping(HighlighterFactory.GENERIC_GRAY),
                hl); //, tl);
        // here: set's value based .. this duplicates logic of 
        // predicate
        LabelProvider provider = new LabelProvider() {

            
            @Override
            protected void configureState(CellContext context) {
                super.configureState(context);
                rendererComponent.setToolTipText(getToolTipText(context));
            }


            private String getToolTipText(CellContext context) {
                if ((context.getValue() instanceof Number))  {
                    int luck = ((Number) context.getValue()).intValue();
                    if (luck < 10) {
                        return "low on luck: " + luck;
                    }
                }
                return null;
            }
            
        };
        provider.setHorizontalAlignment(JLabel.RIGHT);
        table.setDefaultRenderer(Number.class, new DefaultTableRenderer(provider));
        showWithScrollingInFrame(table, "Value-based Tooltip ... on provider");
    }

    /**
     * Example of custom predicate based on the component's value, 
     * (as opposed to on the value of the adapter). 
     * 
     * 
     */
    public void interactiveTableColorBasedOnComponentValue() {
        TableModel model = new AncientSwingTeam();
        JXTable table = new JXTable(model);
        table.setForeground(Color.GREEN);
        HighlightPredicate predicate = new HighlightPredicate() {

            @Override
            public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
                if (!(renderer instanceof JLabel)) return false;
                String text = ((JLabel) renderer).getText();
                 return text.contains("y");
            }
            
        };
        ColorHighlighter hl = new ColorHighlighter(predicate, null, Color.RED);
        table.addHighlighter(HighlighterFactory.createSimpleStriping(HighlighterFactory.GENERIC_GRAY));
        table.addHighlighter(hl);
        showWithScrollingInFrame(table, 
                "component value-based rendering (label text contains y) ");
    }


    //------------------ rollover
    
    /**
     * Issue #503-swingx: custom cursor respected when rollover?
     * Seems okay for table, 
     */
    public void interactiveRolloverHighlightCustomCursor() {
        JXTable table = new JXTable(tableModel);
        table.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
        table.addHighlighter(new ColorHighlighter(HighlightPredicate.ROLLOVER_ROW, Color.YELLOW, null));
        showWithScrollingInFrame(table, "rollover highlight, custom cursor");
    }

    /**
     * Plain RolloverHighlighter. 
     * Issue #513-swingx: no rollover effect for disabled table.
     *
     */
    public void interactiveRolloverHighlight() {
        final JXTable table = new JXTable(tableModel);
        ColorHighlighter colorHighlighter = new ColorHighlighter(HighlightPredicate.ROLLOVER_ROW, Color.YELLOW, null);
        table.addHighlighter(colorHighlighter);
        Action action = new AbstractAction("toggle table enabled") {

            @Override
            public void actionPerformed(ActionEvent e) {
                table.setEnabled(!table.isEnabled());
                
            }
            
        };
        JXFrame frame = showWithScrollingInFrame(table, "rollover highlight, enabled/disabled table");
        addAction(frame, action);
        frame.pack();
    }
    

//--------------------- factory    
    /**
     * Simple ui-striping.
     *
     */
    public void interactiveTreeTableSimpleStripingUI() {
        JXTreeTable table = new JXTreeTable(new FileSystemModel());
        table.setVisibleRowCount(table.getRowCount() + 3);
        table.addHighlighter(HighlighterFactory.createSimpleStriping());
        showWithScrollingInFrame(table, "Simple ui striping");
    }
    
    /**
     * shows the effect of a simple striping highlighter on a 
     * colored table.
     *
     */
    public void interactiveTreeTableSimpleStriping() {
        JXTreeTable table = new JXTreeTable(new FileSystemModel());
        table.setVisibleRowCount(table.getRowCount() + 3);
        table.setBackground(new Color(0xC0FFC0));
        table.addHighlighter(HighlighterFactory.createSimpleStriping());
        showWithScrollingInFrame(table, "Simple gray striping");
    }
    /**
     * Simple ui-striping.
     *
     */
    public void interactiveSimpleStripingUI() {
        JXTable table = new JXTable(tableModel);
        table.setVisibleRowCount(table.getRowCount() + 3);
        table.addHighlighter(HighlighterFactory.createSimpleStriping());
        showWithScrollingInFrame(table, "Simple ui striping");
    }

    /**
     * shows the effect of a simple striping highlighter on a 
     * colored table.
     *
     */
    public void interactiveSimpleStriping() {
        JXTable table = new JXTable(tableModel);
        table.setVisibleRowCount(table.getRowCount() + 3);
        table.setBackground(new Color(0xC0FFC0));
        table.addHighlighter(HighlighterFactory.createSimpleStriping());
        Highlighter disabled = new AbstractHighlighter(HighlightPredicate.READ_ONLY) {
            
            @Override
            protected Component doHighlight(Component component,
                    ComponentAdapter adapter) {
                component.setEnabled(false);
                return component;
            }
        };
        table.getColumnExt(0).setEditable(false);
        table.getColumnExt(2).setEditable(false);
        table.getColumnExt(0).setCellRenderer(new DefaultTableRenderer(new TextFieldProvider()));
        table.addHighlighter(disabled);
        showWithScrollingInFrame(table, "Simple gray striping");
    }

    public static class TextFieldProvider extends ComponentProvider<JTextField> {

        @Override
        protected void configureState(CellContext context) {
            // TODO Auto-generated method stub
            
        }

        @Override
        protected JTextField createRendererComponent() {
            JTextField field = new JTextField(20);
            return field;
        }

        @Override
        protected void format(CellContext context) {
            rendererComponent.setText(getValueAsString(context));
        }
        
    }
    /**
     * shows the effect of a simple striping highlighter on a 
     * colored table.
     *
     */
    public void interactiveSimpleStripingGroup() {
        JXTable table = new JXTable(tableModel);
        table.setVisibleRowCount(table.getRowCount() + 3);
        table.setBackground(Color.YELLOW);
        table.addHighlighter(HighlighterFactory.createSimpleStriping(Color.LIGHT_GRAY, 3));
        showWithScrollingInFrame(table, "Simple gray striping, grouped by 3");
    }
    /**
     * shows the effect of a simple striping highlighter on a 
     * colored table.
     *
     */
    public void interactiveAlternateStriping() {
        JXTable table = new JXTable(tableModel);
        table.setVisibleRowCount(table.getRowCount() + 3);
        table.setBackground(Color.YELLOW);
        table.addHighlighter(HighlighterFactory.createAlternateStriping(Color.WHITE, Color.LIGHT_GRAY));
        showWithScrollingInFrame(table, "Alternate white/gray striping");
    }
    
    /**
     * shows the effect of a simple striping highlighter on a 
     * colored table.
     *
     */
    public void interactiveAlternateStripingGroup() {
        JXTable table = new JXTable(tableModel);
        table.setVisibleRowCount(table.getRowCount() + 3);
        table.setBackground(Color.YELLOW);
        table.addHighlighter(HighlighterFactory.createAlternateStriping(Color.WHITE, Color.LIGHT_GRAY, 3));
        showWithScrollingInFrame(table, "Alternate white/gray striping");
    }

    
    @Override
    protected void setUp() throws Exception {
        super.setUp();
         tableModel = new AncientSwingTeam();
     }

    /**
     * Do nothing, make the test runner happy
     * (would output a warning without a test fixture).
     *
     */
    public void testDummy() {
        
    }
}
