/*
 * This file is part of muCommander, http://www.mucommander.com
 *
 * muCommander is free software; 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 3 of the License, or
 * (at your option) any later version.
 *
 * muCommander 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */


package com.mucommander.ui.main.table;

import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import java.awt.*;


/**
 * A custom <code>JLabel</code> component used by {@link FileTableCellRenderer FileTableCellRenderer} to render table cells.
 *
 * <p>CellLabel is basically a faster dumbed-down JLabel which overrides some JLabel to be no-ops (see below)
 * and some other (setText, setIcon) to call JLabel's super methods only if value has changed since last call,
 * as very often values don't change from one cell to another. Some methods were borrowed from Sun's 
 * <code>DefaultTableCellRender</code> implementation and are marked as much.</p>
 * 
 * <p>Quote from Sun's Javadoc : The table class defines a single cell renderer and uses it as a 
 * as a rubber-stamp for rendering all cells in the table;  it renders the first cell,
 * changes the contents of that cell renderer, shifts the origin to the new location, re-draws it, and so on.
 * <p>The standard <code>JLabel</code> component was not
 * designed to be used this way and we want to avoid 
 * triggering a <code>revalidate</code> each time the
 * cell is drawn. This would greatly decrease performance because the
 * <code>revalidate</code> message would be
 * passed up the hierarchy of the container to determine whether any other
 * components would be affected.  So this class
 * overrides the <code>validate</code>, <code>revalidate</code>,
 * <code>repaint</code>, and <code>firePropertyChange</code> methods to be 
 * no-ops.
 *
 * @author Maxence Bernard, Sun Microsystems
 */
public class CellLabel extends JLabel {
    // - Constants -----------------------------------------------------------------------
    // -----------------------------------------------------------------------------------
    /** Amount of border space on the left and right of the cell */
    public static final int CELL_BORDER_WIDTH = 4;
    /** Amount of border space on the top and bottom of the cell */
    public static final int CELL_BORDER_HEIGHT = 1;
    /** Empty border to give more space around cells */
    private static final Border CELL_BORDER = new EmptyBorder(CELL_BORDER_HEIGHT, CELL_BORDER_WIDTH, CELL_BORDER_HEIGHT, CELL_BORDER_WIDTH);



    // - Instance fields -----------------------------------------------------------------
    // -----------------------------------------------------------------------------------
    /** Last text set by the setText method */
    protected String    lastText;
    /** Last icon set by the setIcon method */
    protected ImageIcon lastIcon;
    /** Last tooltip text set by the setToolTipText method */
    protected String    lastTooltip;
    /** Last foreground color set by the setForeground method */
    protected Color     lastForegroundColor;
    /** Last background color set by the setBackground method */
    protected Color     lastBackgroundColor;
    /** Outline color (top and bottom). */
    protected Color     outlineColor;
    /** Gradient color for the background. */
    protected Color     gradientColor;



    // - Initialisation ------------------------------------------------------------------
    // -----------------------------------------------------------------------------------
    /**
     * Creates a new blank CellLabel.
     */
    public CellLabel() {setBorder(CELL_BORDER);}


    // - Color changing ------------------------------------------------------------------
    // -----------------------------------------------------------------------------------
    /**
     * Overrides <code>JComponent.setForeground</code> to call 
     * the super method only if the value has changed since last call.
     * 
     * @param c the new foreground's color for this label
     */
    @Override
    public void setForeground(Color c) {
        if((c != null && !c.equals(lastForegroundColor)) || (lastForegroundColor != null && !lastForegroundColor.equals(c))) {
            super.setForeground(c); 
            lastForegroundColor = c;
        }
    }
    
    /**
     * Overrides <code>JComponent.setBackground</code> to call 
     * the super method only if the value has changed since last call.
     * 
     * @param c the new background's color for this label
     */
    @Override
    public void setBackground(Color c) {
        if((c != null && !c.equals(lastBackgroundColor)) || (lastBackgroundColor != null && !lastBackgroundColor.equals(c))) {
            super.setBackground(c); 
            lastBackgroundColor = c;
            gradientColor       = null;
        }
    }

    /**
     * Sets the background to a gradient between the two specified colors.
     * @param c1 first component of the gradient.
     * @param c2 second component of the gradient.
     */
    public void setBackground(Color c1, Color c2) {
        if(c1.equals(c2))
            setBackground(c1);
        else {
            lastBackgroundColor = c1;
            gradientColor       = c2;
        }
    }


    /**
     * Sets the label outline color.
     * @param c the new background's color for this label
     */
    public void setOutline(Color c) {outlineColor = c;}



    // - Label content -------------------------------------------------------------------
    // -----------------------------------------------------------------------------------
    /**
     * Overrides <code>JLabel.setText</code> to call 
     * the super method only if the value has changed since last call.
     * 
     * @param text the new text this label will display
     */
    @Override
    public void setText(String text) {
        if((text!=null && !text.equals(lastText)) || (lastText!=null && !lastText.equals(text))) {
            super.setText(text);
            lastText = text;
        }
    }


    /**
     * Overrides <code>JLabel.setIcon</code> to call 
     * the super method only if the value has changed since last call.
     * 
     * @param icon the new icon this label will display
     */
    public void setIcon(ImageIcon icon) {
        if(icon!=lastIcon) {
            super.setIcon(icon);
            lastIcon = icon;
        }
    }


    /**
     * Overrides <code>JLabel.setToolTipText</code> to call 
     * the super method only if the value has changed since last call.
     * 
     * @param tooltip the new tooltip this label will display
     */
    @Override
    public void setToolTipText(String tooltip) {
        if((tooltip!=null && !tooltip.equals(lastTooltip)) || (lastTooltip!=null && !lastTooltip.equals(tooltip))) {
            super.setToolTipText(tooltip);
            lastTooltip = tooltip;
        }
    }



    // - Painting ------------------------------------------------------------------------
    // -----------------------------------------------------------------------------------
    /**
     * Paints the label.
     * @param g where to paint the label.
     */
    @Override
    public void paint(Graphics g) {
        boolean doOutline;

        doOutline = outlineColor != null && !outlineColor.equals(lastBackgroundColor);

        // Checks whether we need to paint a gradient background.
        if(gradientColor != null) {
            Graphics2D g2;       // Allows us to use the setPaint and getPaint methods.
            Paint      oldPaint; // Previous Paint affected to g.

            // Initialisation.
            g2       = (Graphics2D)g;
            oldPaint = g2.getPaint();

            // Paints the gradient background.
            g2.setPaint(new GradientPaint(0, 0, lastBackgroundColor, 0, getHeight(), gradientColor, false));
            if(doOutline)
                g2.fillRect(0, 1, getWidth(), getHeight() - 2);
            else
                g2.fillRect(0, 0, getWidth(), getHeight());

            // Restores the graphics to its previous state.
            g2.setPaint(oldPaint);
        }

        // Normal painting.
        super.paint(g);

        // If necessary, paints the outline color.
        if(doOutline)
        	paintOutline(g);
    }	

    protected void paintOutline(Graphics g) {
    	g.setColor(outlineColor);
        g.drawLine(0, 0, getWidth(), 0);
        g.drawLine(0, getHeight() - 1, getWidth(), getHeight() - 1);
    }

    
    // - DefaultTableCellRenderer implementation -----------------------------------------
    // -----------------------------------------------------------------------------------
    /*
     * The following methods are overridden as a performance measure to 
     * to prune code-paths are often called in the case of renders
     * but which we know are unnecessary.  Great care should be taken
     * when writing your own renderer to weigh the benefits and 
     * drawbacks of overriding methods like these.
     */

    /**
     * Overridden for performance reasons.
     */
    @Override
    public boolean isOpaque() {
        // If we're not using a gradient background, the component's opaque
        // status is context dependant.
        if(gradientColor == null) {
            Color     back;
            Component p;

            back = lastBackgroundColor;
            if((p = getParent()) != null)
                p = p.getParent();

            // The label does not need to be opaque if it has an opaque parent component
            // of the same background color.
            return !((back != null) && (p != null) && back.equals(p.getBackground()) && p.isOpaque());
        }

        // We must consider the label not to be opaque, otherwise the gradient would be overpainted by
        // the component's background color.
        return false;
    }

    /**
     * Overridden for performance reasons.
     */
    @Override
    public void validate() {}

    /**
     * Overridden for performance reasons.
     */
    @Override
    public void revalidate() {}

    /**
     * Overridden for performance reasons.
     */
    @Override
    public void repaint(long tm, int x, int y, int width, int height) {}

    /**
     * Overridden for performance reasons.
     */
    @Override
    public void repaint(Rectangle r) { }

    /**
     * Overridden for performance reasons.
     */
    @Override
    protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
        // Strings get interned...
        if(propertyName.equals("text"))
            super.firePropertyChange(propertyName, oldValue, newValue);
    }

    /**
     * Overridden for performance reasons.
     */
    @Override
    public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { }
}
