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