All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alee.laf.table.TableHeaderPainter Maven / Gradle / Ivy

package com.alee.laf.table;

import com.alee.api.jdk.Objects;
import com.alee.managers.language.Language;
import com.alee.managers.language.LanguageListener;
import com.alee.managers.language.LanguageSensitive;
import com.alee.managers.language.UILanguageManager;
import com.alee.managers.style.Bounds;
import com.alee.painter.AbstractPainter;
import com.alee.utils.SwingUtils;

import javax.swing.*;
import javax.swing.event.TableModelEvent;
import javax.swing.table.*;
import java.awt.*;

/**
 * Basic painter for {@link JTableHeader} component.
 * It is used as {@link WebTableHeaderUI} default painter.
 *
 * @param  component type
 * @param  component UI type
 * @author Alexandr Zernov
 * @author Mikle Garin
 */

public class TableHeaderPainter extends AbstractPainter
        implements ITableHeaderPainter
{
    /**
     * Listeners.
     */
    protected transient LanguageListener languageSensitive;

    /**
     * Style settings.
     * todo Replace with single background painter per cell?
     */
    protected Integer headerHeight;
    protected Color topBgColor;
    protected Color bottomBgColor;
    protected Color gridColor;

    /**
     * Painting variables.
     */
    protected transient CellRendererPane rendererPane = null;

    @Override
    protected void installPropertiesAndListeners ()
    {
        super.installPropertiesAndListeners ();
        installLanguageListeners ();
    }

    @Override
    protected void uninstallPropertiesAndListeners ()
    {
        uninstallLanguageListeners ();
        super.uninstallPropertiesAndListeners ();
    }

    /**
     * Installs language listeners.
     */
    protected void installLanguageListeners ()
    {
        languageSensitive = new LanguageListener ()
        {
            @Override
            public void languageChanged ( final Language oldLanguage, final Language newLanguage )
            {
                if ( isLanguageSensitive () )
                {
                    final JTable table = component.getTable ();
                    if ( table != null && table.getModel () instanceof AbstractTableModel )
                    {
                        // Calling public model methods when possible
                        final AbstractTableModel tableModel = ( AbstractTableModel ) table.getModel ();
                        tableModel.fireTableRowsUpdated ( TableModelEvent.HEADER_ROW, TableModelEvent.HEADER_ROW );
                    }
                    else
                    {
                        // Simply repainting table header
                        component.repaint ();
                    }
                }
            }
        };
        UILanguageManager.addLanguageListener ( component, languageSensitive );
    }

    /**
     * Returns whether or not table is language-sensitive.
     *
     * @return {@code true} if table is language-sensitive, {@code false} otherwise
     */
    protected boolean isLanguageSensitive ()
    {
        boolean sensitive = false;
        if ( component instanceof LanguageSensitive ||
                component.getDefaultRenderer () instanceof LanguageSensitive ||
                component.getTable () instanceof LanguageSensitive ||
                component.getTable () != null && component.getTable ().getModel () instanceof LanguageSensitive )
        {
            // Either table header, its default renderer, table itself or table model is language-sensitive
            sensitive = true;
        }
        else
        {
            // Checking whether or not one of table header column renderers is language-sensitive
            final TableColumnModel columnModel = component.getColumnModel ();
            for ( int i = 0; i < columnModel.getColumnCount (); i++ )
            {
                if ( getHeaderRenderer ( i ) instanceof LanguageSensitive )
                {
                    sensitive = true;
                    break;
                }
            }
            if ( !sensitive )
            {
                // Checking whether or not one of table header values is language-sensitive
                for ( int i = 0; i < columnModel.getColumnCount (); i++ )
                {
                    if ( columnModel.getColumn ( i ).getHeaderValue () instanceof LanguageSensitive )
                    {
                        sensitive = true;
                        break;
                    }
                }
            }
        }
        return sensitive;
    }

    /**
     * Uninstalls language listeners.
     */
    protected void uninstallLanguageListeners ()
    {
        UILanguageManager.removeLanguageListener ( component, languageSensitive );
        languageSensitive = null;
    }

    @Override
    public void prepareToPaint ( final CellRendererPane rendererPane )
    {
        // Saving renderer pane reference
        this.rendererPane = rendererPane;
    }

    @Override
    public void paint ( final Graphics2D g2d, final C c, final U ui, final Bounds bounds )
    {
        // Creating background paint
        final Paint bgPaint = getBackgroundPaint ( 0, 0, 0, component.getHeight () - 1 );

        // Table header background
        g2d.setPaint ( bgPaint );
        g2d.fillRect ( 0, 0, component.getWidth (), component.getHeight () - 1 );

        // Bottom border line
        g2d.setPaint ( gridColor );
        g2d.drawLine ( 0, component.getHeight () - 1, component.getWidth () - 1, component.getHeight () - 1 );

        // Optimization for empty header
        if ( component.getColumnModel ().getColumnCount () > 0 )
        {
            // Variables
            final Rectangle clip = g2d.getClipBounds ();
            final Point left = clip.getLocation ();
            final Point right = new Point ( clip.x + clip.width - 1, clip.y );
            final TableColumnModel cm = component.getColumnModel ();
            int cMin = component.columnAtPoint ( ltr ? left : right );
            int cMax = component.columnAtPoint ( ltr ? right : left );

            // This should never happen.
            if ( cMin == -1 )
            {
                cMin = 0;
            }

            // If the table does not have enough columns to fill the view we'll get -1.
            // Replace this with the index of the last column.
            if ( cMax == -1 )
            {
                cMax = cm.getColumnCount () - 1;
            }

            // Table titles
            final TableColumn draggedColumn = component.getDraggedColumn ();
            int columnWidth;
            final Rectangle cellRect = component.getHeaderRect ( ltr ? cMin : cMax );
            TableColumn aColumn;
            if ( ltr )
            {
                for ( int column = cMin; column <= cMax; column++ )
                {
                    aColumn = cm.getColumn ( column );
                    columnWidth = aColumn.getWidth ();
                    cellRect.width = columnWidth;
                    if ( aColumn != draggedColumn )
                    {
                        paintCell ( g2d, c, cellRect, column, aColumn, draggedColumn, cm );
                    }
                    cellRect.x += columnWidth;
                }
            }
            else
            {
                for ( int column = cMax; column >= cMin; column-- )
                {
                    aColumn = cm.getColumn ( column );
                    columnWidth = aColumn.getWidth ();
                    cellRect.width = columnWidth;
                    if ( aColumn != draggedColumn )
                    {
                        paintCell ( g2d, c, cellRect, column, aColumn, draggedColumn, cm );
                    }
                    cellRect.x += columnWidth;
                }
            }

            // Paint the dragged column if we are dragging.
            if ( draggedColumn != null )
            {
                // Calculating dragged cell rect
                final int draggedColumnIndex = getViewIndexForColumn ( draggedColumn );
                final Rectangle draggedCellRect = component.getHeaderRect ( draggedColumnIndex );
                draggedCellRect.x += component.getDraggedDistance ();

                // Background
                g2d.setPaint ( bgPaint );
                g2d.fillRect ( draggedCellRect.x - 1, draggedCellRect.y, draggedCellRect.width, draggedCellRect.height - 1 );

                // Header cell
                paintCell ( g2d, c, draggedCellRect, draggedColumnIndex, draggedColumn, draggedColumn, cm );
            }

            // Remove all components in the rendererPane
            rendererPane.removeAll ();

            // Clearing renderer pane reference
            rendererPane = null;
        }
    }

    /**
     * Paints single table header cell (column).
     *
     * @param g2d           graphics context
     * @param c             table header
     * @param rect          cell bounds
     * @param columnIndex   column index
     * @param column        table column
     * @param draggedColumn currently dragged table column
     * @param columnModel   table column model
     */
    protected void paintCell ( final Graphics2D g2d, final C c, final Rectangle rect, final int columnIndex, final TableColumn column,
                               final TableColumn draggedColumn, final TableColumnModel columnModel )
    {
        // Table reference
        final JTable table = c.getTable ();

        // Complex check for the cases when trailing border should be painted
        // It can be painted for middle columns, dragged column or when table is smaller than viewport
        final JScrollPane scrollPane = SwingUtils.getScrollPane ( table );
        final boolean paintTrailingBorder = scrollPane != null && ( column == draggedColumn ||
                table.getAutoResizeMode () == JTable.AUTO_RESIZE_OFF && scrollPane.getViewport ().getWidth () > table.getWidth () ||
                ( ltr ? columnIndex != columnModel.getColumnCount () - 1 : columnIndex != 0 ) );

        // Left side border
        if ( ltr || paintTrailingBorder )
        {
            g2d.setColor ( gridColor );
            g2d.drawLine ( rect.x - 1, rect.y + 2, rect.x - 1, rect.y + rect.height - 4 );
        }

        // Painting dragged cell renderer
        final JComponent headerRenderer = ( JComponent ) getHeaderRenderer ( columnIndex );
        headerRenderer.setOpaque ( false );
        headerRenderer.setEnabled ( table == null || table.isEnabled () );
        rendererPane.paintComponent ( g2d, headerRenderer, component, rect.x, rect.y, rect.width, rect.height, true );

        // Right side border
        if ( !ltr || paintTrailingBorder )
        {
            g2d.setColor ( gridColor );
            g2d.drawLine ( rect.x + rect.width - 1, rect.y + 2, rect.x + rect.width - 1, rect.y + rect.height - 4 );
        }
    }

    /**
     * Returns table header background {@link Paint}.
     *
     * @param x1 first painting bounds X coordinate
     * @param y1 first painting bounds Y coordinate
     * @param x2 second painting bounds X coordinate
     * @param y2 second painting bounds Y coordinate
     * @return table header background {@link Paint}
     */
    protected Paint getBackgroundPaint ( final int x1, final int y1, final int x2, final int y2 )
    {
        if ( bottomBgColor == null || Objects.equals ( topBgColor, bottomBgColor ) )
        {
            return topBgColor;
        }
        else
        {
            return new GradientPaint ( x1, y1, topBgColor, x2, y2, bottomBgColor );
        }
    }

    /**
     * Returns header cell renderer {@link Component} for the specified column index.
     *
     * @param index column index
     * @return header cell renderer {@link Component} for the specified column index
     */
    protected Component getHeaderRenderer ( final int index )
    {
        final TableColumn aColumn = component.getColumnModel ().getColumn ( index );
        TableCellRenderer renderer = aColumn.getHeaderRenderer ();
        if ( renderer == null )
        {
            renderer = component.getDefaultRenderer ();
        }
        final boolean hasFocus = !component.isPaintingForPrint () && component.hasFocus ();
        return renderer.getTableCellRendererComponent ( component.getTable (), aColumn.getHeaderValue (), false, hasFocus, -1, index );
    }

    /**
     * Returns actual view index for the specified column.
     *
     * @param column table column
     * @return actual view index for the specified column
     */
    protected int getViewIndexForColumn ( final TableColumn column )
    {
        final TableColumnModel cm = component.getColumnModel ();
        for ( int index = 0; index < cm.getColumnCount (); index++ )
        {
            if ( cm.getColumn ( index ) == column )
            {
                return index;
            }
        }
        return -1;
    }

    @Override
    public Dimension getPreferredSize ()
    {
        final Dimension ps = super.getPreferredSize ();
        if ( headerHeight != null )
        {
            ps.height = Math.max ( ps.height, headerHeight );
        }
        return ps;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy