001 /* 002 * $Id: JXPanel.java 3341 2009-05-22 19:43:05Z kschaefe $ 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.Graphics; 030 import java.awt.Graphics2D; 031 import java.awt.Insets; 032 import java.awt.LayoutManager; 033 import java.awt.Rectangle; 034 035 import javax.swing.JPanel; 036 import javax.swing.RepaintManager; 037 import javax.swing.Scrollable; 038 import javax.swing.plaf.UIResource; 039 040 import org.jdesktop.swingx.painter.Painter; 041 042 /** 043 * <p> 044 * An extended {@code JPanel} that provides additional features. First, the 045 * component is {@code Scrollable}, using reasonable defaults. Second, the 046 * component is alpha-channel enabled. This means that the {@code JXPanel} can 047 * be made fully or partially transparent. Finally, {@code JXPanel} has support 048 * for {@linkplain Painter painters}. 049 * </p> 050 * <p> 051 * A transparency example, this following code will show the black background of 052 * the parent: 053 * 054 * <pre> 055 * JXPanel panel = new JXPanel(); 056 * panel.add(new JButton("Push Me")); 057 * panel.setAlpha(.5f); 058 * 059 * container.setBackground(Color.BLACK); 060 * container.add(panel); 061 * </pre> 062 * 063 * </p> 064 * <p> 065 * A painter example, this following code will show how to add a simple painter: 066 * 067 * <pre> 068 * JXPanel panel = new JXPanel(); 069 * panel.setBackgroundPainter(new PinstripePainter()); 070 * </pre> 071 * 072 * </p> 073 * 074 * @author rbair 075 * @see Scrollable 076 * @see Painter 077 */ 078 public class JXPanel extends JPanel implements Scrollable { 079 private boolean scrollableTracksViewportHeight = true; 080 private boolean scrollableTracksViewportWidth = true; 081 082 /** 083 * The alpha level for this component. 084 */ 085 private float alpha = 1.0f; 086 /** 087 * If the old alpha value was 1.0, I keep track of the opaque setting because 088 * a translucent component is not opaque, but I want to be able to restore 089 * opacity to its default setting if the alpha is 1.0. Honestly, I don't know 090 * if this is necessary or not, but it sounded good on paper :) 091 * <p>TODO: Check whether this variable is necessary or not</p> 092 */ 093 private boolean oldOpaque; 094 /** 095 * Indicates whether this component should inherit its parent alpha value 096 */ 097 private boolean inheritAlpha = true; 098 /** 099 * Specifies the Painter to use for painting the background of this panel. 100 * If no painter is specified, the normal painting routine for JPanel 101 * is called. Old behavior is also honored for the time being if no 102 * backgroundPainter is specified 103 */ 104 private Painter backgroundPainter; 105 106 /** 107 * Creates a new <code>JXPanel</code> with a double buffer 108 * and a flow layout. 109 */ 110 public JXPanel() { 111 } 112 113 /** 114 * Creates a new <code>JXPanel</code> with <code>FlowLayout</code> 115 * and the specified buffering strategy. 116 * If <code>isDoubleBuffered</code> is true, the <code>JXPanel</code> 117 * will use a double buffer. 118 * 119 * @param isDoubleBuffered a boolean, true for double-buffering, which 120 * uses additional memory space to achieve fast, flicker-free 121 * updates 122 */ 123 public JXPanel(boolean isDoubleBuffered) { 124 super(isDoubleBuffered); 125 } 126 127 /** 128 * Create a new buffered JXPanel with the specified layout manager 129 * 130 * @param layout the LayoutManager to use 131 */ 132 public JXPanel(LayoutManager layout) { 133 super(layout); 134 } 135 136 /** 137 * Creates a new JXPanel with the specified layout manager and buffering 138 * strategy. 139 * 140 * @param layout the LayoutManager to use 141 * @param isDoubleBuffered a boolean, true for double-buffering, which 142 * uses additional memory space to achieve fast, flicker-free 143 * updates 144 */ 145 public JXPanel(LayoutManager layout, boolean isDoubleBuffered) { 146 super(layout, isDoubleBuffered); 147 } 148 149 /** 150 * Set the alpha transparency level for this component. This automatically 151 * causes a repaint of the component. 152 * 153 * <p>TODO add support for animated changes in translucency</p> 154 * 155 * @param alpha must be a value between 0 and 1 inclusive. 156 */ 157 public void setAlpha(float alpha) { 158 if (this.alpha != alpha) { 159 assert alpha >= 0 && alpha <= 1.0; 160 float oldAlpha = this.alpha; 161 this.alpha = alpha; 162 if (alpha > 0f && alpha < 1f) { 163 if (oldAlpha == 1) { 164 //it used to be 1, but now is not. Save the oldOpaque 165 oldOpaque = isOpaque(); 166 setOpaque(false); 167 } 168 169 RepaintManager manager = RepaintManager.currentManager(this); 170 RepaintManager trm = SwingXUtilities.getTranslucentRepaintManager(manager); 171 RepaintManager.setCurrentManager(trm); 172 } else if (alpha == 1) { 173 //restore the oldOpaque if it was true (since opaque is false now) 174 if (oldOpaque) { 175 setOpaque(true); 176 } 177 } 178 firePropertyChange("alpha", oldAlpha, alpha); 179 repaint(); 180 } 181 } 182 183 /** 184 * @return the alpha translucency level for this component. This will be 185 * a value between 0 and 1, inclusive. 186 */ 187 public float getAlpha() { 188 return alpha; 189 } 190 191 /** 192 * Unlike other properties, alpha can be set on a component, or on one of 193 * its parents. If the alpha of a parent component is .4, and the alpha on 194 * this component is .5, effectively the alpha for this component is .4 195 * because the lowest alpha in the heirarchy "wins" 196 */ 197 public float getEffectiveAlpha() { 198 if (inheritAlpha) { 199 float a = alpha; 200 Component c = this; 201 while ((c = c.getParent()) != null) { 202 if (c instanceof JXPanel) { 203 a = Math.min(((JXPanel)c).getAlpha(), a); 204 } 205 } 206 return a; 207 } else { 208 return alpha; 209 } 210 } 211 212 /** 213 * Returns the state of the panel with respect to inheriting alpha values. 214 * 215 * @return {@code true} if this panel inherits alpha values; {@code false} 216 * otherwise 217 * @see JXPanel#setInheritAlpha(boolean) 218 */ 219 public boolean isInheritAlpha() { 220 return inheritAlpha; 221 } 222 223 /** 224 * Determines if the effective alpha of this component should include the 225 * alpha of ancestors. 226 * 227 * @param val 228 * {@code true} to include ancestral alpha data; {@code false} 229 * otherwise 230 * @see #isInheritAlpha() 231 * @see #getEffectiveAlpha() 232 */ 233 public void setInheritAlpha(boolean val) { 234 if (inheritAlpha != val) { 235 inheritAlpha = val; 236 firePropertyChange("inheritAlpha", !inheritAlpha, inheritAlpha); 237 } 238 } 239 240 /** 241 * {@inheritDoc} 242 */ 243 public boolean getScrollableTracksViewportHeight() { 244 return scrollableTracksViewportHeight; 245 } 246 247 /** 248 * {@inheritDoc} 249 */ 250 public boolean getScrollableTracksViewportWidth() { 251 return scrollableTracksViewportWidth; 252 } 253 254 /** 255 * {@inheritDoc} 256 */ 257 public Dimension getPreferredScrollableViewportSize() { 258 return getPreferredSize(); 259 } 260 261 /** 262 * {@inheritDoc} 263 */ 264 public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { 265 return 10; 266 } 267 268 /** 269 * {@inheritDoc} 270 */ 271 public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { 272 return 10; 273 } 274 /** 275 * @param scrollableTracksViewportHeight The scrollableTracksViewportHeight to set. 276 */ 277 public void setScrollableTracksViewportHeight(boolean scrollableTracksViewportHeight) { 278 this.scrollableTracksViewportHeight = scrollableTracksViewportHeight; 279 } 280 /** 281 * @param scrollableTracksViewportWidth The scrollableTracksViewportWidth to set. 282 */ 283 public void setScrollableTracksViewportWidth(boolean scrollableTracksViewportWidth) { 284 this.scrollableTracksViewportWidth = scrollableTracksViewportWidth; 285 } 286 287 /** 288 * Sets the background color for this component by 289 * 290 * @param bg 291 * the desired background <code>Color</code> 292 * @see java.swing.JComponent#getBackground 293 * @see #setOpaque 294 * 295 * @beaninfo 296 * preferred: true 297 * bound: true 298 * attribute: visualUpdate true 299 * description: The background color of the component. 300 */ 301 @Override 302 public void setBackground(Color bg) { 303 super.setBackground(bg); 304 305 //TODO problem with SwingX #964. Had to undo changes. 306 //Change causes background to be painter when using setOpaque=false hack 307 // if (canInstallBackgroundUIResourceAsPainter(bg)) { 308 // setBackgroundPainter(new PainterUIResource(new MattePainter<JXPanel>(bg))); 309 // } else { 310 // setBackgroundPainter(new MattePainter<JXPanel>(bg)); 311 // } 312 } 313 314 private boolean canInstallBackgroundUIResourceAsPainter(Color bg) { 315 Painter<?> p = getBackgroundPainter(); 316 317 return bg instanceof UIResource && (p == null || p instanceof UIResource); 318 } 319 320 /** 321 * Sets a Painter to use to paint the background of this JXPanel. 322 * 323 * @param p the new painter 324 * @see #getBackgroundPainter() 325 */ 326 public void setBackgroundPainter(Painter p) { 327 Painter old = getBackgroundPainter(); 328 backgroundPainter = p; 329 firePropertyChange("backgroundPainter", old, getBackgroundPainter()); 330 repaint(); 331 } 332 333 /** 334 * Returns the current background painter. The default value of this property 335 * is a painter which draws the normal JPanel background according to the current look and feel. 336 * @return the current painter 337 * @see #setBackgroundPainter(Painter) 338 * @see #isPaintBorderInsets() 339 */ 340 public Painter getBackgroundPainter() { 341 return backgroundPainter; 342 } 343 344 345 private boolean paintBorderInsets = true; 346 347 /** 348 * Returns true if the background painter should paint where the border is 349 * or false if it should only paint inside the border. This property is 350 * true by default. This property affects the width, height, 351 * and intial transform passed to the background painter. 352 */ 353 public boolean isPaintBorderInsets() { 354 return paintBorderInsets; 355 } 356 357 /** 358 * Sets the paintBorderInsets property. 359 * Set to true if the background painter should paint where the border is 360 * or false if it should only paint inside the border. This property is true by default. 361 * This property affects the width, height, 362 * and intial transform passed to the background painter. 363 * 364 * This is a bound property. 365 */ 366 public void setPaintBorderInsets(boolean paintBorderInsets) { 367 boolean old = this.isPaintBorderInsets(); 368 this.paintBorderInsets = paintBorderInsets; 369 firePropertyChange("paintBorderInsets", old, isPaintBorderInsets()); 370 } 371 372 /** 373 * Overriden paint method to take into account the alpha setting 374 * @param g 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 Painter support. It will call backgroundPainter.paint() 389 * if it is not null, else it will call super.paintComponent(). 390 */ 391 @Override 392 protected void paintComponent(Graphics g) { 393 if(backgroundPainter != null) { 394 if (isOpaque()) super.paintComponent(g); 395 396 Graphics2D g2 = (Graphics2D)g.create(); 397 398 try { 399 // account for the insets 400 if(isPaintBorderInsets()) { 401 backgroundPainter.paint(g2, this, this.getWidth(), this.getHeight()); 402 } else { 403 Insets ins = this.getInsets(); 404 g2.translate(ins.left, ins.top); 405 backgroundPainter.paint(g2, this, 406 this.getWidth() - ins.left - ins.right, 407 this.getHeight() - ins.top - ins.bottom); 408 } 409 } finally { 410 g2.dispose(); 411 } 412 } else { 413 super.paintComponent(g); 414 } 415 } 416 417 }