001 /*
002 * $Id: JXPanel.java,v 1.20 2006/05/14 08:12:17 dmouse Exp $
003 *
004 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
005 * Santa Clara, California 95054, U.S.A. All rights reserved.
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * You should have received a copy of the GNU Lesser General Public
018 * License along with this library; if not, write to the Free Software
019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
020 */
021
022 package org.jdesktop.swingx;
023
024 import java.awt.AlphaComposite;
025 import java.awt.Color;
026 import java.awt.Component;
027 import java.awt.Composite;
028 import java.awt.Dimension;
029 import java.awt.GradientPaint;
030 import java.awt.Graphics;
031 import java.awt.Graphics2D;
032 import java.awt.Insets;
033 import java.awt.LayoutManager;
034 import java.awt.Rectangle;
035 import java.awt.image.BufferedImage;
036
037 import javax.swing.JPanel;
038 import javax.swing.RepaintManager;
039 import javax.swing.Scrollable;
040 import org.jdesktop.swingx.painter.Painter;
041
042 /**
043 * A simple JPanel extension that adds translucency support.
044 * This component and all of its content will be displayed with the specified
045 * "alpha" transluscency property value. It also supports the
046 * Painter API.
047 *
048 * @author rbair
049 */
050 public class JXPanel extends JPanel implements Scrollable {
051 private boolean scrollableTracksViewportHeight;
052 private boolean scrollableTracksViewportWidth;
053
054 /**
055 * The alpha level for this component.
056 */
057 private float alpha = 1.0f;
058 /**
059 * If the old alpha value was 1.0, I keep track of the opaque setting because
060 * a translucent component is not opaque, but I want to be able to restore
061 * opacity to its default setting if the alpha is 1.0. Honestly, I don't know
062 * if this is necessary or not, but it sounded good on paper :)
063 * <p>TODO: Check whether this variable is necessary or not</p>
064 */
065 private boolean oldOpaque;
066 /**
067 * Indicates whether this component should inherit its parent alpha value
068 */
069 private boolean inheritAlpha = true;
070 /**
071 * Indicates whether the JXPanel should draw a gradient or not
072 * @deprecated Use setBackgroundPainter instead
073 */
074 private boolean drawGradient = false;
075 /**
076 * @deprecated Specify the Resize property on a GradientPainter instead
077 */
078 private boolean gradientTrackWidth = true;
079 /**
080 * @deprecated Specify the Resize property on a GradientPainter instead
081 */
082 private boolean gradientTrackHeight = true;
083 /**
084 * If the JXPanel is to draw a gradient, this paint indicates how it should
085 * be painted
086 *
087 * @deprecated
088 */
089 private GradientPaint gradientPaint;
090 /**
091 * Specifies the Painter to use for painting the background of this panel.
092 * If no painter is specified, the normal painting routine for JPanel
093 * is called. Old behavior is also honored for the time being if no
094 * backgroundPainter is specified
095 */
096 private Painter backgroundPainter;
097 /**
098 * Keeps track of the old dimensions so that if the dimensions change, the
099 * saved gradient image can be thrown out and re-rendered. This size is
100 * AFTER applying the insets!
101 */
102 private Dimension oldSize;
103 /**
104 * The cached gradient image
105 */
106 private BufferedImage cachedGradient;
107
108 /**
109 * Creates a new instance of JXPanel
110 */
111 public JXPanel() {
112 }
113
114 /**
115 * @param isDoubleBuffered
116 */
117 public JXPanel(boolean isDoubleBuffered) {
118 super(isDoubleBuffered);
119 }
120
121 /**
122 * @param layout
123 */
124 public JXPanel(LayoutManager layout) {
125 super(layout);
126 }
127
128 /**
129 * @param layout
130 * @param isDoubleBuffered
131 */
132 public JXPanel(LayoutManager layout, boolean isDoubleBuffered) {
133 super(layout, isDoubleBuffered);
134 }
135
136 /**
137 * Set the alpha transparency level for this component. This automatically
138 * causes a repaint of the component.
139 *
140 * <p>TODO add support for animated changes in translucency</p>
141 *
142 * @param alpha must be a value between 0 and 1 inclusive.
143 */
144 public void setAlpha(float alpha) {
145 if (this.alpha != alpha) {
146 assert alpha >= 0 && alpha <= 1.0;
147 float oldAlpha = this.alpha;
148 this.alpha = alpha;
149 if (alpha > 0f && alpha < 1f) {
150 if (oldAlpha == 1) {
151 //it used to be 1, but now is not. Save the oldOpaque
152 oldOpaque = isOpaque();
153 setOpaque(false);
154 }
155 RepaintManager manager = RepaintManager.currentManager(this);
156 if (!manager.getClass().isAnnotationPresent(TranslucentRepaintManager.class)) {
157 RepaintManager.setCurrentManager(new RepaintManagerX());
158 }
159 } else if (alpha == 1) {
160 //restore the oldOpaque if it was true (since opaque is false now)
161 if (oldOpaque) {
162 setOpaque(true);
163 }
164 }
165 firePropertyChange("alpha", oldAlpha, alpha);
166 repaint();
167 }
168 }
169
170 /**
171 * @return the alpha translucency level for this component. This will be
172 * a value between 0 and 1, inclusive.
173 */
174 public float getAlpha() {
175 return alpha;
176 }
177
178 /**
179 * Unlike other properties, alpha can be set on a component, or on one of
180 * its parents. If the alpha of a parent component is .4, and the alpha on
181 * this component is .5, effectively the alpha for this component is .4
182 * because the lowest alpha in the heirarchy "wins"
183 */
184 public float getEffectiveAlpha() {
185 if (inheritAlpha) {
186 float a = alpha;
187 Component c = this;
188 while ((c = c.getParent()) != null) {
189 if (c instanceof JXPanel) {
190 a = Math.min(((JXPanel)c).getAlpha(), a);
191 }
192 }
193 return a;
194 } else {
195 return alpha;
196 }
197 }
198
199 public boolean isInheritAlpha() {
200 return inheritAlpha;
201 }
202
203 public void setInheritAlpha(boolean val) {
204 if (inheritAlpha != val) {
205 inheritAlpha = val;
206 firePropertyChange("inheritAlpha", !inheritAlpha, inheritAlpha);
207 }
208 }
209
210 /* (non-Javadoc)
211 * @see javax.swing.Scrollable#getScrollableTracksViewportHeight()
212 */
213 public boolean getScrollableTracksViewportHeight() {
214 return scrollableTracksViewportHeight;
215 }
216
217 /* (non-Javadoc)
218 * @see javax.swing.Scrollable#getScrollableTracksViewportWidth()
219 */
220 public boolean getScrollableTracksViewportWidth() {
221 return scrollableTracksViewportWidth;
222 }
223
224 /* (non-Javadoc)
225 * @see javax.swing.Scrollable#getPreferredScrollableViewportSize()
226 */
227 public Dimension getPreferredScrollableViewportSize() {
228 return getPreferredSize();
229 }
230
231 /* (non-Javadoc)
232 * @see javax.swing.Scrollable#getScrollableBlockIncrement(java.awt.Rectangle, int, int)
233 */
234 public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
235 return 10;
236 }
237
238 /* (non-Javadoc)
239 * @see javax.swing.Scrollable#getScrollableUnitIncrement(java.awt.Rectangle, int, int)
240 */
241 public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
242 return 10;
243 }
244 /**
245 * @param scrollableTracksViewportHeight The scrollableTracksViewportHeight to set.
246 */
247 public void setScrollableTracksViewportHeight(boolean scrollableTracksViewportHeight) {
248 this.scrollableTracksViewportHeight = scrollableTracksViewportHeight;
249 }
250 /**
251 * @param scrollableTracksViewportWidth The scrollableTracksViewportWidth to set.
252 */
253 public void setScrollableTracksViewportWidth(boolean scrollableTracksViewportWidth) {
254 this.scrollableTracksViewportWidth = scrollableTracksViewportWidth;
255 }
256
257 /**
258 * @deprecated To specify a gradient for the panel, use the
259 * #setBackgroundPainter method, along with a Painter, like
260 * this:
261 * <pre><code>
262 * BasicGradientPainter gradient =
263 * new BasicGradientPainter(new GradientPaint(
264 * new Point2D.Double(0,0),
265 * Color.WHITE,
266 * new Point2D.Double(1,0),
267 * UIManager.getColor("control")));
268 * panel.setBackgroundPainter(gradient);
269 * </code></pre>
270 *
271 * There are several predefined gradients that may also be used. For example:
272 * <pre><code>
273 * BasicGradientPainter gradient =
274 * new BasicGradientPainter(BasicGradientPainter.WHITE_TO_CONTROL_HORIZONTAL);
275 * panel.setBackgroundPainter(gradient);
276 * </code></pre>
277 */
278 public void setGradientPaint(GradientPaint paint) {
279 GradientPaint oldPaint = this.gradientPaint;
280 this.gradientPaint = paint;
281 firePropertyChange("gradientPaint", oldPaint, paint);
282 repaint();
283 }
284
285 /**
286 * @deprecated See setGradientPaint
287 */
288 public GradientPaint getGradientPaint() {
289 return gradientPaint;
290 }
291
292 /**
293 * @deprecated See setGradientPaint
294 */
295 public void setDrawGradient(boolean b) {
296 if (drawGradient != b) {
297 boolean old = drawGradient;
298 drawGradient = b;
299 oldSize = getSize();
300 firePropertyChange("drawGradient", old, b);
301 repaint();
302 }
303 }
304
305 /**
306 * @deprecated See setGradientPaint
307 */
308 public boolean isDrawGradient() {
309 return drawGradient;
310 }
311
312 /**
313 * @deprecated See setGradientPaint
314 */
315 public void setGradientTrackWidth(boolean b) {
316 if (gradientTrackWidth != b) {
317 boolean old = gradientTrackWidth;
318 gradientTrackWidth = b;
319 firePropertyChange("gradientTrackWidth", old, b);
320 repaint();
321 }
322 }
323
324 /**
325 * @deprecated See setGradientPaint
326 */
327 public boolean isGradientTrackWidth() {
328 return gradientTrackWidth;
329 }
330
331 /**
332 * @deprecated See setGradientPaint
333 */
334 public void setGradientTrackHeight(boolean b) {
335 if (gradientTrackHeight != b) {
336 boolean old = gradientTrackHeight;
337 gradientTrackHeight = b;
338 firePropertyChange("gradientTrackHeight", old, b);
339 repaint();
340 }
341 }
342
343 /**
344 * @deprecated See setGradientPaint
345 */
346 public boolean isGradientTrackHeight() {
347 return gradientTrackHeight;
348 }
349
350 /**
351 * Specifies a Painter to use to paint the background of this JXPanel.
352 * If <code>p</code> is not null, then setOpaque(false) will be called
353 * as a side effect. A component should not be opaque if painters are
354 * being used, because Painters may paint transparent pixels or not
355 * paint certain pixels, such as around the border insets.
356 */
357 public void setBackgroundPainter(Painter p) {
358 Painter old = getBackgroundPainter();
359 this.backgroundPainter = p;
360
361 if (p != null) {
362 setOpaque(false);
363 }
364
365 firePropertyChange("backgroundPainter", old, getBackgroundPainter());
366 repaint();
367 }
368
369 public Painter getBackgroundPainter() {
370 return backgroundPainter;
371 }
372
373 /**
374 * Overriden paint method to take into account the alpha setting
375 */
376 @Override
377 public void paint(Graphics g) {
378 Graphics2D g2d = (Graphics2D)g;
379 Composite oldComp = g2d.getComposite();
380 float alpha = getEffectiveAlpha();
381 Composite alphaComp = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha);
382 g2d.setComposite(alphaComp);
383 super.paint(g2d);
384 g2d.setComposite(oldComp);
385 }
386
387 /**
388 * overridden to provide gradient painting
389 *
390 * TODO: Chris says that in OGL we actually suffer here by caching the
391 * gradient paint since the OGL pipeline will render gradients on
392 * hardware for us. The decision to use cacheing is based on my experience
393 * with gradient title borders slowing down repaints -- this could use more
394 * extensive analysis.
395 */
396 @Override
397 protected void paintComponent(Graphics g) {
398 if (backgroundPainter != null) {
399 backgroundPainter.paint((Graphics2D)g, this);
400 } else {
401 super.paintComponent(g);
402 if (drawGradient) {
403 Insets insets = getInsets();
404 int width = getWidth() - insets.right - insets.left;
405 int height = getHeight() - insets.top - insets.bottom;
406
407 //TODO need to detect a change in gradient paint as well
408 if (gradientPaint == null || oldSize == null || oldSize.width != width || oldSize.height != height) {
409 Color c1 = null;//UIManager.getColor("control");
410 Color c2 = null;//c.darker();
411 if (gradientPaint == null) {
412 c1 = getBackground();
413 c2 = new Color(c1.getRed() - 40, c1.getGreen() - 40, c1.getBlue() - 40);
414 float x1 = 0f;
415 float y1 = 0f;
416 float x2 = width;
417 float y2 = 0;
418 boolean cyclic = false;
419 gradientPaint = new GradientPaint(x1, y1, c1, x2, y2, c2, cyclic);
420 } else {
421 //same GP as before, but where the values differed for x1, x2, replace
422 //x2 with the current width, and where values differed for y1, y2
423 //replace with current height
424 GradientPaint gp = gradientPaint;
425 float x2 = (float)gp.getPoint2().getX();
426 if (gradientTrackWidth) {
427 float ratio = (float)width / (float)oldSize.width;
428 x2 = ((float)gp.getPoint2().getX()) * ratio;
429 }
430 float y2 = (float)gp.getPoint2().getY();
431 if (gradientTrackHeight) {
432 float ratio = (float)height / (float)oldSize.height;
433 y2 = ((float)gp.getPoint2().getY()) * ratio;
434 }
435 gradientPaint = new GradientPaint((float)gp.getPoint1().getX(),
436 (float)gp.getPoint1().getY(), gp.getColor1(),
437 x2, y2, gp.getColor2(),
438 gp.isCyclic());
439 }
440
441 oldSize = new Dimension(width, height);
442 cachedGradient = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
443 Graphics2D imgg = (Graphics2D)cachedGradient.getGraphics();
444 imgg.setPaint(gradientPaint);
445 imgg.fillRect(0, 0, width, height);
446 }
447
448 // draw the image
449 Graphics2D g2 = (Graphics2D)g;
450 g2.drawImage(cachedGradient, null, insets.left, insets.top);
451 }
452 }
453 }
454 }