KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > jgoodies > looks > common > ShadowPopup


1 /*
2  * Copyright (c) 2005 JGoodies Karsten Lentzsch. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * o Redistributions of source code must retain the above copyright notice,
8  * this list of conditions and the following disclaimer.
9  *
10  * o Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  *
14  * o Neither the name of JGoodies Karsten Lentzsch nor the names of
15  * its contributors may be used to endorse or promote products derived
16  * from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
22  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */

30
31 package com.jgoodies.looks.common;
32
33 import java.awt.*;
34 import java.awt.image.BufferedImage JavaDoc;
35 import java.util.ArrayList JavaDoc;
36 import java.util.List JavaDoc;
37
38 import javax.swing.*;
39 import javax.swing.border.Border JavaDoc;
40
41 /**
42  * Does all the magic for getting popups with drop shadows.
43  * It adds the drop shadow border to the Popup,
44  * in <code>#show</code> it snapshots the screen background as needed,
45  * and in <code>#hide</code> it cleans up all changes made before.
46  *
47  * @author Andrej Golovnin
48  * @version $Revision: 1.2 $
49  *
50  * @see com.jgoodies.looks.common.ShadowPopupBorder
51  * @see com.jgoodies.looks.common.ShadowPopupFactory
52  */

53 public final class ShadowPopup extends Popup {
54
55     /**
56      * Max number of items to store in the cache.
57      */

58     private static final int MAX_CACHE_SIZE = 5;
59
60     /**
61      * The cache to use for ShadowPopups.
62      */

63     private static List JavaDoc cache;
64
65     /**
66      * The singleton instance used to draw all borders.
67      */

68     private static final Border JavaDoc SHADOW_BORDER = ShadowPopupBorder.getInstance();
69
70     /**
71      * The size of the drop shadow.
72      */

73     private static final int SHADOW_SIZE = 5;
74
75     /**
76      * Indicates whether we can make snapshots from screen or not.
77      */

78     private static boolean canSnapshot = true;
79
80     /**
81      * The component mouse coordinates are relative to, may be null.
82      */

83     private Component owner;
84
85     /**
86      * The contents of the popup.
87      */

88     private Component contents;
89
90     /**
91      * The desired x and y location of the popup.
92      */

93     private int x, y;
94
95     /**
96      * The real popup. The #show() and #hide() methods will delegate
97      * all calls to these popup.
98      */

99     private Popup popup;
100
101     /**
102      * The border of the contents' parent replaced by SHADOW_BORDER.
103      */

104     private Border JavaDoc oldBorder;
105
106     /**
107      * The old value of the opaque property of the contents' parent.
108      */

109     private boolean oldOpaque;
110
111     /**
112      * The heavy weight container of the popup contents, may be null.
113      */

114     private Container heavyWeightContainer;
115
116     /**
117      * Returns a previously used <code>ShadowPopup</code>, or a new one
118      * if none of the popups have been recycled.
119      */

120     static Popup getInstance(Component owner, Component contents, int x,
121             int y, Popup delegate) {
122         ShadowPopup result;
123         synchronized (ShadowPopup.class) {
124             if (cache == null) {
125                 cache = new ArrayList JavaDoc(MAX_CACHE_SIZE);
126             }
127             if (cache.size() > 0) {
128                 result = (ShadowPopup) cache.remove(0);
129             } else {
130                 result = new ShadowPopup();
131             }
132         }
133         result.reset(owner, contents, x, y, delegate);
134         return result;
135     }
136
137     /**
138      * Recycles the ShadowPopup.
139      */

140     private static void recycle(ShadowPopup popup) {
141         synchronized (ShadowPopup.class) {
142             if (cache.size() < MAX_CACHE_SIZE) {
143                 cache.add(popup);
144             }
145         }
146     }
147     
148     public static boolean canSnapshot() {
149         return canSnapshot;
150     }
151
152     /** {@inheritDoc} */
153     public void hide() {
154         JComponent parent = (JComponent) contents.getParent();
155         popup.hide();
156         if (parent.getBorder() == SHADOW_BORDER) {
157             parent.setBorder(oldBorder);
158             parent.setOpaque(oldOpaque);
159             oldBorder = null;
160             if (heavyWeightContainer != null) {
161                 parent.putClientProperty(ShadowPopupFactory.PROP_HORIZONTAL_BACKGROUND, null);
162                 parent.putClientProperty(ShadowPopupFactory.PROP_VERTICAL_BACKGROUND, null);
163                 heavyWeightContainer = null;
164             }
165         }
166         owner = null;
167         contents = null;
168         popup = null;
169         recycle(this);
170     }
171
172     /** {@inheritDoc} */
173     public void show() {
174         if (heavyWeightContainer != null) {
175             snapshot();
176         }
177         popup.show();
178     }
179
180     /**
181      * Reinitializes this ShadowPopup using the given parameters.
182      *
183      * @param owner component mouse coordinates are relative to, may be null
184      * @param contents the contents of the popup
185      * @param x the desired x location of the popup
186      * @param y the desired y location of the popup
187      * @param popup the popup to wrap
188      */

189     private void reset(Component owner, Component contents, int x, int y,
190             Popup popup) {
191         this.owner = owner;
192         this.contents = contents;
193         this.popup = popup;
194         this.x = x;
195         this.y = y;
196         if (owner instanceof JComboBox) {
197             return;
198         }
199         Container mediumWeightContainer = null;
200         for(Container p = contents.getParent(); p != null; p = p.getParent()) {
201             if (p instanceof JWindow) {
202                 // Workaround for the gray rect problem.
203
p.setBackground(contents.getBackground());
204                 heavyWeightContainer = p;
205                 break;
206             } else if (p instanceof Panel) {
207                 // It is medium weight. Setting background
208
// to a transparent color makes it transparent and
209
// we don't need to capture the screen background.
210
Color bg = p.getBackground();
211                 int rgba = contents.getBackground().getRGB() & 0x00ffffff;
212                 if ((bg == null) || (bg.getRGB() != rgba)) {
213                     p.setBackground(new Color(rgba, true));
214                 }
215                 mediumWeightContainer = p;
216                 break;
217             }
218         }
219         JComponent parent = (JComponent) contents.getParent();
220         oldOpaque = parent.isOpaque();
221         oldBorder = parent.getBorder();
222         parent.setOpaque(false);
223         parent.setBorder(SHADOW_BORDER);
224         // Pack it because we have changed the border.
225
if (mediumWeightContainer != null) {
226             mediumWeightContainer.setSize(
227                     mediumWeightContainer.getPreferredSize());
228         } else {
229             parent.setSize(parent.getPreferredSize());
230         }
231     }
232
233     /**
234      * The 'scratch pad' objects used to calculate dirty regions of
235      * the screen snapshots.
236      *
237      * @see #snapshot()
238      */

239     private static final Point point = new Point();
240     private static final Rectangle rect = new Rectangle();
241
242     /**
243      * Snapshots the background. The snapshots are stored as client
244      * properties of the contents' parent. The next time the border is drawn,
245      * this background will be used.<p>
246      *
247      * Uses a robot on the default screen device to capture the screen
248      * region under the drop shadow. Does <em>not</em> use the window's
249      * device, because that may be an outdated device (due to popup reuse)
250      * and the robot's origin seems to be adjusted with the default screen
251      * device.
252      *
253      * @see #show()
254      * @see com.jgoodies.looks.common.ShadowPopupBorder
255      */

256     private void snapshot() {
257         try {
258             Robot robot = new Robot(); // uses the default screen device
259

260             Dimension size = heavyWeightContainer.getPreferredSize();
261             int width = size.width;
262             int height = size.height;
263
264             rect.setBounds(x, y + height - SHADOW_SIZE, width, SHADOW_SIZE);
265             BufferedImage JavaDoc hShadowBg = robot.createScreenCapture(rect);
266
267             rect.setBounds(x + width - SHADOW_SIZE, y, SHADOW_SIZE,
268                     height - SHADOW_SIZE);
269             BufferedImage JavaDoc vShadowBg = robot.createScreenCapture(rect);
270
271             JComponent parent = (JComponent) contents.getParent();
272             parent.putClientProperty(ShadowPopupFactory.PROP_HORIZONTAL_BACKGROUND, hShadowBg);
273             parent.putClientProperty(ShadowPopupFactory.PROP_VERTICAL_BACKGROUND, vShadowBg);
274
275             JComponent layeredPane = getLayeredPane();
276             if (layeredPane == null) {
277                 // This could happen if owner is null.
278
return;
279             }
280
281             int layeredPaneWidth = layeredPane.getWidth();
282             int layeredPaneHeight = layeredPane.getHeight();
283
284             point.x = x;
285             point.y = y;
286             SwingUtilities.convertPointFromScreen(point, layeredPane);
287
288             // If needed paint dirty region of the horizontal snapshot.
289
rect.x = point.x;
290             rect.y = point.y + height - SHADOW_SIZE;
291             rect.width = width;
292             rect.height = SHADOW_SIZE;
293
294             if ((rect.x + rect.width) > layeredPaneWidth) {
295                 rect.width = layeredPaneWidth - rect.x;
296             }
297             if ((rect.y + rect.height) > layeredPaneHeight) {
298                 rect.height = layeredPaneHeight - rect.y;
299             }
300             if (!rect.isEmpty()) {
301                 Graphics g = hShadowBg.createGraphics();
302                 g.translate(-rect.x, -rect.y);
303                 g.setClip(rect);
304                 boolean doubleBuffered = layeredPane.isDoubleBuffered();
305                 layeredPane.setDoubleBuffered(false);
306                 layeredPane.paint(g);
307                 layeredPane.setDoubleBuffered(doubleBuffered);
308                 g.dispose();
309             }
310
311             // If needed paint dirty region of the vertical snapshot.
312
rect.x = point.x + width - SHADOW_SIZE;
313             rect.y = point.y;
314             rect.width = SHADOW_SIZE;
315             rect.height = height - SHADOW_SIZE;
316
317             if ((rect.x + rect.width) > layeredPaneWidth) {
318                 rect.width = layeredPaneWidth - rect.x;
319             }
320             if ((rect.y + rect.height) > layeredPaneHeight) {
321                 rect.height = layeredPaneHeight - rect.y;
322             }
323             if (!rect.isEmpty()) {
324                 Graphics g = vShadowBg.createGraphics();
325                 g.translate(-rect.x, -rect.y);
326                 g.setClip(rect);
327                 boolean doubleBuffered = layeredPane.isDoubleBuffered();
328                 layeredPane.setDoubleBuffered(false);
329                 layeredPane.paint(g);
330                 layeredPane.setDoubleBuffered(doubleBuffered);
331                 g.dispose();
332             }
333         } catch (AWTException e) {
334             canSnapshot = false;
335         } catch (SecurityException JavaDoc e) {
336             canSnapshot = false;
337         }
338     }
339
340     /**
341      * @return the top level layered pane which contains the owner.
342      */

343     private JComponent getLayeredPane() {
344         // The code below is copied from PopupFactory#LightWeightPopup#show()
345
Container parent = null;
346         if (owner != null) {
347             parent = owner instanceof Container
348                     ? (Container) owner
349                     : owner.getParent();
350         }
351         // Try to find a JLayeredPane and Window to add
352
for (Container p = parent; p != null; p = p.getParent()) {
353             if (p instanceof JRootPane) {
354                 if (p.getParent() instanceof JInternalFrame) {
355                     continue;
356                 }
357                 parent = ((JRootPane)p).getLayeredPane();
358                 // Continue, so that if there is a higher JRootPane, we'll
359
// pick it up.
360
} else if(p instanceof Window) {
361                 if (parent == null) {
362                     parent = p;
363                 }
364                 break;
365             } else if (p instanceof JApplet) {
366                 // Painting code stops at Applets, we don't want
367
// to add to a Component above an Applet otherwise
368
// you'll never see it painted.
369
break;
370             }
371         }
372         return (JComponent) parent;
373     }
374
375 }
Popular Tags