KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > antlr > works > completion > AutoCompletionMenu


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

31
32 package org.antlr.works.completion;
33
34 import org.antlr.works.prefs.AWPrefs;
35 import org.antlr.works.stats.StatisticsAW;
36 import org.antlr.works.utils.OverlayObject;
37 import org.antlr.xjlib.appkit.frame.XJFrameInterface;
38
39 import javax.swing.*;
40 import javax.swing.border.BevelBorder JavaDoc;
41 import javax.swing.text.BadLocationException JavaDoc;
42 import javax.swing.text.Document JavaDoc;
43 import javax.swing.text.JTextComponent JavaDoc;
44 import java.awt.*;
45 import java.awt.event.*;
46 import java.util.LinkedList JavaDoc;
47 import java.util.List JavaDoc;
48
49 public class AutoCompletionMenu extends OverlayObject {
50
51     protected AutoCompletionMenuDelegate delegate;
52
53     protected DefaultListModel listModel;
54     protected JList list;
55
56     protected List JavaDoc<String JavaDoc> words;
57     /** Used to store most recently used during autocompletion
58      * the newest should be stored at the front of the list.
59      */

60     protected static List JavaDoc<String JavaDoc> recentlyUsedWords = new LinkedList JavaDoc<String JavaDoc>();
61     protected int maxWordLength;
62
63     protected int insertionStartIndex;
64     protected int insertionEndIndex;
65
66     protected int displayIndex;
67
68     public static int visibleMatchingRules = 15;
69
70     public AutoCompletionMenu(AutoCompletionMenuDelegate delegate, JTextComponent JavaDoc textComponent, XJFrameInterface frame) {
71         super(frame, textComponent);
72         this.delegate = delegate;
73     }
74
75     public boolean isVStyle() {
76         return AWPrefs.isVStyleAutoCompletion();
77     }
78
79     public JTextComponent JavaDoc getTextComponent() {
80         return (JTextComponent JavaDoc)parentComponent;
81     }
82
83     public JComponent overlayCreateInterface() {
84         visibleMatchingRules = (isVStyle()?7:15); //if it's on all the time, better if there's less displayed on screen
85
getTextComponent().addKeyListener(new MyKeyAdapter());
86
87         listModel = new DefaultListModel();
88
89         list = new JList(listModel) {
90             public int getVisibleRowCount() {
91                 return Math.min(listModel.getSize(), visibleMatchingRules);
92             }
93         };
94         list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
95         list.setBackground(new Color(235, 244, 254));
96         list.addKeyListener(new MyKeyAdapter());
97         list.setPrototypeCellValue("This is a rule name g");
98         list.addMouseListener(new ListMouseAdapter());
99
100         JScrollPane scrollPane = new JScrollPane(list, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
101         scrollPane.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
102         return scrollPane;
103     }
104
105     public boolean overlayWillDisplay() {
106         if (isVStyle()){
107             //this is vsStyle, so whenever space is pressed, this is run
108
int index = getTextComponent().getCaretPosition()+1;//the space hasn't happened yet
109

110             String JavaDoc partialWord = "";
111             setInsertionStartIndex(index);
112             setInsertionEndIndex(index);
113
114             List JavaDoc<String JavaDoc> matchingRules = delegate.autoCompletionMenuGetMatchingWordsForPartialWord(partialWord);
115             if(matchingRules.size() == 0) { //this'll only happen if there's no rules made yet (since partial word is "")
116
return false;
117             }
118
119             list.setFont(new Font(AWPrefs.getEditorFont(), Font.PLAIN, 12));
120             list.setAutoscrolls(true);
121             setDisplayIndex(index);
122             setWordLists(matchingRules, matchingRules);
123             delegate.autoCompletionMenuWillDisplay();
124             selectMostRecentlyUsedWordPosition(partialWord, matchingRules.get(0));
125             return true;
126         } else {
127             int position = getTextComponent().getCaretPosition();
128
129             int index = getPartialWordBeginsAtPosition(position);
130             String JavaDoc partialWord = "";
131             if(index < position)
132                 partialWord = getTextComponent().getText().substring(index+1, position);
133
134             setInsertionStartIndex(index+1);
135             setInsertionEndIndex(position);
136
137             List JavaDoc<String JavaDoc> matchingRules = delegate.autoCompletionMenuGetMatchingWordsForPartialWord(partialWord);
138             if(matchingRules.size() == 0) {
139                 return false;
140             } else if(matchingRules.size() == 1) {
141                 completePartialWord(matchingRules.get(0));
142                 return false;
143             }
144
145             list.setFont(new Font(AWPrefs.getEditorFont(), Font.PLAIN, 12));
146             list.setAutoscrolls(true);
147             setDisplayIndex(index+1);
148             setWordLists(matchingRules, matchingRules);
149             delegate.autoCompletionMenuWillDisplay();
150             return true;
151         }
152     }
153
154     public KeyStroke overlayDisplayKeyStroke() {
155         if (isVStyle())
156             return KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0); //whenever space is pressed
157
else
158             return KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, InputEvent.CTRL_MASK);
159     }
160
161     public String JavaDoc overlayDisplayKeyStrokeMappingName() {
162         return "controlEspace";
163     }
164
165     public void setWordLists(List JavaDoc<String JavaDoc> names, List JavaDoc<String JavaDoc> words) {
166         listModel.clear();
167         for (String JavaDoc name : names) listModel.addElement(name);
168
169         this.words = words;
170         maxWordLength = 0;
171         for (String JavaDoc word : words) {
172             maxWordLength = Math.max(maxWordLength, word.length());
173         }
174     }
175
176     public void setInsertionStartIndex(int startIndex) {
177         insertionStartIndex = startIndex;
178     }
179
180     public void setInsertionEndIndex(int endIndex) {
181         insertionEndIndex = endIndex;
182     }
183
184     public void setDisplayIndex(int index) {
185         this.displayIndex = index;
186     }
187
188     public static boolean isCharIdentifier(char c) {
189         return Character.isLetterOrDigit(c) || c == '_';
190     }
191
192     private static boolean isAlphaNumericOr_(int keyCode) {
193         return (keyCode>=65 && keyCode <=90 || keyCode == 45); //is a-zA-Z or '_'(45)
194
}
195
196     private static boolean isFunctionKey(int keyCode) {
197         //16-18 (ctrl,shift,alt) 20 is capslock
198
return (keyCode >= 16 && keyCode <=18 || keyCode==20);
199     }
200
201     public int getPartialWordBeginsAtPosition(int pos) {
202         String JavaDoc t = getTextComponent().getText();
203         int index = pos-1;
204         while((index>=0) && isCharIdentifier(t.charAt(index))) {
205             index--;
206         }
207         return index;
208     }
209
210     public void completePartialWord(String JavaDoc word) {
211         try {
212             Document JavaDoc doc = getTextComponent().getDocument();
213             doc.remove(insertionStartIndex, insertionEndIndex-insertionStartIndex);
214             doc.insertString(insertionStartIndex, word, null);
215         } catch (BadLocationException JavaDoc e) {
216             e.printStackTrace();
217         }
218     }
219
220     public void autoComplete() {
221         if(list.getSelectedIndex() >= 0){
222             String JavaDoc partialWord = words.get(list.getSelectedIndex());
223             if (isVStyle()) {
224                 recentlyUsedWords.remove(partialWord); //put it at the beginning of the list if it exists
225
((LinkedList JavaDoc)recentlyUsedWords).addFirst(partialWord);
226             }
227             completePartialWord(partialWord);
228         }
229     }
230
231     public void resize() {
232         Rectangle rect = null;
233
234         StatisticsAW.shared().recordEvent(StatisticsAW.EVENT_SHOW_AUTO_COMPLETION_MENU);
235
236         try {
237             rect = getTextComponent().getUI().modelToView(getTextComponent(), displayIndex);
238         } catch (BadLocationException JavaDoc e) {
239             e.printStackTrace();
240         }
241
242         if(rect == null)
243             return;
244
245         Point p = SwingUtilities.convertPoint(getTextComponent(), new Point(rect.x, rect.y), parentFrame.getRootPane());
246         int height = list.getFixedCellHeight();
247         int size = listModel.size();
248         if(size > 0) {
249             height = height*Math.min(visibleMatchingRules, size)+5;
250             content.setBounds(p.x - 3, p.y + rect.height, maxWordLength*8+50, height);
251         }
252     }
253
254     public void selectMostRecentlyUsedWordPosition(String JavaDoc partialWord, String JavaDoc firstWordInList){
255         String JavaDoc mostRecentWord="";
256         for (String JavaDoc recentlyUsedWord : recentlyUsedWords) {
257             if (recentlyUsedWord.toLowerCase().startsWith(partialWord) && words.contains(recentlyUsedWord)) {
258                 mostRecentWord = recentlyUsedWord;
259                 break;
260             }
261         }
262
263         if (mostRecentWord.length()>0){
264             list.setSelectedValue(mostRecentWord,true);
265         }
266         else {
267             //don't have any recent words that match, so select first word
268
list.setSelectedValue(firstWordInList,true);
269         }
270         //fix the scrolling:
271
//put the word in the middle of the box
272
int selectedIndex = list.getSelectedIndex();
273         int bottomIndex = Math.max(0,selectedIndex-1);
274         int topIndex = Math.min(words.size()-1, selectedIndex+2);
275         list.scrollRectToVisible(list.getCellBounds(bottomIndex, topIndex));
276     }
277
278     public void updateAutoCompleteList() {
279         if(!content.isVisible())
280             return;
281
282         int position = getTextComponent().getCaretPosition();
283         int index = getPartialWordBeginsAtPosition(position);
284         String JavaDoc partialWord = "";
285         if(index<position)
286             partialWord = getTextComponent().getText().substring(index+1, position);
287
288         List JavaDoc<String JavaDoc> matchingRules = delegate.autoCompletionMenuGetMatchingWordsForPartialWord(partialWord);
289         if(matchingRules == null || matchingRules.size() == 0) {
290             hide();
291         } else {
292             setInsertionEndIndex(position);
293             if (!isVStyle()) setWordLists(matchingRules, matchingRules);
294             selectMostRecentlyUsedWordPosition(partialWord,matchingRules.get(0));
295             resize();
296         }
297     }
298
299     public class ListMouseAdapter extends MouseAdapter {
300         public void mouseReleased(MouseEvent e) {
301             if (e.isConsumed() || !content.isVisible())
302                 return;
303             autoComplete();
304             content.setVisible(false);
305             parentComponent.requestFocusInWindow(); //return focus to text
306
}
307     }
308
309     public class MyKeyAdapter extends KeyAdapter {
310
311         public void move(int delta) {
312             if(listModel.getSize() < 1)
313                 return;
314
315             int current = list.getSelectedIndex();
316             int index = Math.max(0, Math.min(listModel.getSize() - 1, current + delta));
317             list.setSelectionInterval(index, index);
318             list.scrollRectToVisible(list.getCellBounds(index, index));
319         }
320
321         public void keyPressed(KeyEvent e) {
322             if(e.isConsumed())
323                 return;
324
325             int keyCode = e.getKeyCode();
326             if(!content.isVisible())
327                 return;
328             switch(keyCode) {
329                 case KeyEvent.VK_LEFT:
330                 case KeyEvent.VK_RIGHT:
331                     content.setVisible(false);
332                     break;
333
334                 case KeyEvent.VK_BACK_SPACE:
335                     int position = getTextComponent().getCaretPosition();
336                     int index = getPartialWordBeginsAtPosition(position);
337                     if (position-1 <= index)
338                         content.setVisible(false);
339                     //make it so they can backspace out of a word and it closes autocomplete
340
//(it closes if the word they were typing completely deletes)
341
//position-2 : right as they delete the last letter(it hasn't been updated yet)
342
//position-1 : they have deleted the last letter and are now deleting the space
343
//I personally like position-1 better...I think
344

345                     break;
346                 case KeyEvent.VK_T: //if Ctrl+T is pressed
347
case KeyEvent.VK_F: //if Ctrl+F is pressed
348
if (!e.isControlDown()) break;
349                     content.setVisible(false); //don't consume
350
break;
351
352                 case KeyEvent.VK_ESCAPE:
353                     //@todo it'd be cool to do intellij CTRL+mouse = goto...but it won't work in this class :(
354
content.setVisible(false);
355                     e.consume();
356                     break;
357
358                 case KeyEvent.VK_ENTER:
359                     autoComplete();
360                     content.setVisible(false);
361                     e.consume();
362                     break;
363
364                 case KeyEvent.VK_DOWN:
365                     move(1);
366                     e.consume();
367                     break;
368
369                 case KeyEvent.VK_UP:
370                     move(-1);
371                     e.consume();
372                     break;
373
374                 case KeyEvent.VK_PAGE_DOWN:
375                     if (isVStyle()) {content.setVisible(false);break;} //good.mdiehl: this just gets annoying when autocomplete is always up
376
move(list.getVisibleRowCount() - 1);
377                     e.consume();
378                     break;
379
380                 case KeyEvent.VK_PAGE_UP:
381                     if (isVStyle()) {content.setVisible(false);break;} //good.mdiehl: this just gets annoying when autocomplete is always up
382
move(-(list.getVisibleRowCount() - 1));
383                     e.consume();
384                     break;
385
386                 case KeyEvent.VK_HOME:
387                     if (isVStyle()) {content.setVisible(false);break;} //good.mdiehl: this just gets annoying when autocomplete is always up
388
move(-listModel.getSize());
389                     e.consume();
390                     break;
391
392                 case KeyEvent.VK_END:
393                     if (isVStyle()) {content.setVisible(false);break;} //good.mdiehl: this just gets annoying when autocomplete is always up
394
move(listModel.getSize());
395                     e.consume();
396                     break;
397
398                 default: //good.mdiehl: if they type anything not part of an identifier, close the autocomplete (ctrl,alt,shift are ok)
399
if (!(isAlphaNumericOr_(keyCode) || isFunctionKey(keyCode)) ){
400                         System.out.println(keyCode); //good.mdiehl: find out which key was pressed that is killing autocomplete
401
content.setVisible(false);
402                     }
403             }
404         }
405
406     }
407 }
408
Popular Tags