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 }