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 }