PageRenderTime 89ms CodeModel.GetById 61ms app.highlight 24ms RepoModel.GetById 0ms app.codeStats 1ms

/jEdit/tags/jedit-4-1-pre5/org/gjt/sp/jedit/gui/CompleteWord.java

#
Java | 471 lines | 342 code | 66 blank | 63 comment | 68 complexity | a0af526a8374657bf570ebb5c096a4f0 MD5 | raw file
  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
 23package org.gjt.sp.jedit.gui;
 24
 25//{{{ Imports
 26import javax.swing.*;
 27import javax.swing.event.*;
 28import java.awt.*;
 29import java.awt.event.*;
 30import java.util.Vector;
 31import org.gjt.sp.jedit.syntax.*;
 32import org.gjt.sp.jedit.textarea.*;
 33import org.gjt.sp.jedit.*;
 34//}}}
 35
 36public class CompleteWord extends JWindow
 37{
 38	//{{{ completeWord() method
 39	public static void completeWord(View view)
 40	{
 41		JEditTextArea textArea = view.getTextArea();
 42		Buffer buffer = view.getBuffer();
 43		int caretLine = textArea.getCaretLine();
 44		int caret = textArea.getCaretPosition();
 45
 46		if(!buffer.isEditable())
 47		{
 48			textArea.getToolkit().beep();
 49			return;
 50		}
 51
 52		KeywordMap keywordMap = buffer.getKeywordMapAtOffset(caret);
 53		String noWordSep = getNonAlphaNumericWordChars(buffer,keywordMap,caret);
 54		String word = getWordToComplete(buffer,caretLine,caret,noWordSep);
 55		if(word == null)
 56		{
 57			textArea.getToolkit().beep();
 58			return;
 59		}
 60
 61		Vector completions = getCompletions(buffer,word,keywordMap,
 62			noWordSep,caret);
 63
 64		if(completions.size() == 0
 65			|| (completions.size() == 1 &&
 66			((Completion)completions.get(0)).text.equals(word)))
 67		{
 68			textArea.getToolkit().beep();
 69		}
 70		//{{{ if there is only one competion, insert in buffer
 71		else if(completions.size() == 1)
 72		{
 73			textArea.setSelectedText(completions
 74				.elementAt(0).toString()
 75				.substring(word.length()));
 76		} //}}}
 77		//{{{ show popup if > 1
 78		else
 79		{
 80			textArea.scrollToCaret(false);
 81			Point location = textArea.offsetToXY(caret - word.length());
 82			location.y += textArea.getPainter().getFontMetrics()
 83				.getHeight();
 84
 85			SwingUtilities.convertPointToScreen(location,
 86				textArea.getPainter());
 87			new CompleteWord(view,word,completions,location);
 88		} //}}}
 89	} //}}}
 90
 91	//{{{ CompleteWord constructor
 92	public CompleteWord(View view, String word, Vector completions, Point location)
 93	{
 94		super(view);
 95
 96		setContentPane(new JPanel(new BorderLayout())
 97		{
 98			/**
 99			 * Returns if this component can be traversed by pressing the
100			 * Tab key. This returns false.
101			 */
102			public boolean isManagingFocus()
103			{
104				return false;
105			}
106
107			/**
108			 * Makes the tab key work in Java 1.4.
109			 */
110			public boolean getFocusTraversalKeysEnabled()
111			{
112				return false;
113			}
114		});
115
116		this.view = view;
117		this.textArea = view.getTextArea();
118		this.buffer = view.getBuffer();
119		this.word = word;
120
121		words = new JList(completions);
122		words.setFont(UIManager.getFont("TextArea.font"));
123
124		words.setVisibleRowCount(Math.min(completions.size(),8));
125
126		words.addMouseListener(new MouseHandler());
127		words.setSelectedIndex(0);
128		words.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
129		words.setCellRenderer(new Renderer());
130
131		// stupid scrollbar policy is an attempt to work around
132		// bugs people have been seeing with IBM's JDK -- 7 Sep 2000
133		JScrollPane scroller = new JScrollPane(words,
134			JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
135			JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
136
137		getContentPane().add(scroller, BorderLayout.CENTER);
138
139		GUIUtilities.requestFocus(this,words);
140
141		pack();
142		setLocation(location);
143		show();
144
145		KeyHandler keyHandler = new KeyHandler();
146		addKeyListener(keyHandler);
147		words.addKeyListener(keyHandler);
148		view.setKeyEventInterceptor(keyHandler);
149	} //}}}
150
151	//{{{ dispose() method
152	public void dispose()
153	{
154		view.setKeyEventInterceptor(null);
155		super.dispose();
156		SwingUtilities.invokeLater(new Runnable()
157		{
158			public void run()
159			{
160				textArea.requestFocus();
161			}
162		});
163	} //}}}
164
165	//{{{ Private members
166
167	//{{{ getNonAlphaNumericWordChars() method
168	private static String getNonAlphaNumericWordChars(Buffer buffer,
169		KeywordMap keywordMap, int caret)
170	{
171		// figure out what constitutes a word character and what
172		// doesn't
173		String noWordSep = buffer.getStringProperty("noWordSep");
174		if(noWordSep == null)
175			noWordSep = "";
176		if(keywordMap != null)
177		{
178			String keywordNoWordSep = keywordMap.getNonAlphaNumericChars();
179			if(keywordNoWordSep != null)
180				noWordSep = noWordSep + keywordNoWordSep;
181		}
182
183		return noWordSep;
184	} //}}}
185
186	//{{{ getWordToComplete() method
187	private static String getWordToComplete(Buffer buffer, int caretLine,
188		int caret, String noWordSep)
189	{
190		String line = buffer.getLineText(caretLine);
191		int dot = caret - buffer.getLineStartOffset(caretLine);
192		if(dot == 0)
193			return null;
194
195		char ch = line.charAt(dot-1);
196		if(!Character.isLetterOrDigit(ch)
197			&& noWordSep.indexOf(ch) == -1)
198		{
199			// attempting to expand non-word char
200			return null;
201		}
202
203		int wordStart = TextUtilities.findWordStart(line,dot-1,noWordSep);
204		String word = line.substring(wordStart,dot);
205		if(word.length() == 0)
206			return null;
207
208		return word;
209	} //}}}
210
211	//{{{ getCompletions() method
212	private static Vector getCompletions(Buffer buffer, String word,
213		KeywordMap keywordMap, String noWordSep, int caret)
214	{
215		Vector completions = new Vector();
216
217		int wordLen = word.length();
218
219		//{{{ try to find matching keywords
220		if(keywordMap != null)
221		{
222			String[] keywords = keywordMap.getKeywords();
223			for(int i = 0; i < keywords.length; i++)
224			{
225				String _keyword = keywords[i];
226				if(_keyword.regionMatches(keywordMap.getIgnoreCase(),
227					0,word,0,wordLen))
228				{
229					Completion keyword = new Completion(_keyword,true);
230					if(completions.indexOf(keyword) == -1)
231						completions.addElement(keyword);
232				}
233			}
234		} //}}}
235
236		//{{{ loop through all lines of current buffer
237		for(int i = 0; i < buffer.getLineCount(); i++)
238		{
239			String line = buffer.getLineText(i);
240			int start = buffer.getLineStartOffset(i);
241
242			// check for match at start of line
243
244			if(line.startsWith(word) && caret != start + word.length())
245			{
246				String _word = completeWord(line,0,noWordSep);
247				Completion comp = new Completion(_word,false);
248
249				// remove duplicates
250				if(completions.indexOf(comp) == -1)
251					completions.addElement(comp);
252			}
253
254			// check for match inside line
255			int len = line.length() - word.length();
256			for(int j = 0; j < len; j++)
257			{
258				char c = line.charAt(j);
259				if(!Character.isLetterOrDigit(c) && noWordSep.indexOf(c) == -1)
260				{
261					if(line.regionMatches(j + 1,word,0,wordLen)
262						&& caret != start + j + word.length() + 1)
263					{
264						String _word = completeWord(line,j + 1,noWordSep);
265						Completion comp = new Completion(_word,false);
266
267						// remove duplicates
268						if(completions.indexOf(comp) == -1)
269							completions.addElement(comp);
270					}
271				}
272			}
273		} //}}}
274
275		// sort completion list
276		MiscUtilities.quicksort(completions,new MiscUtilities.StringICaseCompare());
277
278		return completions;
279	} //}}}
280
281	//{{{ completeWord() method
282	private static String completeWord(String line, int offset, String noWordSep)
283	{
284		// '+ 1' so that findWordEnd() doesn't pick up the space at the start
285		int wordEnd = TextUtilities.findWordEnd(line,offset + 1,noWordSep);
286		return line.substring(offset,wordEnd);
287	} //}}}
288
289	//{{{ Instance variables
290	private View view;
291	private JEditTextArea textArea;
292	private Buffer buffer;
293	private String word;
294	private JList words;
295	//}}}
296
297	//{{{ insertSelected() method
298	private void insertSelected()
299	{
300		textArea.setSelectedText(words.getSelectedValue().toString()
301			.substring(word.length()));
302		dispose();
303	} //}}}
304
305	//}}}
306
307	//{{{ Completion class
308	static class Completion
309	{
310		String text;
311		boolean keyword;
312
313		Completion(String text, boolean keyword)
314		{
315			this.text = text;
316			this.keyword = keyword;
317		}
318
319		public String toString()
320		{
321			return text;
322		}
323
324		public boolean equals(Object obj)
325		{
326			if(obj instanceof Completion)
327				return ((Completion)obj).text.equals(text);
328			else
329				return false;
330		}
331	} //}}}
332
333	//{{{ Renderer class
334	static class Renderer extends DefaultListCellRenderer
335	{
336		public Component getListCellRendererComponent(JList list, Object value,
337			int index, boolean isSelected, boolean cellHasFocus)
338		{
339			super.getListCellRendererComponent(list,value,index,
340				isSelected,cellHasFocus);
341
342			Completion comp = (Completion)value;
343			if(comp.keyword)
344				setFont(list.getFont().deriveFont(Font.BOLD));
345			else
346				setFont(list.getFont());
347
348			return this;
349		}
350	} //}}}
351
352	//{{{ KeyHandler class
353	class KeyHandler extends KeyAdapter
354	{
355		//{{{ keyPressed() method
356		public void keyPressed(KeyEvent evt)
357		{
358			switch(evt.getKeyCode())
359			{
360			case KeyEvent.VK_TAB:
361			case KeyEvent.VK_ENTER:
362				insertSelected();
363				evt.consume();
364				break;
365			case KeyEvent.VK_ESCAPE:
366				dispose();
367				evt.consume();
368				break;
369			case KeyEvent.VK_UP:
370				int selected = words.getSelectedIndex();
371
372				if(selected == 0)
373					selected = words.getModel().getSize() - 1;
374				else if(getFocusOwner() == words)
375					return;
376				else
377					selected = selected - 1;
378
379				words.setSelectedIndex(selected);
380				words.ensureIndexIsVisible(selected);
381
382				evt.consume();
383				break;
384			case KeyEvent.VK_DOWN:
385				/* int */ selected = words.getSelectedIndex();
386
387				if(selected == words.getModel().getSize() - 1)
388					selected = 0;
389				else if(getFocusOwner() == words)
390					return;
391				else
392					selected = selected + 1;
393
394				words.setSelectedIndex(selected);
395				words.ensureIndexIsVisible(selected);
396
397				evt.consume();
398				break;
399			case KeyEvent.VK_BACK_SPACE:
400				if(word.length() == 1)
401				{
402					textArea.backspace();
403					evt.consume();
404					dispose();
405				}
406				else
407				{
408					word = word.substring(0,word.length() - 1);
409					textArea.backspace();
410					int caret = textArea.getCaretPosition();
411					KeywordMap keywordMap = buffer.getKeywordMapAtOffset(caret);
412					String noWordSep = getNonAlphaNumericWordChars(buffer,keywordMap,caret);
413
414					Vector completions = getCompletions(buffer,word,
415						keywordMap,noWordSep,caret);
416
417					if(completions.size() == 0)
418						dispose();
419
420					words.setListData(completions);
421					words.setSelectedIndex(0);
422
423					evt.consume();
424				}
425				break;
426			default:
427				if(evt.isActionKey())
428				{
429					dispose();
430					view.processKeyEvent(evt);
431				}
432				break;
433			}
434		} //}}}
435
436		//{{{ keyTyped() method
437		public void keyTyped(KeyEvent evt)
438		{
439			char ch = evt.getKeyChar();
440			evt = KeyEventWorkaround.processKeyEvent(evt);
441			if(evt == null)
442				return;
443			else if(ch != '\b')
444			{
445				word = word + ch;
446				textArea.userInput(ch);
447				int caret = textArea.getCaretPosition();
448				KeywordMap keywordMap = buffer.getKeywordMapAtOffset(caret);
449				String noWordSep = getNonAlphaNumericWordChars(buffer,keywordMap,caret);
450
451				Vector completions = getCompletions(buffer,word,keywordMap,
452					noWordSep,caret);
453
454				if(completions.size() == 0)
455					dispose();
456
457				words.setListData(completions);
458				words.setSelectedIndex(0);
459			}
460		} //}}}
461	} //}}}
462
463	//{{{ MouseHandler class
464	class MouseHandler extends MouseAdapter
465	{
466		public void mouseClicked(MouseEvent evt)
467		{
468			insertSelected();
469		}
470	} //}}}
471}