PageRenderTime 196ms CodeModel.GetById 155ms app.highlight 35ms RepoModel.GetById 1ms app.codeStats 1ms

/jEdit/tags/jedit-4-5-pre1/org/gjt/sp/jedit/gui/ActionBar.java

#
Java | 548 lines | 435 code | 52 blank | 61 comment | 96 complexity | 230333416c83e19de2bfd465f646c487 MD5 | raw file
  1/*
  2 * ActionBar.java - For invoking actions directly
  3 * :tabSize=8:indentSize=8:noTabs=false:
  4 * :folding=explicit:collapseFolds=1:
  5 *
  6 * Copyright (C) 2003 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 org.gjt.sp.jedit.bsh.NameSpace;
 27import java.awt.event.*;
 28import java.awt.*;
 29import java.util.ArrayList;
 30import javax.swing.event.*;
 31import javax.swing.*;
 32import org.gjt.sp.jedit.*;
 33import org.gjt.sp.util.StandardUtilities;
 34//}}}
 35
 36/**
 37 * Action invocation bar.
 38 */
 39public class ActionBar extends JToolBar
 40{
 41	//{{{ ActionBar constructor
 42	public ActionBar(View view, boolean temp)
 43	{
 44		this.view = view;
 45		this.temp = temp;
 46		
 47		setLayout(new BoxLayout(this,BoxLayout.X_AXIS));
 48		setFloatable(false);
 49		add(Box.createHorizontalStrut(2));
 50
 51		JLabel label = new JLabel(jEdit.getProperty("view.action.prompt"));
 52		add(label);
 53		add(Box.createHorizontalStrut(12));
 54		add(action = new ActionTextField());
 55		action.setEnterAddsToHistory(false);
 56		Dimension max = action.getPreferredSize();
 57		max.width = Integer.MAX_VALUE;
 58		action.setMaximumSize(max);
 59		action.addActionListener(new ActionHandler());
 60		action.getDocument().addDocumentListener(new DocumentHandler());
 61
 62		if(temp)
 63		{
 64			close = new RolloverButton(GUIUtilities.loadIcon("closebox.gif"));
 65			close.addActionListener(new ActionHandler());
 66			close.setToolTipText(jEdit.getProperty(
 67				"view.action.close-tooltip"));
 68			add(close);
 69		}
 70
 71		// if 'temp' is true, hide search bar after user is done with it
 72		this.temp = temp;
 73	} //}}}
 74
 75	//{{{ getField() method
 76	public HistoryTextField getField()
 77	{
 78		return action;
 79	} //}}}
 80
 81	//{{{ goToActionBar() method
 82	public void goToActionBar()
 83	{
 84		repeatCount = view.getInputHandler().getRepeatCount();
 85		action.setText(null);
 86		action.requestFocus();
 87	} //}}}
 88
 89	//{{{ Private members
 90
 91	private static NameSpace namespace = new NameSpace(
 92		BeanShell.getNameSpace(),"action bar namespace");
 93
 94	//{{{ Instance variables
 95	private View view;
 96	private boolean temp;
 97	private int repeatCount;
 98	private HistoryTextField action;
 99	private CompletionPopup popup;
100	private RolloverButton close;
101	//}}}
102
103	//{{{ invoke() method
104	private void invoke()
105	{
106		String cmd;
107		if(popup != null)
108			cmd = popup.list.getSelectedValue().toString();
109		else
110		{
111			cmd = action.getText().trim();
112			int index = cmd.indexOf('=');
113			if(index != -1)
114			{
115				action.addCurrentToHistory();
116				String propName = cmd.substring(0,index).trim();
117				String propValue = cmd.substring(index + 1).trim();
118				String code;
119				/* construct a BeanShell snippet instead of
120				 * invoking directly so that user can record
121				 * property changes in macros. */
122				if(propName.startsWith("buffer."))
123				{
124					if(propName.equals("buffer.mode"))
125					{
126						code = "buffer.setMode(\""
127							+ StandardUtilities.charsToEscapes(
128							propValue) + "\");";
129					}
130					else
131					{
132						code = "buffer.setStringProperty(\""
133							+ StandardUtilities.charsToEscapes(
134							propName.substring("buffer.".length())
135							) + "\",\""
136							+ StandardUtilities.charsToEscapes(
137							propValue) + "\");";
138					}
139
140					code += "\nbuffer.propertiesChanged();";
141				}
142				else if(propName.startsWith("!buffer."))
143				{
144					code = "jEdit.setProperty(\""
145						+ StandardUtilities.charsToEscapes(
146						propName.substring(1)) + "\",\""
147						+ StandardUtilities.charsToEscapes(
148						propValue) + "\");\n"
149						+ "jEdit.propertiesChanged();";
150				}
151				else
152				{
153					code = "jEdit.setProperty(\""
154						+ StandardUtilities.charsToEscapes(
155						propName) + "\",\""
156						+ StandardUtilities.charsToEscapes(
157						propValue) + "\");\n"
158						+ "jEdit.propertiesChanged();";
159				}
160
161				Macros.Recorder recorder = view.getMacroRecorder();
162				if(recorder != null)
163					recorder.record(code);
164				BeanShell.eval(view,namespace,code);
165				cmd = null;
166			}
167			else if(cmd.length() != 0)
168			{
169				String[] completions = getCompletions(cmd);
170				if(completions.length != 0)
171				{
172					cmd = completions[0];
173				}
174			}
175			else
176				cmd = null;
177		}
178
179		if(popup != null)
180		{
181			popup.dispose();
182			popup = null;
183		}
184
185		final String finalCmd = cmd;
186		final EditAction act = (finalCmd == null ? null : jEdit.getAction(finalCmd));
187		if(temp)
188			view.removeToolBar(this);
189
190		SwingUtilities.invokeLater(new Runnable()
191		{
192			public void run()
193			{
194				view.getTextArea().requestFocus();
195				if(act == null)
196				{
197					if(finalCmd != null)
198					{
199						view.getStatus().setMessageAndClear(
200							jEdit.getProperty(
201							"view.action.no-completions"));
202					}
203				}
204				else
205				{
206					view.getInputHandler().setRepeatCount(repeatCount);
207					view.getInputHandler().invokeAction(act);
208				}
209			}
210		});
211	} //}}}
212
213	//{{{ getCompletions() method
214	private static String[] getCompletions(String str)
215	{
216		str = str.toLowerCase();
217		String[] actions = jEdit.getActionNames();
218		ArrayList<String> returnValue = new ArrayList<String>(actions.length);
219		for(int i = 0; i < actions.length; i++)
220		{
221			if(actions[i].toLowerCase().contains(str))
222				returnValue.add(actions[i]);
223		}
224
225		return returnValue.toArray(new String[returnValue.size()]);
226	} //}}}
227
228	//{{{ complete() method
229	private void complete(boolean insertLongestPrefix)
230	{
231		String text = action.getText().trim();
232		String[] completions = getCompletions(text);
233		if(completions.length == 1)
234		{
235			if(insertLongestPrefix)
236				action.setText(completions[0]);
237		}
238		else if(completions.length != 0)
239		{
240			if(insertLongestPrefix)
241			{
242				String prefix = MiscUtilities.getLongestPrefix(
243					completions,true);
244				if(prefix.contains(text))
245					action.setText(prefix);
246			}
247
248			if(popup != null)
249				popup.setModel(completions);
250			else
251				popup = new CompletionPopup(completions);
252			return;
253		}
254
255		if(popup != null)
256		{
257			popup.dispose();
258			popup = null;
259		}
260	} //}}}
261
262	//}}}
263
264	//{{{ Inner classes
265
266	//{{{ ActionHandler class
267	private class ActionHandler implements ActionListener
268	{
269		public void actionPerformed(ActionEvent evt)
270		{
271			if(evt.getSource() == close)
272				view.removeToolBar(ActionBar.this);
273			else
274				invoke();
275		}
276	} //}}}
277
278	//{{{ DocumentHandler class
279	private class DocumentHandler implements DocumentListener
280	{
281		//{{{ insertUpdate() method
282		public void insertUpdate(DocumentEvent evt)
283		{
284			if(popup != null)
285				complete(false);
286		} //}}}
287
288		//{{{ removeUpdate() method
289		public void removeUpdate(DocumentEvent evt)
290		{
291			if(popup != null)
292				complete(false);
293		} //}}}
294
295		//{{{ changedUpdate() method
296		public void changedUpdate(DocumentEvent evt) {}
297		//}}}
298	} //}}}
299
300	//{{{ ActionTextField class
301	private class ActionTextField extends HistoryTextField
302	{
303		boolean repeat;
304		boolean nonDigit;
305
306		ActionTextField()
307		{
308			super("action");
309			setSelectAllOnFocus(true);
310		}
311
312		@Override
313		public boolean getFocusTraversalKeysEnabled()
314		{
315			return false;
316		}
317
318		@Override
319		public void processKeyEvent(KeyEvent evt)
320		{
321			evt = KeyEventWorkaround.processKeyEvent(evt);
322			if(evt == null)
323				return;
324
325			switch(evt.getID())
326			{
327			case KeyEvent.KEY_TYPED:
328				char ch = evt.getKeyChar();
329				if(!nonDigit && Character.isDigit(ch))
330				{
331					super.processKeyEvent(evt);
332					repeat = true;
333					repeatCount = Integer.parseInt(action.getText());
334				}
335				else
336				{
337					nonDigit = true;
338					if(repeat)
339					{
340						passToView(evt);
341					}
342					else
343						super.processKeyEvent(evt);
344				}
345				break;
346			case KeyEvent.KEY_PRESSED:
347				int keyCode = evt.getKeyCode();
348				if(evt.isActionKey()
349					|| evt.isControlDown()
350					|| evt.isAltDown()
351					|| evt.isMetaDown()
352					|| keyCode == KeyEvent.VK_BACK_SPACE
353					|| keyCode == KeyEvent.VK_DELETE
354					|| keyCode == KeyEvent.VK_ENTER
355					|| keyCode == KeyEvent.VK_TAB
356					|| keyCode == KeyEvent.VK_ESCAPE)
357				{
358					nonDigit = true;
359					if(repeat)
360					{
361						passToView(evt);
362						break;
363					}
364					else if(keyCode == KeyEvent.VK_TAB)
365					{
366						complete(true);
367						evt.consume();
368					}
369					else if(keyCode == KeyEvent.VK_ESCAPE)
370					{
371						evt.consume();
372						if(popup != null)
373						{
374							popup.dispose();
375							popup = null;
376							action.requestFocus();
377						}
378						else
379						{
380							if(temp)
381								view.removeToolBar(ActionBar.this);
382							view.getEditPane().focusOnTextArea();
383						}
384						break;
385					}
386					else if((keyCode == KeyEvent.VK_UP
387						|| keyCode == KeyEvent.VK_DOWN)
388						&& popup != null)
389					{
390						popup.list.processKeyEvent(evt);
391						break;
392					}
393				}
394				super.processKeyEvent(evt);
395				break;
396			}
397		}
398
399		private void passToView(final KeyEvent evt)
400		{
401			if(temp)
402				view.removeToolBar(ActionBar.this);
403			view.getTextArea().requestFocus();
404			SwingUtilities.invokeLater(new Runnable()
405			{
406				public void run()
407				{
408					view.getTextArea().requestFocus();
409					view.getInputHandler().setRepeatCount(repeatCount);
410					view.getInputHandler().processKeyEvent(evt,
411						View.ACTION_BAR, false);
412				}
413			});
414		}
415
416		@Override
417		public void addNotify()
418		{
419			super.addNotify();
420			repeat = nonDigit = false;
421		}
422	} //}}}
423
424	//{{{ CompletionPopup class
425	private class CompletionPopup extends JWindow
426	{
427		CompletionList list;
428
429		//{{{ CompletionPopup constructor
430		CompletionPopup(String[] actions)
431		{
432			super(view);
433
434			setContentPane(new JPanel(new BorderLayout())
435			{
436				/**
437				 * Makes the tab key work in Java 1.4.
438				 */
439				@Override
440				public boolean getFocusTraversalKeysEnabled()
441				{
442					return false;
443				}
444			});
445
446			list = new CompletionList(actions);
447			list.setVisibleRowCount(8);
448			list.addMouseListener(new MouseHandler());
449			list.setSelectedIndex(0);
450			list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
451
452			// stupid scrollbar policy is an attempt to work around
453			// bugs people have been seeing with IBM's JDK -- 7 Sep 2000
454			JScrollPane scroller = new JScrollPane(list,
455				ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
456				ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
457
458			getContentPane().add(scroller, BorderLayout.CENTER);
459
460			GUIUtilities.requestFocus(this,list);
461
462			pack();
463			Point p = new Point(0,-getHeight());
464			SwingUtilities.convertPointToScreen(p,action);
465			setLocation(p);
466			setVisible(true);
467
468			KeyHandler keyHandler = new KeyHandler();
469			addKeyListener(keyHandler);
470			list.addKeyListener(keyHandler);
471		} //}}}
472
473		//{{{ setModel() method
474		void setModel(String[] actions)
475		{
476			list.setListData(actions);
477			list.setSelectedIndex(0);
478		} //}}}
479
480		//{{{ MouseHandler class
481		private class MouseHandler extends MouseAdapter
482		{
483			@Override
484			public void mouseClicked(MouseEvent evt)
485			{
486				invoke();
487			}
488		} //}}}
489
490		//{{{ CompletionList class
491		class CompletionList extends JList
492		{
493			CompletionList(Object[] data)
494			{
495				super(data);
496			}
497
498			// we need this public not protected
499			@Override
500			public void processKeyEvent(KeyEvent evt)
501			{
502				super.processKeyEvent(evt);
503			}
504		} //}}}
505
506		//{{{ KeyHandler class
507		private class KeyHandler extends KeyAdapter
508		{
509			@Override
510			public void keyTyped(KeyEvent evt)
511			{
512				action.processKeyEvent(evt);
513			}
514
515			@Override
516			public void keyPressed(KeyEvent evt)
517			{
518				int keyCode = evt.getKeyCode();
519				if(keyCode == KeyEvent.VK_ESCAPE)
520					action.processKeyEvent(evt);
521				else if(keyCode == KeyEvent.VK_ENTER)
522					invoke();
523				else if(keyCode == KeyEvent.VK_UP)
524				{
525					int selected = list.getSelectedIndex();
526					if(selected == 0)
527					{
528						list.setSelectedIndex(
529							list.getModel().getSize()
530							- 1);
531						evt.consume();
532					}
533				}
534				else if(keyCode == KeyEvent.VK_DOWN)
535				{
536					int selected = list.getSelectedIndex();
537					if(selected == list.getModel().getSize() - 1)
538					{
539						list.setSelectedIndex(0);
540						evt.consume();
541					}
542				}
543			}
544		} //}}}
545	} //}}}
546
547	//}}}
548}