swingx-master/swingx-core/src/test/java/org/jdesktop/swingx/JXTableHeaderTest.java
/*
 * Created on 09.06.2006
 *
 */
package org.jdesktop.swingx;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.GraphicsEnvironment;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.util.logging.Logger;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;

import org.jdesktop.swingx.table.TableColumnExt;
import org.jdesktop.test.PropertyChangeReport;
import org.jdesktop.test.TestUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;


@RunWith(JUnit4.class)
public class JXTableHeaderTest extends InteractiveTestCase {
    private static final Logger LOG = Logger.getLogger(JXTableHeaderTest.class
            .getName());
    
    /**
     * the table used to instantiate a TableColumnModel used in the header.
     */
    private JXTable table;
    /**
     * A header instantiated with the same TableColumnModel as the table.
     * NOTE: this is a standalone header, except for the model unrelated
     * to the table.
     */
    private JXTableHeader header;

    @Test
    public void testSortsOnDoubleClick() {
        assertFalse(header.getResortsOnDoubleClick());
        header.setResortsOnDoubleClick(true);
        assertEquals(header.getXTable() != null, header.getResortsOnDoubleClick());
        header.setTable(table);
        assertEquals(header.getXTable() != null, header.getResortsOnDoubleClick());
        
    }

    /**
     * Issue #1563-swingx: find cell that was clicked for componentPopup
     * 
     * Test api and event firing.
     */
    @Test
    public void testPopupTriggerLocationAvailable() {
        JXTableHeader table = (JXTableHeader) new JXTable(10, 3).getTableHeader();
        MouseEvent event = new MouseEvent(table, 0,
                0, 0, 40, 5, 0, false);
        PropertyChangeReport report = new PropertyChangeReport(table);
        table.getPopupLocation(event);
        assertEquals(event.getPoint(), table.getPopupTriggerLocation());
        TestUtils.assertPropertyChangeEvent(report, "popupTriggerLocation", 
                null, event.getPoint());
    }
    
    
    /**
     * Issue #1563-swingx: find cell that was clicked for componentPopup
     * 
     * Test safe return value.
     */
    @Test
    public void testPopupTriggerCopy() {
        JXTableHeader table = (JXTableHeader) new JXTable(10, 3).getTableHeader();
        MouseEvent event = new MouseEvent(table, 0,
                0, 0, 40, 5, 0, false);
        table.getPopupLocation(event);
        assertNotSame("trigger point must not be same", 
                table.getPopupTriggerLocation(), table.getPopupTriggerLocation());
    }
    
    /**
     * Issue #1563-swingx: find cell that was clicked for componentPopup
     * 
     * Test safe handle null.
     */
    @Test
    public void testPopupTriggerKeyboard() {
        JXTableHeader table = (JXTableHeader) new JXTable(10, 3).getTableHeader();
        MouseEvent event = new MouseEvent(table, 0,
                0, 0, 40, 5, 0, false);
        table.getPopupLocation(event);
        PropertyChangeReport report = new PropertyChangeReport(table);
        table.getPopupLocation(null);
        assertNull("trigger must null", 
                table.getPopupTriggerLocation());
        TestUtils.assertPropertyChangeEvent(report, "popupTriggerLocation", 
                event.getPoint(), null);
    }


    
    /**
     * Issue #1341-swingx: resizing/dragged/column/distance bound properties.
     */
    @Test
    public void testNotifyResizingColumn() {
        assertNull(header.getResizingColumn());
        PropertyChangeReport report = new PropertyChangeReport(header);
        TableColumn tableColumn = header.getColumnModel().getColumn(0);
        header.setResizingColumn(tableColumn);
        TestUtils.assertPropertyChangeEvent(report, "resizingColumn", null, tableColumn);
        
    }
    
    /**
     * Issue #1341-swingx: resizing/dragged/column/distance bound properties.
     */
    @Test
    public void testNotifyDraggedDistance() {
        PropertyChangeReport report = new PropertyChangeReport(header);
        header.setDraggedDistance(10);
        TestUtils.assertPropertyChangeEvent(report, "draggedDistance", 0, 10);
    }
    
    /**
     * Issue #1341-swingx: resizing/dragged/column/distance bound properties.
     */
    @Test
    public void testNotifyDraggedColumn() {
        assertNull(header.getDraggedColumn());
        PropertyChangeReport report = new PropertyChangeReport(header);
        TableColumn tableColumn = header.getColumnModel().getColumn(0);
        header.setDraggedColumn(tableColumn);
        TestUtils.assertPropertyChangeEvent(report, "draggedColumn", null, tableColumn);
    }
    

    /**
     * Issue #1154-swingx: Regression after switching to Mustang.
     * was: Issue #31 (swingx): clicking header must not sort if table !enabled.
     * 
     * Actually, an older debate - to disable or not to disable the header
     * if table disabled. Decided to synch header disabled (via listener in
     * JXTableHeader. This is consistent with ColumnControlButton.
     *
     */
    @Test
    public void testEnabledSynched() {
        // test synch on propertyChange
        JXTable table = new JXTable();
        table.setEnabled(false);
        assertEquals(table.isEnabled(), table.getTableHeader().isEnabled());
        // test synch on setTable
        JXTableHeader header = new JXTableHeader();
        header.setTable(table);
        assertEquals(table.isEnabled(), header.isEnabled());
        // test true if table null
        header.setTable(null);
        assertEquals(true, header.isEnabled());
    }
    
    

    /**
     * Issue #485-swingx: table header disappears if all header values are
     * empty. 
     * 
     * fixed for SwingX.
     *
     */
    @Test
    public void testHeaderSizeEmptyStringHeaderValue() {
        final String[] alternate = { 
                "", 
                "", 
                };
        JXTable xTable = new JXTable(10, 2);
        xTable.getColumn(0).setHeaderValue(alternate[0]);
        xTable.getColumn(1).setHeaderValue(alternate[1]);
        assertTrue("header height must be > 0", xTable.getTableHeader().getPreferredSize().height > 0);
    }

    /**
     * Issue #485-swingx: table header disappears if all header values are
     * empty. 
     * 
     * fixed for Swingx.
     *
     */
    @Test
    public void testHeaderSizeNullHeaderValue() {
        final String[] alternate = { 
                null, 
                null, 
                };
        JXTable xTable = new JXTable(10, 2);
        xTable.getColumn(0).setHeaderValue(alternate[0]);
        xTable.getColumn(1).setHeaderValue(alternate[1]);
        assertTrue("header height must be > 0", xTable.getTableHeader().getPreferredSize().height > 0);
    }
    /**
     * Issue #390-swingx: JXTableHeader: throws AIOOB on removing dragged column.
     * Test that getDraggedColumn is null if removed.
     * 
     * Problem was reported on mac:
     * http://forums.java.net/jive/thread.jspa?threadID=18368&tstart=0
     * when hiding column while drag(?) is in process.
     * 
     *
     */
    @Test
    public void testDraggedColumnRemoved() {
        JXTable table = new JXTable(10, 2);
        TableColumnExt columnExt = table.getColumnExt(0);
        table.getTableHeader().setDraggedColumn(columnExt);
        // sanity assert
        assertEquals(columnExt, table.getTableHeader().getDraggedColumn());
        table.getColumnModel().removeColumn(columnExt);
        assertNull("draggedColumn must be null if removed", table.getTableHeader().getDraggedColumn());
    }
    
    /**
     * Issue #390-swingx: JXTableHeader: throws AIOOB on removing dragged column.
     * Test that getDraggedColumn is visible or null.
     * 
     * Problem was reported on mac:
     * http://forums.java.net/jive/thread.jspa?threadID=18368&tstart=0
     * when hiding column while drag(?) is in process.
     */
    @Test
    public void testDraggedColumnVisible() {
        JXTable table = new JXTable(10, 2);
        TableColumnExt columnExt = table.getColumnExt(0);
        table.getTableHeader().setDraggedColumn(columnExt);
        // sanity assert
        assertEquals(columnExt, table.getTableHeader().getDraggedColumn());
        columnExt.setVisible(false);
        assertNull("dragged column must visible or null", table.getTableHeader().getDraggedColumn());
    }
    
    
    /**
     * Issue 334-swingx: BasicTableHeaderUI.getPrefSize doesn't respect 
     *   all renderere's size requirements.
     *
     */
    @Test
    public void testPreferredHeight() {
        JXTable table = new JXTable(10, 2);
        TableColumnExt columnExt = table.getColumnExt(1);
        columnExt.setTitle("<html><center>Line 1<br>Line 2</center></html>");
        JXTableHeader tableHeader = (JXTableHeader) table.getTableHeader();
        TableCellRenderer renderer = tableHeader.getCellRenderer(1);
        Component comp = renderer.getTableCellRendererComponent(table, 
                columnExt.getHeaderValue(), false, false, -1, 1);
        Dimension prefRendererSize = comp.getPreferredSize();
        assertEquals("Header pref height must respect renderer",
                prefRendererSize.height, tableHeader.getPreferredSize().height);
    }
    

    /**
     * test doc'ed xheader.getToolTipText(MouseEvent) behaviour.
     *
     */
    @Test
    public void testColumnToolTip() {
        JXTable table = new JXTable(10, 2);
        TableColumnExt columnExt = table.getColumnExt(0);
        JXTableHeader tableHeader = (JXTableHeader) table.getTableHeader();
        MouseEvent event = new MouseEvent(tableHeader, 0,
                0, 0, 40, 5, 0, false);
        DefaultTableCellRenderer renderer = new DefaultTableCellRenderer();
        String rendererToolTip = "rendererToolTip";
        renderer.setToolTipText(rendererToolTip);
        columnExt.setHeaderRenderer(renderer);
        assertEquals(rendererToolTip, tableHeader.getToolTipText(event));
        String columnToolTip = "columnToolTip";
        columnExt.setToolTipText(columnToolTip);
        assertEquals(columnToolTip, tableHeader.getToolTipText(event));
        
    }
    
    /**
     * Issue #1560-swingx: column tooltip not working for stand-alone header.
     */
    @Test
    public void testColumnToolTipStandAlone() {
        JXTable table = new JXTable(10, 2);
        TableColumnExt columnExt = table.getColumnExt(0);
        JXTableHeader tableHeader = new JXTableHeader(table.getColumnModel());
        MouseEvent event = new MouseEvent(tableHeader, 0,
                  0, 0, 40, 5, 0, false);
        String columnToolTip = "columnToolTip";
        columnExt.setToolTipText(columnToolTip);
        assertEquals(columnToolTip, tableHeader.getToolTipText(event));

    }
    
    /**
     * #212-swingx: second last column cannot be set to invisible programatically.
     * 
     * One reason for the "trick" of reselecting the last is that 
     * the header and with it the columnControl vanishes if there is 
     * no visible column.
     * 
     * 
     *
     */
    @Test
    public void testHeaderVisibleWithoutColumns() {
        // This test will not work in a headless configuration.
        if (GraphicsEnvironment.isHeadless()) {
            LOG.fine("cannot run headerVisible - headless environment");
            return;
        }
        JXTable table = new JXTable();
        table.setColumnControlVisible(true);
        wrapWithScrollingInFrame(table, "");
        assertTrue("headerHeight must be > 0", table.getTableHeader().getHeight() > 0);
        assertEquals("headerWidth must be table width", 
                table.getWidth(), table.getTableHeader().getWidth());
        
    }
    
    /**
     * #212-swingx: second last column cannot be set to invisible programatically.
     * 
     * One reason for the "trick" of reselecting the last is that 
     * the header and with it the columnControl vanishes if there is 
     * no visible column.
     * 
     * 
     *
     */
    @Test
    public void testHeaderVisibleWithColumns() {
        // This test will not work in a headless configuration.
        if (GraphicsEnvironment.isHeadless()) {
            LOG.fine("cannot run headerVisible - headless environment");
            return;
        }
        JXTable table = new JXTable(10, 2);
        table.setColumnControlVisible(true);
        wrapWithScrollingInFrame(table, "");
        assertTrue("headerHeight must be > 0", table.getTableHeader().getHeight() > 0);
        table.setModel(new DefaultTableModel());
        assertTrue("headerHeight must be > 0", table.getTableHeader().getHeight() > 0);
        
    }
    

//--------------------------------- visual checks
    /**
     * Issue #485-swingx: table header disappears if all header values are
     * empty. Compare core <--> JXTable
     * fixed for SwingX.
     *
     */
    public void interactiveHeaderSizeRequirements() {
        
        final String[] alternate = { 
                null, 
                null, 
                };
        final JTable table = new JTable(10, 2);
        table.getColumnModel().getColumn(0).setHeaderValue(alternate[0]);
        table.getColumnModel().getColumn(1).setHeaderValue(alternate[1]);
        
        JXTable xTable = new JXTable(10, 2);
        xTable.getColumn(0).setHeaderValue(alternate[0]);
        xTable.getColumn(1).setHeaderValue(alternate[1]);
        
        JXFrame frame = wrapWithScrollingInFrame(table, xTable, "header height empty (core - xtable)");
        frame.setVisible(true);
        
    }

    /**
     * Issue #390-swingx: JXTableHeader: throws AIOOB on removing dragged column.
     * 
     */
    public void interactiveDraggedColumnRemoved() {
        final JXTable table = new JXTable(10, 5);
        Action deleteColumn = new AbstractAction("deleteCurrentColumn") {

            public void actionPerformed(ActionEvent e) {
                TableColumn column = table.getTableHeader().getDraggedColumn();
                if (column == null) return;
                table.getColumnModel().removeColumn(column);
            }
            
        };
        KeyStroke keyStroke = KeyStroke.getKeyStroke("F1");
        table.getInputMap(JTable.WHEN_IN_FOCUSED_WINDOW).put(keyStroke, "f1");
        table.getActionMap().put("f1", deleteColumn);
        JXFrame frame = wrapWithScrollingInFrame(table, "Remove dragged column with F1");
        frame.setVisible(true);
    }
    /**
     * Visual demo that header is always visible.
     */
    public void interactiveHeaderVisible() {
        final JXTable table = new JXTable();
        table.setColumnControlVisible(true);
        JXFrame frame = wrapWithScrollingInFrame(table, "header always visible");
        Action action = new AbstractAction("toggle model") {

            public void actionPerformed(ActionEvent e) {
                int columnCount = table.getColumnCount(true);
                table.setModel(columnCount > 0 ?
                        new DefaultTableModel() : new DefaultTableModel(10, 2));
                
            }
            
        };
        addAction(frame, action);
        frame.setVisible(true);
        
    }
    public static void main(String args[]) {
        JXTableHeaderTest test = new JXTableHeaderTest();
        try {
          test.runInteractiveTests();
         //   test.runInteractiveTests("interactive.*Siz.*");
        } catch (Exception e) {
            System.err.println("exception when executing interactive tests:");
            e.printStackTrace();
        } 
    }

    
    @Override
    @Before
    public void setUp() {
        table = new JXTable(10, 5); 
        header = new JXTableHeader(table.getColumnModel());
    }


    
    @After
    public void tearDownJ4() throws Exception {
        tearDown();
    }
    


}
