KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > gjt > sp > jedit > gui > CompleteWord


1 /*
2  * CompleteWord.java - Complete word dialog
3  * :tabSize=8:indentSize=8:noTabs=false:
4  * :folding=explicit:collapseFolds=1:
5  *
6  * Copyright (C) 2000, 2001 Slava Pestov
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21  */

22
23 package org.gjt.sp.jedit.gui;
24
25 //{{{ Imports
26
import javax.swing.*;
27 import java.awt.*;
28 import java.awt.event.*;
29 import java.util.HashSet JavaDoc;
30 import java.util.TreeSet JavaDoc;
31 import java.util.Set JavaDoc;
32 import org.gjt.sp.jedit.syntax.*;
33 import org.gjt.sp.jedit.textarea.*;
34 import org.gjt.sp.jedit.*;
35 import org.gjt.sp.util.StandardUtilities;
36 //}}}
37

38 /**
39  * A completion popup class.
40  */

41
42 public class CompleteWord extends JWindow
43 {
44     //{{{ completeWord() method
45
public static void completeWord(View view)
46     {
47         JEditTextArea textArea = view.getTextArea();
48         Buffer buffer = view.getBuffer();
49         int caretLine = textArea.getCaretLine();
50         int caret = textArea.getCaretPosition();
51
52         if(!buffer.isEditable())
53         {
54             textArea.getToolkit().beep();
55             return;
56         }
57
58         KeywordMap keywordMap = buffer.getKeywordMapAtOffset(caret);
59         String JavaDoc noWordSep = getNonAlphaNumericWordChars(
60             buffer,keywordMap);
61         String JavaDoc word = getWordToComplete(buffer,caretLine,
62             caret,noWordSep);
63         if(word == null)
64         {
65             textArea.getToolkit().beep();
66             return;
67         }
68
69         Completion[] completions = getCompletions(buffer,word,caret);
70
71         if(completions.length == 0)
72         {
73             textArea.getToolkit().beep();
74         }
75         //{{{ if there is only one competion, insert in buffer
76
else if(completions.length == 1)
77         {
78             Completion c = completions[0];
79
80             if(c.text.equals(word))
81             {
82                 textArea.getToolkit().beep();
83             }
84             else
85             {
86                 textArea.setSelectedText(c.text.substring(
87                     word.length()));
88             }
89         } //}}}
90
//{{{ show popup if > 1
91
else
92         {
93             String JavaDoc longestPrefix = MiscUtilities.getLongestPrefix(
94                 completions,
95                 keywordMap != null
96                 ? keywordMap.getIgnoreCase()
97                 : false);
98
99             if (word.length() < longestPrefix.length())
100             {
101                 buffer.insert(caret,longestPrefix.substring(
102                     word.length()));
103             }
104
105             textArea.scrollToCaret(false);
106             Point location = textArea.offsetToXY(
107                 caret - word.length());
108             location.y += textArea.getPainter().getFontMetrics()
109                 .getHeight();
110
111             SwingUtilities.convertPointToScreen(location,
112                 textArea.getPainter());
113             new CompleteWord(view,longestPrefix,
114                 completions,location,noWordSep);
115         } //}}}
116
} //}}}
117

118     //{{{ fitInScreen() method
119
public static Point fitInScreen(Point p, Window w, int lineHeight)
120     {
121         Rectangle screenSize = w.getGraphicsConfiguration().getBounds();
122         if(p.y + w.getHeight() >= screenSize.height)
123             p.y = p.y - w.getHeight() - lineHeight;
124         return p;
125     } //}}}
126

127     //{{{ CompleteWord constructor
128
public CompleteWord(View view, String JavaDoc word, Completion[] completions,
129         Point location, String JavaDoc noWordSep)
130     {
131         super(view);
132
133         this.noWordSep = noWordSep;
134
135         setContentPane(new JPanel(new BorderLayout())
136         {
137             /**
138              * Returns if this component can be traversed by pressing the
139              * Tab key. This returns false.
140              */

141             public boolean isManagingFocus()
142             {
143                 return false;
144             }
145
146             /**
147              * Makes the tab key work in Java 1.4.
148              */

149             public boolean getFocusTraversalKeysEnabled()
150             {
151                 return false;
152             }
153         });
154
155         this.view = view;
156         this.textArea = view.getTextArea();
157         this.buffer = view.getBuffer();
158         this.word = word;
159
160         words = new JList(completions);
161
162         words.setVisibleRowCount(Math.min(completions.length,8));
163
164         words.addMouseListener(new MouseHandler());
165         words.setSelectedIndex(0);
166         words.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
167         words.setCellRenderer(new Renderer());
168
169         // stupid scrollbar policy is an attempt to work around
170
// bugs people have been seeing with IBM's JDK -- 7 Sep 2000
171
JScrollPane scroller = new JScrollPane(words,
172             ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
173             ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
174
175         getContentPane().add(scroller, BorderLayout.CENTER);
176
177         GUIUtilities.requestFocus(this,words);
178
179         pack();
180         setLocation(fitInScreen(location,this,
181             textArea.getPainter().getFontMetrics()
182             .getHeight()));
183         setVisible(true);
184
185         KeyHandler keyHandler = new KeyHandler();
186         addKeyListener(keyHandler);
187         words.addKeyListener(keyHandler);
188         view.setKeyEventInterceptor(keyHandler);
189     } //}}}
190

191     //{{{ dispose() method
192
public void dispose()
193     {
194         view.setKeyEventInterceptor(null);
195         super.dispose();
196         SwingUtilities.invokeLater(new Runnable JavaDoc()
197         {
198             public void run()
199             {
200                 textArea.requestFocus();
201             }
202         });
203     } //}}}
204

205     //{{{ Private members
206

207     //{{{ getNonAlphaNumericWordChars() method
208
private static String JavaDoc getNonAlphaNumericWordChars(Buffer buffer,
209         KeywordMap keywordMap)
210     {
211         // figure out what constitutes a word character and what
212
// doesn't
213
String JavaDoc noWordSep = buffer.getStringProperty("noWordSep");
214         if(noWordSep == null)
215             noWordSep = "";
216         if(keywordMap != null)
217         {
218             String JavaDoc keywordNoWordSep = keywordMap.getNonAlphaNumericChars();
219             if(keywordNoWordSep != null)
220                 noWordSep += keywordNoWordSep;
221         }
222
223         return noWordSep;
224     } //}}}
225

226     //{{{ getWordToComplete() method
227
private static String JavaDoc getWordToComplete(Buffer buffer, int caretLine,
228         int caret, String JavaDoc noWordSep)
229     {
230         String JavaDoc line = buffer.getLineText(caretLine);
231         int dot = caret - buffer.getLineStartOffset(caretLine);
232         if(dot == 0)
233             return null;
234
235         char ch = line.charAt(dot-1);
236         if(!Character.isLetterOrDigit(ch)
237             && noWordSep.indexOf(ch) == -1)
238         {
239             // attempting to expand non-word char
240
return null;
241         }
242
243         int wordStart = TextUtilities.findWordStart(line,dot-1,noWordSep);
244         String JavaDoc word = line.substring(wordStart,dot);
245         if(word.length() == 0)
246             return null;
247
248         return word;
249     } //}}}
250

251     //{{{ getCompletions() method
252
private static Completion[] getCompletions(Buffer buffer, String JavaDoc word,
253         int caret)
254     {
255         // build a list of unique words in all visible buffers
256
Set JavaDoc<Completion> completions = new TreeSet JavaDoc<Completion>(new StandardUtilities
257
            .StringCompare());
258         Set JavaDoc<Buffer> buffers = new HashSet JavaDoc<Buffer>();
259
260         // only complete current buffer's keyword map
261
KeywordMap keywordMap = buffer.getKeywordMapAtOffset(caret);
262         String JavaDoc noWordSep = getNonAlphaNumericWordChars(
263             buffer,keywordMap);
264
265         View views = jEdit.getFirstView();
266         while(views != null)
267         {
268             EditPane[] panes = views.getEditPanes();
269             for(int i = 0; i < panes.length; i++)
270             {
271                 Buffer b = panes[i].getBuffer();
272                 if(buffers.contains(b))
273                     continue;
274
275                 buffers.add(b);
276
277                 // only complete current buffer's keyword map
278
KeywordMap _keywordMap;
279                 if(b == buffer)
280                     _keywordMap = keywordMap;
281                 else
282                     _keywordMap = null;
283
284                 int offset = (b == buffer ? caret : 0);
285
286                 getCompletions(b,word,keywordMap,noWordSep,
287                     offset,completions);
288             }
289
290             views = views.getNext();
291         }
292
293         Completion[] completionArray = completions
294             .toArray(new Completion[completions.size()]);
295
296         return completionArray;
297     } //}}}
298

299     //{{{ getCompletions() method
300
private static void getCompletions(Buffer buffer, String JavaDoc word,
301         KeywordMap keywordMap, String JavaDoc noWordSep, int caret,
302         Set JavaDoc<Completion> completions)
303     {
304         int wordLen = word.length();
305
306         //{{{ try to find matching keywords
307
if(keywordMap != null)
308         {
309             String JavaDoc[] keywords = keywordMap.getKeywords();
310             for(int i = 0; i < keywords.length; i++)
311             {
312                 String JavaDoc _keyword = keywords[i];
313                 if(_keyword.regionMatches(keywordMap.getIgnoreCase(),
314                     0,word,0,wordLen))
315                 {
316                     Completion keyword = new Completion(_keyword,true);
317                     if(!completions.contains(keyword))
318                     {
319                         completions.add(keyword);
320                     }
321                 }
322             }
323         } //}}}
324

325         //{{{ loop through all lines of current buffer
326
for(int i = 0; i < buffer.getLineCount(); i++)
327         {
328             String JavaDoc line = buffer.getLineText(i);
329             int start = buffer.getLineStartOffset(i);
330
331             // check for match at start of line
332

333             if(line.startsWith(word) && caret != start + word.length())
334             {
335                 String JavaDoc _word = completeWord(line,0,noWordSep);
336                 Completion comp = new Completion(_word,false);
337
338                 // remove duplicates
339
if(!completions.contains(comp))
340                 {
341                     completions.add(comp);
342                 }
343             }
344
345             // check for match inside line
346
int len = line.length() - word.length();
347             for(int j = 0; j < len; j++)
348             {
349                 char c = line.charAt(j);
350                 if(!Character.isLetterOrDigit(c) && noWordSep.indexOf(c) == -1)
351                 {
352                     if(line.regionMatches(j + 1,word,0,wordLen)
353                         && caret != start + j + word.length() + 1)
354                     {
355                         String JavaDoc _word = completeWord(line,j + 1,noWordSep);
356                         Completion comp = new Completion(_word,false);
357
358                         // remove duplicates
359
if(!completions.contains(comp))
360                         {
361                             completions.add(comp);
362                         }
363                     }
364                 }
365             }
366         } //}}}
367
} //}}}
368

369     //{{{ completeWord() method
370
private static String JavaDoc completeWord(String JavaDoc line, int offset, String JavaDoc noWordSep)
371     {
372         // '+ 1' so that findWordEnd() doesn't pick up the space at the start
373
int wordEnd = TextUtilities.findWordEnd(line,offset + 1,noWordSep);
374         return line.substring(offset,wordEnd);
375     } //}}}
376

377     //{{{ Instance variables
378
private View view;
379     private JEditTextArea textArea;
380     private Buffer buffer;
381     private String JavaDoc word;
382     private JList words;
383     private String JavaDoc noWordSep;
384     //}}}
385

386     //{{{ insertSelected() method
387
private void insertSelected()
388     {
389         textArea.setSelectedText(words.getSelectedValue().toString()
390             .substring(word.length()));
391         dispose();
392     } //}}}
393

394     //}}}
395

396     //{{{ Completion class
397
static class Completion
398     {
399         final String JavaDoc text;
400         final boolean keyword;
401
402         Completion(String JavaDoc text, boolean keyword)
403         {
404             this.text = text;
405             this.keyword = keyword;
406         }
407
408         public String JavaDoc toString()
409         {
410             return text;
411         }
412
413         public int hashCode()
414         {
415             return text.hashCode();
416         }
417
418         public boolean equals(Object JavaDoc obj)
419         {
420             if(obj instanceof Completion)
421                 return ((Completion)obj).text.equals(text);
422             else
423                 return false;
424         }
425     } //}}}
426

427     //{{{ Renderer class
428
static class Renderer extends DefaultListCellRenderer
429     {
430         public Component getListCellRendererComponent(JList list, Object JavaDoc value,
431             int index, boolean isSelected, boolean cellHasFocus)
432         {
433             super.getListCellRendererComponent(list,null,index,
434                 isSelected,cellHasFocus);
435
436             Completion comp = (Completion)value;
437
438             if(index < 9)
439                 setText((index + 1) + ": " + comp.text);
440             else if(index == 9)
441                 setText("0: " + comp.text);
442             else
443                 setText(comp.text);
444
445             if(comp.keyword)
446                 setFont(list.getFont().deriveFont(Font.BOLD));
447             else
448                 setFont(list.getFont());
449
450             return this;
451         }
452     } //}}}
453

454     //{{{ KeyHandler class
455
class KeyHandler extends KeyAdapter
456     {
457         //{{{ keyPressed() method
458
public void keyPressed(KeyEvent evt)
459         {
460             int selected = words.getSelectedIndex();
461             int numRows = words.getVisibleRowCount()-1;
462             int newSelect = -1;
463
464             switch(evt.getKeyCode())
465             {
466             
467             case KeyEvent.VK_TAB:
468             case KeyEvent.VK_ENTER:
469                 insertSelected();
470                 evt.consume();
471                 break;
472             case KeyEvent.VK_ESCAPE:
473                 dispose();
474                 evt.consume();
475                 break;
476             case KeyEvent.VK_UP:
477                 if (getFocusOwner() == words) return;
478                 evt.consume();
479                 if(selected == 0) return;
480                 selected--;
481                 words.setSelectedIndex(selected);
482                 words.ensureIndexIsVisible(selected);
483                 break;
484             case KeyEvent.VK_DOWN:
485                 if(getFocusOwner() == words) return;
486                 evt.consume();
487                 if(selected >= words.getModel().getSize()) break;
488                 selected++;
489                 words.setSelectedIndex(selected);
490                 words.ensureIndexIsVisible(selected);
491                 break;
492             case KeyEvent.VK_PAGE_UP:
493                 newSelect = selected - numRows;
494                 if (newSelect < 0) newSelect = 0;
495                 words.setSelectedIndex(newSelect);
496                 words.ensureIndexIsVisible(newSelect);
497                 evt.consume();
498                 break;
499             case KeyEvent.VK_PAGE_DOWN:
500                 newSelect = selected + numRows;
501                 if (newSelect >= words.getModel().getSize()) newSelect = words.getModel().getSize() - 1;
502                 words.setSelectedIndex(newSelect);
503                 words.ensureIndexIsVisible(newSelect);
504                 evt.consume();
505                 break;
506                 
507             case KeyEvent.VK_BACK_SPACE:
508                 if(word.length() == 1)
509                 {
510                     textArea.backspace();
511                     evt.consume();
512                     dispose();
513                 }
514                 else
515                 {
516                     word = word.substring(0,word.length() - 1);
517                     textArea.backspace();
518                     int caret = textArea.getCaretPosition();
519
520                     Completion[] completions
521                         = getCompletions(buffer,word,
522                         caret);
523
524                     if(completions.length == 0)
525                     {
526                         dispose();
527                         return;
528                     }
529
530                     words.setListData(completions);
531                     words.setSelectedIndex(0);
532                     words.setVisibleRowCount(Math.min(completions.length,8));
533
534                     pack();
535
536                     evt.consume();
537                 }
538                 break;
539             default:
540                 if(evt.isActionKey()
541                     || evt.isControlDown()
542                     || evt.isAltDown()
543                     || evt.isMetaDown())
544                 {
545                     dispose();
546                     view.getInputHandler().processKeyEvent(evt, View.VIEW, false);
547                 }
548                 break;
549             }
550
551
552         } //}}}
553

554         //{{{ keyTyped() method
555
public void keyTyped(KeyEvent evt)
556         {
557             char ch = evt.getKeyChar();
558             evt = KeyEventWorkaround.processKeyEvent(evt);
559             if(evt == null)
560                 return;
561
562             if(Character.isDigit(ch))
563             {
564                 int index = ch - '0';
565                 if(index == 0)
566                     index = 9;
567                 else
568                     index--;
569                 if(index < words.getModel().getSize())
570                 {
571                     words.setSelectedIndex(index);
572                     textArea.setSelectedText(words.getModel()
573                         .getElementAt(index).toString()
574                         .substring(word.length()));
575                     dispose();
576                     return;
577                 }
578                 else
579                     /* fall through */;
580             }
581
582             // \t handled above
583
if(ch != '\b' && ch != '\t')
584             {
585                 /* eg, foo<C+b>, will insert foobar, */
586                 if(!Character.isLetterOrDigit(ch) && noWordSep.indexOf(ch) == -1)
587                 {
588                     insertSelected();
589                     textArea.userInput(ch);
590                     dispose();
591                     return;
592                 }
593
594                 textArea.userInput(ch);
595
596                 word += ch;
597                 int caret = textArea.getCaretPosition();
598
599                 Completion[] completions = getCompletions(
600                     buffer,word,caret);
601
602                 if(completions.length == 0)
603                 {
604                     dispose();
605                     return;
606                 }
607
608                 words.setListData(completions);
609                 words.setSelectedIndex(0);
610                 words.setVisibleRowCount(Math.min(completions.length,8));
611             }
612         } //}}}
613
} //}}}
614

615     //{{{ MouseHandler class
616
class MouseHandler extends MouseAdapter
617     {
618         public void mouseClicked(MouseEvent evt)
619         {
620             evt.consume();
621             insertSelected();
622         }
623     } //}}}
624
}
625
Popular Tags