001    /*
002     * This file is part of muCommander, http://www.mucommander.com
003     * Copyright (C) 2002-2008 Maxence Bernard
004     *
005     * muCommander is free software; you can redistribute it and/or modify
006     * it under the terms of the GNU General Public License as published by
007     * the Free Software Foundation; either version 3 of the License, or
008     * (at your option) any later version.
009     *
010     * muCommander is distributed in the hope that it will be useful,
011     * but WITHOUT ANY WARRANTY; without even the implied warranty of
012     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013     * GNU General Public License for more details.
014     *
015     * You should have received a copy of the GNU General Public License
016     * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017     */
018    
019    
020    package com.mucommander.ui.main.table;
021    
022    import javax.swing.*;
023    import javax.swing.border.Border;
024    import javax.swing.border.EmptyBorder;
025    import java.awt.*;
026    
027    
028    /**
029     * A custom <code>JLabel</code> component used by {@link FileTableCellRenderer FileTableCellRenderer} to render table cells.
030     *
031     * <p>CellLabel is basically a faster dumbed-down JLabel which overrides some JLabel to be no-ops (see below)
032     * and some other (setText, setIcon) to call JLabel's super methods only if value has changed since last call,
033     * as very often values don't change from one cell to another. Some methods were borrowed from Sun's 
034     * <code>DefaultTableCellRender</code> implementation and are marked as much.</p>
035     * 
036     * <p>Quote from Sun's Javadoc : The table class defines a single cell renderer and uses it as a 
037     * as a rubber-stamp for rendering all cells in the table;  it renders the first cell,
038     * changes the contents of that cell renderer, shifts the origin to the new location, re-draws it, and so on.
039     * <p>The standard <code>JLabel</code> component was not
040     * designed to be used this way and we want to avoid 
041     * triggering a <code>revalidate</code> each time the
042     * cell is drawn. This would greatly decrease performance because the
043     * <code>revalidate</code> message would be
044     * passed up the hierarchy of the container to determine whether any other
045     * components would be affected.  So this class
046     * overrides the <code>validate</code>, <code>revalidate</code>,
047     * <code>repaint</code>, and <code>firePropertyChange</code> methods to be 
048     * no-ops.
049     *
050     * @author Maxence Bernard, Sun Microsystems
051     */
052    public class CellLabel extends JLabel {
053        // - Constants -----------------------------------------------------------------------
054        // -----------------------------------------------------------------------------------
055        /** Amount of border space on the left and right of the cell */
056        public static final int CELL_BORDER_WIDTH = 4;
057        /** Amount of border space on the top and bottom of the cell */
058        public static final int CELL_BORDER_HEIGHT = 1;
059        /** Empty border to give more space around cells */
060        private static final Border CELL_BORDER = new EmptyBorder(CELL_BORDER_HEIGHT, CELL_BORDER_WIDTH, CELL_BORDER_HEIGHT, CELL_BORDER_WIDTH);
061    
062    
063    
064        // - Instance fields -----------------------------------------------------------------
065        // -----------------------------------------------------------------------------------
066        /** Last text set by the setText method */
067        private String    lastText;
068        /** Last icon set by the setIcon method */
069        private ImageIcon lastIcon;
070        /** Last tooltip text set by the setToolTipText method */
071        private String    lastTooltip;
072        /** Last foreground color set by the setForeground method */
073        private Color     lastForegroundColor;
074        /** Last background color set by the setBackground method */
075        private Color     lastBackgroundColor;
076        /** Outline color (top and bottom). */
077        private Color     outlineColor;
078        /** Gradient color for the background. */
079        private Color     gradientColor;
080    
081    
082    
083        // - Initialisation ------------------------------------------------------------------
084        // -----------------------------------------------------------------------------------
085        /**
086         * Creates a new blank CellLabel.
087         */
088        public CellLabel() {setBorder(CELL_BORDER);}
089    
090    
091        // - Color changing ------------------------------------------------------------------
092        // -----------------------------------------------------------------------------------
093        /**
094         * Overrides <code>JComponent.setForeground</code> to call 
095         * the super method only if the value has changed since last call.
096         * 
097         * @param c the new foreground's color for this label
098         */
099        public void setForeground(Color c) {
100            if((c != null && !c.equals(lastForegroundColor)) || (lastForegroundColor != null && !lastForegroundColor.equals(c))) {
101                super.setForeground(c); 
102                lastForegroundColor = c;
103            }
104        }
105        
106        /**
107         * Overrides <code>JComponent.setBackground</code> to call 
108         * the super method only if the value has changed since last call.
109         * 
110         * @param c the new background's color for this label
111         */
112        public void setBackground(Color c) {
113            if((c != null && !c.equals(lastBackgroundColor)) || (lastBackgroundColor != null && !lastBackgroundColor.equals(c))) {
114                super.setBackground(c); 
115                lastBackgroundColor = c;
116                gradientColor       = null;
117            }
118        }
119    
120        /**
121         * Sets the background to a gradient between the two specified colors.
122         * @param c1 first component of the gradient.
123         * @param c2 second component of the gradient.
124         */
125        public void setBackground(Color c1, Color c2) {
126            if(c1.equals(c2))
127                setBackground(c1);
128            else {
129                lastBackgroundColor = c1;
130                gradientColor       = c2;
131            }
132        }
133    
134    
135        /**
136         * Sets the label outline color.
137         * @param c the new background's color for this label
138         */
139        public void setOutline(Color c) {outlineColor = c;}
140    
141    
142    
143        // - Label content -------------------------------------------------------------------
144        // -----------------------------------------------------------------------------------
145        /**
146         * Overrides <code>JLabel.setText</code> to call 
147         * the super method only if the value has changed since last call.
148         * 
149         * @param text the new text this label will display
150         */
151        public void setText(String text) {
152            if((text!=null && !text.equals(lastText)) || (lastText!=null && !lastText.equals(text))) {
153                super.setText(text);
154                lastText = text;
155            }
156        }
157    
158    
159        /**
160         * Overrides <code>JLabel.setIcon</code> to call 
161         * the super method only if the value has changed since last call.
162         * 
163         * @param icon the new icon this label will display
164         */
165        public void setIcon(ImageIcon icon) {
166            if(icon!=lastIcon) {
167                super.setIcon(icon);
168                lastIcon = icon;
169            }
170        }
171    
172    
173        /**
174         * Overrides <code>JLabel.setToolTipText</code> to call 
175         * the super method only if the value has changed since last call.
176         * 
177         * @param tooltip the new tooltip this label will display
178         */
179        public void setToolTipText(String tooltip) {
180            if((tooltip!=null && !tooltip.equals(lastTooltip)) || (lastTooltip!=null && !lastTooltip.equals(tooltip))) {
181                super.setToolTipText(tooltip);
182                lastTooltip = tooltip;
183            }
184        }
185    
186    
187    
188        // - Painting ------------------------------------------------------------------------
189        // -----------------------------------------------------------------------------------
190        /**
191         * Paints the label.
192         * @param g where to paint the label.
193         */
194        public void paint(Graphics g) {
195            boolean doOutline;
196    
197            doOutline = outlineColor != null && !outlineColor.equals(lastBackgroundColor);
198    
199            // Checks whether we need to paint a gradient background.
200            if(gradientColor != null) {
201                Graphics2D g2;       // Allows us to use the setPaint and getPaint methods.
202                Paint      oldPaint; // Previous Paint affected to g.
203    
204                // Initialisation.
205                g2       = (Graphics2D)g;
206                oldPaint = g2.getPaint();
207    
208                // Paints the gradient background.
209                g2.setPaint(new GradientPaint(0, 0, lastBackgroundColor, 0, getHeight(), gradientColor, false));
210                if(doOutline)
211                    g2.fillRect(0, 1, getWidth(), getHeight() - 2);
212                else
213                    g2.fillRect(0, 0, getWidth(), getHeight());
214    
215                // Restores the graphics to its previous state.
216                g2.setPaint(oldPaint);
217            }
218    
219            // Normal painting.
220            super.paint(g);
221    
222            // If necessary, paints the outline color.
223            if(doOutline) {
224                g.setColor(outlineColor);
225                g.drawLine(0, 0, getWidth(), 0);
226                g.drawLine(0, getHeight() - 1, getWidth(), getHeight() - 1);
227            }
228        }   
229    
230    
231    
232        // - DefaultTableCellRenderer implementation -----------------------------------------
233        // -----------------------------------------------------------------------------------
234        /*
235         * The following methods are overridden as a performance measure to 
236         * to prune code-paths are often called in the case of renders
237         * but which we know are unnecessary.  Great care should be taken
238         * when writing your own renderer to weigh the benefits and 
239         * drawbacks of overriding methods like these.
240         */
241    
242        /**
243         * Overridden for performance reasons.
244         */
245        public boolean isOpaque() {
246            // If we're not using a gradient background, the component's opaque
247            // status is context dependant.
248            if(gradientColor == null) {
249                Color     back;
250                Component p;
251    
252                back = lastBackgroundColor;
253                if((p = getParent()) != null)
254                    p = p.getParent();
255    
256                // The label does not need to be opaque if it has an opaque parent component
257                // of the same background color.
258                return !((back != null) && (p != null) && back.equals(p.getBackground()) && p.isOpaque());
259            }
260    
261            // We must consider the label not to be opaque, otherwise the gradient would be overpainted by
262            // the component's background color.
263            return false;
264        }
265    
266        /**
267         * Overridden for performance reasons.
268         */
269        public void validate() {}
270    
271        /**
272         * Overridden for performance reasons.
273         */
274        public void revalidate() {}
275    
276        /**
277         * Overridden for performance reasons.
278         */
279        public void repaint(long tm, int x, int y, int width, int height) {}
280    
281        /**
282         * Overridden for performance reasons.
283         */
284        public void repaint(Rectangle r) { }
285    
286        /**
287         * Overridden for performance reasons.
288         */
289        protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {  
290            // Strings get interned...
291            if(propertyName.equals("text"))
292                super.firePropertyChange(propertyName, oldValue, newValue);
293        }
294    
295        /**
296         * Overridden for performance reasons.
297         */
298        public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { }
299    }