/* * This file is part of muCommander, http://www.mucommander.com * Copyright (C) 2002-2016 Maxence Bernard * * 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) { } }