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 }