KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > editor > completion > CompletionLayoutPopup


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
20 package org.netbeans.modules.editor.completion;
21
22 import java.awt.Dimension JavaDoc;
23 import java.awt.GraphicsConfiguration JavaDoc;
24 import java.awt.Point JavaDoc;
25 import java.awt.Rectangle JavaDoc;
26 import java.awt.event.KeyEvent JavaDoc;
27 import javax.swing.JComponent JavaDoc;
28 import javax.swing.Popup JavaDoc;
29 import javax.swing.PopupFactory JavaDoc;
30 import javax.swing.SwingUtilities JavaDoc;
31 import javax.swing.text.BadLocationException JavaDoc;
32 import javax.swing.text.JTextComponent JavaDoc;
33
34
35 /**
36  * Completion popup - either completion, documentation or tooltip
37  * popup implementations.
38  *
39  * @author Dusan Balek, Miloslav Metelka
40  */

41 abstract class CompletionLayoutPopup {
42     
43     private CompletionLayout layout;
44     
45     private Popup JavaDoc popup;
46     
47     /** Bounds at which the visible popup has. */
48     private Rectangle JavaDoc popupBounds;
49
50     private JComponent JavaDoc contentComponent;
51     
52     private int anchorOffset;
53     
54     private Rectangle JavaDoc anchorOffsetBounds;
55     
56     private boolean displayAboveCaret;
57     
58     private Rectangle JavaDoc screenBounds;
59     
60     private boolean preferDisplayAboveCaret;
61     
62     private boolean showRetainedPreferredSize;
63     
64     public final boolean isVisible() {
65         return (popup != null);
66     }
67     
68     public final boolean isActive() {
69         return (contentComponent != null);
70     }
71     
72     public final void hide() {
73         if (isVisible()) {
74             popup.hide();
75             popup = null;
76             popupBounds = null;
77             contentComponent = null;
78             anchorOffset = -1;
79             // Reset screen bounds as well to not cache too long
80
screenBounds = null;
81         }
82     }
83     
84     public final boolean isDisplayAboveCaret() {
85         return displayAboveCaret;
86     }
87     
88     public final Rectangle JavaDoc getPopupBounds() {
89         return popupBounds;
90     }
91     
92     final void setLayout(CompletionLayout layout) {
93         assert (layout != null);
94         this.layout = layout;
95     }
96     
97     final void setPreferDisplayAboveCaret(boolean preferDisplayAboveCaret) {
98         this.preferDisplayAboveCaret = preferDisplayAboveCaret;
99     }
100     
101     final void setContentComponent(JComponent JavaDoc contentComponent) {
102         assert (contentComponent != null);
103         this.contentComponent = contentComponent;
104     }
105     
106     final void setAnchorOffset(int anchorOffset) {
107         this.anchorOffset = anchorOffset;
108         anchorOffsetBounds = null;
109     }
110     
111     final Rectangle JavaDoc getScreenBounds() {
112         if (screenBounds == null) {
113         JTextComponent JavaDoc editorComponent = getEditorComponent();
114             GraphicsConfiguration JavaDoc configuration = editorComponent != null
115                     ? editorComponent.getGraphicsConfiguration() : null;
116             screenBounds = configuration != null
117                     ? configuration.getBounds() : new Rectangle JavaDoc();
118         }
119         return screenBounds;
120     }
121     
122     final int getAnchorOffset() {
123     int offset = anchorOffset;
124     if (offset == -1) {
125         // Get caret position
126
JTextComponent JavaDoc editorComponent = getEditorComponent();
127         if (editorComponent != null) {
128         offset = editorComponent.getSelectionStart();
129         }
130     }
131     return offset;
132     }
133     
134     final JComponent JavaDoc getContentComponent() {
135         return contentComponent;
136     }
137     
138     final Dimension JavaDoc getPreferredSize() {
139         JComponent JavaDoc comp = getContentComponent();
140         return (comp == null) ? new Dimension JavaDoc(0,0) : comp.getPreferredSize();
141     }
142     
143     final void resetPreferredSize() {
144         JComponent JavaDoc comp = getContentComponent();
145         if (comp == null){
146             return;
147         }
148         comp.setPreferredSize(null);
149     }
150     
151     final boolean isShowRetainedPreferredSize() {
152         return showRetainedPreferredSize;
153     }
154     
155     final CompletionLayout getLayout() {
156         return layout;
157     }
158     
159     final JTextComponent JavaDoc getEditorComponent() {
160         return layout.getEditorComponent();
161     }
162     
163     protected int getAnchorHorizontalShift() {
164         return 0;
165     }
166
167     final Rectangle JavaDoc getAnchorOffsetBounds() {
168     JTextComponent JavaDoc editorComponent = getEditorComponent();
169     if (editorComponent == null) {
170         return new Rectangle JavaDoc();
171     }
172         if (anchorOffsetBounds == null){
173             int anchorOffset = getAnchorOffset();
174             try {
175                 anchorOffsetBounds = editorComponent.modelToView(anchorOffset);
176                 if (anchorOffsetBounds != null){
177                     anchorOffsetBounds.x -= getAnchorHorizontalShift();
178                 } else {
179                     anchorOffsetBounds = new Rectangle JavaDoc(); // use empty rectangle
180
}
181             } catch (BadLocationException JavaDoc e) {
182                 anchorOffsetBounds = new Rectangle JavaDoc(); // use empty rectangle
183
}
184             Point JavaDoc anchorOffsetPoint = anchorOffsetBounds.getLocation();
185             SwingUtilities.convertPointToScreen(anchorOffsetPoint, editorComponent);
186             anchorOffsetBounds.setLocation(anchorOffsetPoint);
187         }
188         return anchorOffsetBounds;
189     }
190     
191     final Popup JavaDoc getPopup() {
192         return popup;
193     }
194     
195     /**
196      * Find bounds of the popup based on knowledge of the preferred size
197      * of the content component and the preference of the displaying
198      * of the popup either above or below the occupied bounds.
199      *
200      * @param occupiedBounds bounds of the rectangle above or below which
201      * the bounds should be found.
202      * @param aboveOccupiedBounds whether the bounds should be found for position
203      * above or below the occupied bounds.
204      * @return rectangle with absolute screen bounds of the popup.
205      */

206     private Rectangle JavaDoc findPopupBounds(Rectangle JavaDoc occupiedBounds, boolean aboveOccupiedBounds) {
207         Dimension JavaDoc prefSize = getPreferredSize();
208         Rectangle JavaDoc screen = getScreenBounds();
209         Rectangle JavaDoc popupBounds = new Rectangle JavaDoc();
210         
211         popupBounds.x = Math.min(occupiedBounds.x,
212                 (screen.x + screen.width) - prefSize.width);
213         popupBounds.x = Math.max(popupBounds.x, screen.x);
214         popupBounds.width = Math.min(prefSize.width, screen.width);
215         
216         if (aboveOccupiedBounds) {
217             popupBounds.height = Math.min(prefSize.height,
218                     occupiedBounds.y - screen.y - CompletionLayout.POPUP_VERTICAL_GAP);
219             popupBounds.y = occupiedBounds.y - CompletionLayout.POPUP_VERTICAL_GAP - popupBounds.height;
220         } else { // below caret
221
popupBounds.y = occupiedBounds.y
222                     + occupiedBounds.height + CompletionLayout.POPUP_VERTICAL_GAP;
223             popupBounds.height = Math.min(prefSize.height,
224                     (screen.y + screen.height) - popupBounds.y);
225         }
226         return popupBounds;
227     }
228     
229     /**
230      * Create and display the popup at the given bounds.
231      *
232      * @param popupBounds location and size of the popup.
233      * @param displayAboveCaret whether the popup is displayed above the anchor
234      * bounds or below them (it does not be right above them).
235      */

236     private void show(Rectangle JavaDoc popupBounds, boolean displayAboveCaret) {
237         // Hide the original popup if exists
238
if (popup != null) {
239             popup.hide();
240             popup = null;
241         }
242         
243         // Explicitly set the preferred size
244
Dimension JavaDoc origPrefSize = getPreferredSize();
245         Dimension JavaDoc newPrefSize = popupBounds.getSize();
246         JComponent JavaDoc contComp = getContentComponent();
247         if (contComp == null){
248             return;
249         }
250         contComp.setPreferredSize(newPrefSize);
251         showRetainedPreferredSize = newPrefSize.equals(origPrefSize);
252
253         PopupFactory JavaDoc factory = PopupFactory.getSharedInstance();
254         popup = factory.getPopup(layout.getEditorComponent(), contComp,
255                 popupBounds.x, popupBounds.y);
256         popup.show();
257
258         this.popupBounds = popupBounds;
259         this.displayAboveCaret = displayAboveCaret;
260     }
261     
262     /**
263      * Show the popup along the anchor bounds and take
264      * the preferred location (above or below caret) into account.
265      */

266     void showAlongAnchorBounds() {
267         showAlongOccupiedBounds(getAnchorOffsetBounds());
268     }
269     
270     void showAlongAnchorBounds(boolean aboveCaret) {
271         showAlongOccupiedBounds(getAnchorOffsetBounds(), aboveCaret);
272     }
273     
274     /**
275      * Show the popup along the anchor bounds and take
276      * the preferred location (above or below caret) into account.
277      */

278     void showAlongOccupiedBounds(Rectangle JavaDoc occupiedBounds) {
279         boolean aboveCaret;
280         if (isEnoughSpace(occupiedBounds, preferDisplayAboveCaret)) {
281             aboveCaret = preferDisplayAboveCaret;
282         } else { // not enough space at preferred location
283
// Choose the location with more space
284
aboveCaret = isMoreSpaceAbove(occupiedBounds);
285         }
286         Rectangle JavaDoc bounds = findPopupBounds(occupiedBounds, aboveCaret);
287         show(bounds, aboveCaret);
288     }
289     
290     void showAlongOccupiedBounds(Rectangle JavaDoc occupiedBounds, boolean aboveCaret) {
291         Rectangle JavaDoc bounds = findPopupBounds(occupiedBounds, aboveCaret);
292         show(bounds, aboveCaret);
293     }
294     
295     boolean isMoreSpaceAbove(Rectangle JavaDoc bounds) {
296         Rectangle JavaDoc screen = getScreenBounds();
297         int above = bounds.y - screen.y;
298         int below = (screen.y + screen.height) - (bounds.y + bounds.height);
299         return (above > below);
300     }
301     
302     /**
303      * Check whether there is enough space for this popup
304      * on its preferred location related to caret.
305      */

306     boolean isEnoughSpace(Rectangle JavaDoc occupiedBounds) {
307         return isEnoughSpace(occupiedBounds, preferDisplayAboveCaret);
308     }
309     
310     /**
311      * Check whether there is enough space for this popup above
312      * or below the given occupied bounds.
313      *
314      * @param occupiedBounds bounds above or below which the available
315      * space should be determined.
316      * @param aboveOccupiedBounds whether the space should be checked above
317      * or below the occupiedBounds.
318      * @return true if there is enough space for the preferred size of this popup
319      * on the requested side or false if not.
320      */

321     boolean isEnoughSpace(Rectangle JavaDoc occupiedBounds, boolean aboveOccupiedBounds) {
322         Rectangle JavaDoc screen = getScreenBounds();
323         int freeHeight = aboveOccupiedBounds
324             ? occupiedBounds.y - screen.y
325             : (screen.y + screen.height) - (occupiedBounds.y + occupiedBounds.height);
326         Dimension JavaDoc prefSize = getPreferredSize();
327         return (prefSize.height < freeHeight);
328     }
329     
330     boolean isEnoughSpace(boolean aboveCaret) {
331         return isEnoughSpace(getAnchorOffsetBounds(), aboveCaret);
332     }
333     
334     public boolean isOverlapped(Rectangle JavaDoc bounds) {
335         return isVisible() ? popupBounds.intersects(bounds) : false;
336     }
337
338     public boolean isOverlapped(CompletionLayoutPopup popup) {
339         return popup.isVisible() ? isOverlapped(popup.getPopupBounds()) : false;
340     }
341     
342     public Rectangle JavaDoc unionBounds(Rectangle JavaDoc bounds) {
343         return isVisible() ? bounds.union(getPopupBounds()) : bounds;
344     }
345
346     public abstract void processKeyEvent(KeyEvent JavaDoc evt);
347 }
348
Popular Tags