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
156: .currentManager(this );
157: if (!manager.getClass().isAnnotationPresent(
158: TranslucentRepaintManager.class)) {
159: RepaintManager
160: .setCurrentManager(new RepaintManagerX());
161: }
162: } else if (alpha == 1) {
163: //restore the oldOpaque if it was true (since opaque is false now)
164: if (oldOpaque) {
165: setOpaque(true);
166: }
167: }
168: firePropertyChange("alpha", oldAlpha, alpha);
169: repaint();
170: }
171: }
172:
173: /**
174: * @return the alpha translucency level for this component. This will be
175: * a value between 0 and 1, inclusive.
176: */
177: public float getAlpha() {
178: return alpha;
179: }
180:
181: /**
182: * Unlike other properties, alpha can be set on a component, or on one of
183: * its parents. If the alpha of a parent component is .4, and the alpha on
184: * this component is .5, effectively the alpha for this component is .4
185: * because the lowest alpha in the heirarchy "wins"
186: */
187: public float getEffectiveAlpha() {
188: if (inheritAlpha) {
189: float a = alpha;
190: Component c = this ;
191: while ((c = c.getParent()) != null) {
192: if (c instanceof JXPanel) {
193: a = Math.min(((JXPanel) c).getAlpha(), a);
194: }
195: }
196: return a;
197: } else {
198: return alpha;
199: }
200: }
201:
202: public boolean isInheritAlpha() {
203: return inheritAlpha;
204: }
205:
206: public void setInheritAlpha(boolean val) {
207: if (inheritAlpha != val) {
208: inheritAlpha = val;
209: firePropertyChange("inheritAlpha", !inheritAlpha,
210: inheritAlpha);
211: }
212: }
213:
214: /* (non-Javadoc)
215: * @see javax.swing.Scrollable#getScrollableTracksViewportHeight()
216: */
217: public boolean getScrollableTracksViewportHeight() {
218: return scrollableTracksViewportHeight;
219: }
220:
221: /* (non-Javadoc)
222: * @see javax.swing.Scrollable#getScrollableTracksViewportWidth()
223: */
224: public boolean getScrollableTracksViewportWidth() {
225: return scrollableTracksViewportWidth;
226: }
227:
228: /* (non-Javadoc)
229: * @see javax.swing.Scrollable#getPreferredScrollableViewportSize()
230: */
231: public Dimension getPreferredScrollableViewportSize() {
232: return getPreferredSize();
233: }
234:
235: /* (non-Javadoc)
236: * @see javax.swing.Scrollable#getScrollableBlockIncrement(java.awt.Rectangle, int, int)
237: */
238: public int getScrollableBlockIncrement(Rectangle visibleRect,
239: int orientation, int direction) {
240: return 10;
241: }
242:
243: /* (non-Javadoc)
244: * @see javax.swing.Scrollable#getScrollableUnitIncrement(java.awt.Rectangle, int, int)
245: */
246: public int getScrollableUnitIncrement(Rectangle visibleRect,
247: int orientation, int direction) {
248: return 10;
249: }
250:
251: /**
252: * @param scrollableTracksViewportHeight The scrollableTracksViewportHeight to set.
253: */
254: public void setScrollableTracksViewportHeight(
255: boolean scrollableTracksViewportHeight) {
256: this .scrollableTracksViewportHeight = scrollableTracksViewportHeight;
257: }
258:
259: /**
260: * @param scrollableTracksViewportWidth The scrollableTracksViewportWidth to set.
261: */
262: public void setScrollableTracksViewportWidth(
263: boolean scrollableTracksViewportWidth) {
264: this .scrollableTracksViewportWidth = scrollableTracksViewportWidth;
265: }
266:
267: /**
268: * @deprecated To specify a gradient for the panel, use the
269: * #setBackgroundPainter method, along with a Painter, like
270: * this:
271: * <pre><code>
272: * BasicGradientPainter gradient =
273: * new BasicGradientPainter(new GradientPaint(
274: * new Point2D.Double(0,0),
275: * Color.WHITE,
276: * new Point2D.Double(1,0),
277: * UIManager.getColor("control")));
278: * panel.setBackgroundPainter(gradient);
279: * </code></pre>
280: *
281: * There are several predefined gradients that may also be used. For example:
282: * <pre><code>
283: * BasicGradientPainter gradient =
284: * new BasicGradientPainter(BasicGradientPainter.WHITE_TO_CONTROL_HORIZONTAL);
285: * panel.setBackgroundPainter(gradient);
286: * </code></pre>
287: */
288: public void setGradientPaint(GradientPaint paint) {
289: GradientPaint oldPaint = this .gradientPaint;
290: this .gradientPaint = paint;
291: firePropertyChange("gradientPaint", oldPaint, paint);
292: repaint();
293: }
294:
295: /**
296: * @deprecated See setGradientPaint
297: */
298: public GradientPaint getGradientPaint() {
299: return gradientPaint;
300: }
301:
302: /**
303: * @deprecated See setGradientPaint
304: */
305: public void setDrawGradient(boolean b) {
306: if (drawGradient != b) {
307: boolean old = drawGradient;
308: drawGradient = b;
309: oldSize = getSize();
310: firePropertyChange("drawGradient", old, b);
311: repaint();
312: }
313: }
314:
315: /**
316: * @deprecated See setGradientPaint
317: */
318: public boolean isDrawGradient() {
319: return drawGradient;
320: }
321:
322: /**
323: * @deprecated See setGradientPaint
324: */
325: public void setGradientTrackWidth(boolean b) {
326: if (gradientTrackWidth != b) {
327: boolean old = gradientTrackWidth;
328: gradientTrackWidth = b;
329: firePropertyChange("gradientTrackWidth", old, b);
330: repaint();
331: }
332: }
333:
334: /**
335: * @deprecated See setGradientPaint
336: */
337: public boolean isGradientTrackWidth() {
338: return gradientTrackWidth;
339: }
340:
341: /**
342: * @deprecated See setGradientPaint
343: */
344: public void setGradientTrackHeight(boolean b) {
345: if (gradientTrackHeight != b) {
346: boolean old = gradientTrackHeight;
347: gradientTrackHeight = b;
348: firePropertyChange("gradientTrackHeight", old, b);
349: repaint();
350: }
351: }
352:
353: /**
354: * @deprecated See setGradientPaint
355: */
356: public boolean isGradientTrackHeight() {
357: return gradientTrackHeight;
358: }
359:
360: /**
361: * Specifies a Painter to use to paint the background of this JXPanel.
362: * If <code>p</code> is not null, then setOpaque(false) will be called
363: * as a side effect. A component should not be opaque if painters are
364: * being used, because Painters may paint transparent pixels or not
365: * paint certain pixels, such as around the border insets.
366: */
367: public void setBackgroundPainter(Painter p) {
368: Painter old = getBackgroundPainter();
369: this .backgroundPainter = p;
370:
371: if (p != null) {
372: setOpaque(false);
373: }
374:
375: firePropertyChange("backgroundPainter", old,
376: getBackgroundPainter());
377: repaint();
378: }
379:
380: public Painter getBackgroundPainter() {
381: return backgroundPainter;
382: }
383:
384: /**
385: * Overriden paint method to take into account the alpha setting
386: */
387: @Override
388: public void paint(Graphics g) {
389: Graphics2D g2d = (Graphics2D) g;
390: Composite oldComp = g2d.getComposite();
391: float alpha = getEffectiveAlpha();
392: Composite alphaComp = AlphaComposite.getInstance(
393: AlphaComposite.SRC_OVER, alpha);
394: g2d.setComposite(alphaComp);
395: super .paint(g2d);
396: g2d.setComposite(oldComp);
397: }
398:
399: /**
400: * overridden to provide gradient painting
401: *
402: * TODO: Chris says that in OGL we actually suffer here by caching the
403: * gradient paint since the OGL pipeline will render gradients on
404: * hardware for us. The decision to use cacheing is based on my experience
405: * with gradient title borders slowing down repaints -- this could use more
406: * extensive analysis.
407: */
408: @Override
409: protected void paintComponent(Graphics g) {
410: if (backgroundPainter != null) {
411: backgroundPainter.paint((Graphics2D) g, this );
412: } else {
413: super .paintComponent(g);
414: if (drawGradient) {
415: Insets insets = getInsets();
416: int width = getWidth() - insets.right - insets.left;
417: int height = getHeight() - insets.top - insets.bottom;
418:
419: //TODO need to detect a change in gradient paint as well
420: if (gradientPaint == null || oldSize == null
421: || oldSize.width != width
422: || oldSize.height != height) {
423: Color c1 = null;//UIManager.getColor("control");
424: Color c2 = null;//c.darker();
425: if (gradientPaint == null) {
426: c1 = getBackground();
427: c2 = new Color(c1.getRed() - 40,
428: c1.getGreen() - 40, c1.getBlue() - 40);
429: float x1 = 0f;
430: float y1 = 0f;
431: float x2 = width;
432: float y2 = 0;
433: boolean cyclic = false;
434: gradientPaint = new GradientPaint(x1, y1, c1,
435: x2, y2, c2, cyclic);
436: } else {
437: //same GP as before, but where the values differed for x1, x2, replace
438: //x2 with the current width, and where values differed for y1, y2
439: //replace with current height
440: GradientPaint gp = gradientPaint;
441: float x2 = (float) gp.getPoint2().getX();
442: if (gradientTrackWidth) {
443: float ratio = (float) width
444: / (float) oldSize.width;
445: x2 = ((float) gp.getPoint2().getX())
446: * ratio;
447: }
448: float y2 = (float) gp.getPoint2().getY();
449: if (gradientTrackHeight) {
450: float ratio = (float) height
451: / (float) oldSize.height;
452: y2 = ((float) gp.getPoint2().getY())
453: * ratio;
454: }
455: gradientPaint = new GradientPaint((float) gp
456: .getPoint1().getX(), (float) gp
457: .getPoint1().getY(), gp.getColor1(),
458: x2, y2, gp.getColor2(), gp.isCyclic());
459: }
460:
461: oldSize = new Dimension(width, height);
462: cachedGradient = new BufferedImage(width, height,
463: BufferedImage.TYPE_INT_ARGB);
464: Graphics2D imgg = (Graphics2D) cachedGradient
465: .getGraphics();
466: imgg.setPaint(gradientPaint);
467: imgg.fillRect(0, 0, width, height);
468: }
469:
470: // draw the image
471: Graphics2D g2 = (Graphics2D) g;
472: g2.drawImage(cachedGradient, null, insets.left,
473: insets.top);
474: }
475: }
476: }
477: }
|