KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > awt > JPopupMenuUtils


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19 package org.openide.awt;
20
21 import org.openide.util.RequestProcessor;
22 import org.openide.util.Utilities;
23
24 import java.awt.*;
25
26 import java.util.StringTokenizer JavaDoc;
27
28 import javax.swing.JMenu JavaDoc;
29 import javax.swing.JPopupMenu JavaDoc;
30 import javax.swing.SwingUtilities JavaDoc;
31
32
33 /** A class that contains a set of utility classes and methods
34  * around displaying and positioning popup menus.
35  *
36  * Popup menus sometimes need to be repositioned so that they
37  * don't "fall off" the edges of the screen.
38  *
39  * Some of the menus have items that are added dynamically, that is,
40  * after the menu is displayed. These menus are often placed correctly
41  * for their initial size, but will need to be repositioned as they
42  * grow.
43  *
44  * @author Evan Adams
45  */

46 public class JPopupMenuUtils {
47     private static boolean problemTested = false;
48     private static boolean problem = false;
49     private static RequestProcessor reqProc;
50     private static RequestProcessor.Task task;
51 // private static final boolean NO_POPUP_PLACEMENT_HACK = Boolean.getBoolean("netbeans.popup.no_hack"); // NOI18N
52

53     /*
54      * Called when a visible menu has dynamically changed. Ensure that
55      * it stays on the screen. Compute its new location and,
56      * if it differs from the current one, move the popup.
57      *
58      * @param popup the popup menu
59      */

60     public static void dynamicChange(final JPopupMenu JavaDoc popup, boolean usedToBeContained) {
61         if (!popup.isShowing()) {
62             return;
63         }
64
65         if (isProblemConfig()) {
66             callRefreshLater(popup);
67
68             return;
69         }
70
71         refreshPopup(popup);
72
73         Point p = popup.getLocationOnScreen();
74         Point newPt = getPopupMenuOrigin(popup, p);
75
76         boolean willBeContained = willPopupBeContained(popup, newPt);
77
78         if (usedToBeContained != willBeContained) {
79             popup.setVisible(false);
80         }
81
82         if (!newPt.equals(p)) {
83 // if (!NO_POPUP_PLACEMENT_HACK) {
84
// popup.setLocation(newPt.x, newPt.y);
85
// }
86
}
87
88         if (usedToBeContained != willBeContained) {
89             popup.setVisible(true);
90         }
91     }
92
93     /** Mysterious calls to pack(), invalidate() and validate() ;-) */
94     private static void refreshPopup(JPopupMenu JavaDoc popup) {
95         popup.pack();
96         popup.invalidate();
97
98         Component c = popup.getParent();
99
100         if (c != null) {
101             c.validate();
102         }
103     }
104
105     /** Called from dynamicChange. Performs refresh of the popup
106      * in task in reqProc.
107      */

108     private static void callRefreshLater(final JPopupMenu JavaDoc popup) {
109         // this may cause the popup to flicker
110
if (reqProc == null) {
111             reqProc = new RequestProcessor();
112         }
113
114         if (task == null) {
115             task = reqProc.create(
116                     new Runnable JavaDoc() {
117                         public void run() {
118                             SwingUtilities.invokeLater(
119                                 new Runnable JavaDoc() {
120                                     public void run() {
121                                         task = null;
122
123                                         // after the action is performed new task should be created
124
// (probably for another instance of a popup)
125
if (!popup.isShowing()) {
126                                             return;
127                                         }
128
129                                         Point p = popup.getLocationOnScreen();
130                                         Point newPt = getPopupMenuOrigin(popup, p);
131                                         popup.setVisible(false);
132                                         refreshPopup(popup);
133
134                                         if (!newPt.equals(p)) {
135 // if (!NO_POPUP_PLACEMENT_HACK) {
136
// popup.setLocation(newPt.x, newPt.y);
137
// }
138
}
139
140                                         popup.setVisible(true);
141                                     }
142                                 }
143                             );
144                         }
145                     }
146                 );
147         }
148
149         task.schedule(100);
150     }
151
152     /** Returns true when the popup has to be unconditioanlly
153      * redisplayed when adding new items. Currently
154      * this is true with JDK1.3 on Linux/Gnome. This
155      * method checks for presence of system property
156      * "netbeans.popup.linuxhack".
157      */

158     private static boolean isProblemConfig() {
159         // we have already tested the need for the hack
160
if (problemTested) {
161             return problem;
162         }
163
164         problem = false;
165
166         String JavaDoc needHack = System.getProperty("netbeans.popup.linuxhack");
167
168         if (needHack != null) {
169             problem = true;
170         }
171
172         return problem;
173     }
174
175     /*
176      * Called when a visible submenu (pullright) has dynamically changed.
177      * Ensure that it stays on the screen. If it doesn't fit, then hide
178      * the popup and redisplay it. This causes JMenu's placement code
179      * to get executed again which may change the submens to go up rather
180      * than down.
181      *
182      * @param popup the popup menu
183      */

184     public static void dynamicChangeToSubmenu(JPopupMenu JavaDoc popup, boolean usedToBeContained) {
185         Object JavaDoc invoker = popup.getInvoker();
186
187         if (!(invoker instanceof JMenu JavaDoc)) {
188             return;
189         }
190
191         JMenu JavaDoc menu = (JMenu JavaDoc) invoker;
192
193         if (!popup.isShowing()) {
194             return;
195         }
196
197         if (isProblemConfig()) {
198             callRefreshLater2(popup, menu);
199
200             return;
201         }
202
203         refreshPopup(popup);
204
205         Point p = popup.getLocationOnScreen();
206         Dimension popupSize = popup.getPreferredSize();
207         Rectangle popupRect = new Rectangle(p, popupSize);
208         Rectangle screenRect = getScreenRect();
209         boolean willBeContained = isPopupContained(popup);
210
211         if (!screenRect.contains(popupRect)) {
212             /*
213              * The menu grew off the edge of the screen.
214              */

215             menu.setPopupMenuVisible(false);
216             menu.setPopupMenuVisible(true);
217         } else if (usedToBeContained != willBeContained) {
218             /*
219              * The menu grew off the edge of the containing window.
220              * Use the setVisible() hack to change the menu from
221              * lightweight to heavyweight.
222              */

223             popup.setVisible(false);
224             popup.setVisible(true);
225         }
226     }
227
228     /** Called from dynamicChangeToSubmenu. Calls the popup refresh
229      * in a task in the reqProc.
230      */

231     private static void callRefreshLater2(final JPopupMenu JavaDoc popup, final JMenu JavaDoc menu) {
232         // this may cause the popup to flicker
233
if (reqProc == null) {
234             reqProc = new RequestProcessor();
235         }
236
237         if (task == null) {
238             task = reqProc.create(
239                     new Runnable JavaDoc() {
240                         public void run() {
241                             SwingUtilities.invokeLater(
242                                 new Runnable JavaDoc() {
243                                     public void run() {
244                                         task = null;
245
246                                         // after the action is performed new task should be created
247
// (probably for another instance of a popup)
248
if (!popup.isShowing()) {
249                                             return;
250                                         }
251
252                                         popup.setVisible(false);
253                                         refreshPopup(popup);
254                                         popup.setVisible(true);
255
256                                         Point p = popup.getLocationOnScreen();
257                                         Dimension popupSize = popup.getPreferredSize();
258                                         Rectangle popupRect = new Rectangle(p, popupSize);
259                                         Rectangle screenRect = getScreenRect();
260
261                                         if (!screenRect.contains(popupRect)) {
262                                             menu.setPopupMenuVisible(false);
263                                             menu.setPopupMenuVisible(true);
264                                         }
265                                     }
266                                 }
267                             );
268                         }
269                     }
270                 );
271         }
272
273         task.schedule(100);
274     }
275
276     /*
277      * Return the point for the origin of this popup.
278      * This is where the adjustments are made to ensure the
279      * popup stays on the screen.
280      *
281      * @param popup the popup menu
282      * @param p the popup menu's origin
283      * @return the popup menu's new origin
284      */

285     static Point getPopupMenuOrigin(JPopupMenu JavaDoc popup, Point p) {
286         Point newPt = new Point(p);
287         Dimension popupSize = popup.getPreferredSize();
288         Rectangle screenRect = getScreenRect();
289         int popupRight = newPt.x + popupSize.width;
290         int popupBottom = newPt.y + popupSize.height;
291         int screenRight = screenRect.x + screenRect.width;
292         int screenBottom = screenRect.y + screenRect.height;
293
294         if (popupRight > screenRight) { // Are we off the right edge?
295
newPt.x = screenRight - popupSize.width;
296         }
297
298         if (newPt.x < screenRect.x) { // Are we off the left edge?
299
newPt.x = screenRect.x;
300         }
301
302         if (popupBottom > screenBottom) { // Are we off the bottom edge?
303
newPt.y = screenBottom - popupSize.height;
304         }
305
306         if (newPt.y < screenRect.y) { // Are we off the top edge?
307
newPt.y = screenRect.y;
308         }
309
310         return newPt;
311     }
312
313     /*
314      * Return whether or not the given popup is contained by its
315      * parent window. Uses the current location and size of the popup.
316      *
317      * @return boolean indicating if the popup is contained
318      */

319     public static boolean isPopupContained(JPopupMenu JavaDoc popup) {
320         if (!popup.isShowing()) {
321             return false;
322         }
323
324         return willPopupBeContained(popup, popup.getLocationOnScreen());
325     }
326
327     /*
328      * Return whether or not the given popup will be contained by
329      * its parent window if it is moved to <code>origin</origin>.
330      * Use its current size.
331      *
332      * @param <code>popup</code> the popup to be tested
333      * @param <code>origin</code> location of the popup to be tested
334      * @return boolean indicating if the popup will be contained
335      */

336     private static boolean willPopupBeContained(JPopupMenu JavaDoc popup, Point origin) {
337         if (!popup.isShowing()) {
338             return false;
339         }
340
341         Window w = SwingUtilities.windowForComponent(popup.getInvoker());
342         Rectangle r = new Rectangle(origin, popup.getSize());
343
344         return (w != null) && w.getBounds().contains(r);
345     }
346
347     /*
348      * Return a rectange defining the usable portion of the screen. Originally
349      * designed to provide a way to account for the taskbar in Windows. Didn't
350      * work with multiple monitor configuration. The new implementation
351      * detects the current monitor and returns its bounds. Never cache the
352      * result of this method.
353      *
354      * @return a rectangle defining the usable area.
355      */

356     public static Rectangle getScreenRect() {
357         return Utilities.getUsableScreenBounds();
358     }
359 }
360
Popular Tags