001: /*
002: * ActionBar.java - For invoking actions directly
003: * :tabSize=8:indentSize=8:noTabs=false:
004: * :folding=explicit:collapseFolds=1:
005: *
006: * Copyright (C) 2003 Slava Pestov
007: *
008: * This program is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU General Public License
010: * as published by the Free Software Foundation; either version 2
011: * of the License, or any later version.
012: *
013: * This program is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016: * GNU General Public License for more details.
017: *
018: * You should have received a copy of the GNU General Public License
019: * along with this program; if not, write to the Free Software
020: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
021: */
022:
023: package org.gjt.sp.jedit.gui;
024:
025: //{{{ Imports
026: import org.gjt.sp.jedit.bsh.NameSpace;
027: import java.awt.event.*;
028: import java.awt.*;
029: import java.util.ArrayList;
030: import javax.swing.event.*;
031: import javax.swing.*;
032: import org.gjt.sp.jedit.*;
033:
034: //}}}
035:
036: /**
037: * Action invocation bar.
038: */
039: public class ActionBar extends JPanel {
040: //{{{ ActionBar constructor
041: public ActionBar(View view, boolean temp) {
042: setLayout(new BoxLayout(this , BoxLayout.X_AXIS));
043:
044: this .view = view;
045: this .temp = temp;
046:
047: add(Box.createHorizontalStrut(2));
048:
049: JLabel label = new JLabel(jEdit
050: .getProperty("view.action.prompt"));
051: add(label);
052: add(Box.createHorizontalStrut(12));
053: add(action = new ActionTextField());
054: action.setEnterAddsToHistory(false);
055: Dimension max = action.getPreferredSize();
056: max.width = Integer.MAX_VALUE;
057: action.setMaximumSize(max);
058: action.addActionListener(new ActionHandler());
059: action.getDocument().addDocumentListener(new DocumentHandler());
060:
061: if (temp) {
062: close = new RolloverButton(GUIUtilities
063: .loadIcon("closebox.gif"));
064: close.addActionListener(new ActionHandler());
065: close.setToolTipText(jEdit
066: .getProperty("view.action.close-tooltip"));
067: add(close);
068: }
069:
070: // if 'temp' is true, hide search bar after user is done with it
071: this .temp = temp;
072: } //}}}
073:
074: //{{{ getField() method
075: public HistoryTextField getField() {
076: return action;
077: } //}}}
078:
079: //{{{ goToActionBar() method
080: public void goToActionBar() {
081: repeatCount = view.getInputHandler().getRepeatCount();
082: action.setText(null);
083: action.requestFocus();
084: } //}}}
085:
086: //{{{ Private members
087:
088: private static NameSpace namespace = new NameSpace(BeanShell
089: .getNameSpace(), "action bar namespace");
090:
091: //{{{ Instance variables
092: private View view;
093: private boolean temp;
094: private int repeatCount;
095: private HistoryTextField action;
096: private CompletionPopup popup;
097: private RolloverButton close;
098:
099: //}}}
100:
101: //{{{ invoke() method
102: private void invoke() {
103: String cmd;
104: if (popup != null)
105: cmd = popup.list.getSelectedValue().toString();
106: else {
107: cmd = action.getText().trim();
108: int index = cmd.indexOf('=');
109: if (index != -1) {
110: action.addCurrentToHistory();
111: String propName = cmd.substring(0, index).trim();
112: String propValue = cmd.substring(index + 1).trim();
113: String code;
114: /* construct a BeanShell snippet instead of
115: * invoking directly so that user can record
116: * property changes in macros. */
117: if (propName.startsWith("buffer.")) {
118: if (propName.equals("buffer.mode")) {
119: code = "buffer.setMode(\""
120: + MiscUtilities
121: .charsToEscapes(propValue)
122: + "\");";
123: } else {
124: code = "buffer.setStringProperty(\""
125: + MiscUtilities.charsToEscapes(propName
126: .substring("buffer.".length()))
127: + "\",\""
128: + MiscUtilities
129: .charsToEscapes(propValue)
130: + "\");";
131: }
132:
133: code += "\nbuffer.propertiesChanged();";
134: } else if (propName.startsWith("!buffer.")) {
135: code = "jEdit.setProperty(\""
136: + MiscUtilities.charsToEscapes(propName
137: .substring(1)) + "\",\""
138: + MiscUtilities.charsToEscapes(propValue)
139: + "\");\n" + "jEdit.propertiesChanged();";
140: } else {
141: code = "jEdit.setProperty(\""
142: + MiscUtilities.charsToEscapes(propName)
143: + "\",\""
144: + MiscUtilities.charsToEscapes(propValue)
145: + "\");\n" + "jEdit.propertiesChanged();";
146: }
147:
148: Macros.Recorder recorder = view.getMacroRecorder();
149: if (recorder != null)
150: recorder.record(code);
151: BeanShell.eval(view, namespace, code);
152: cmd = null;
153: } else if (cmd.length() != 0) {
154: String[] completions = getCompletions(cmd);
155: if (completions.length != 0) {
156: cmd = completions[0];
157: }
158: } else
159: cmd = null;
160: }
161:
162: if (popup != null) {
163: popup.dispose();
164: popup = null;
165: }
166:
167: final String finalCmd = cmd;
168: final EditAction act = (finalCmd == null ? null : jEdit
169: .getAction(finalCmd));
170: if (temp)
171: view.removeToolBar(this );
172:
173: SwingUtilities.invokeLater(new Runnable() {
174: public void run() {
175: view.getTextArea().requestFocus();
176: if (act == null) {
177: if (finalCmd != null) {
178: view
179: .getStatus()
180: .setMessageAndClear(
181: jEdit
182: .getProperty("view.action.no-completions"));
183: }
184: } else {
185: view.getInputHandler().setRepeatCount(repeatCount);
186: view.getInputHandler().invokeAction(act);
187: }
188: }
189: });
190: } //}}}
191:
192: //{{{ getCompletions() method
193: private String[] getCompletions(String str) {
194: str = str.toLowerCase();
195: String[] actions = jEdit.getActionNames();
196: ArrayList<String> returnValue = new ArrayList<String>(
197: actions.length);
198: for (int i = 0; i < actions.length; i++) {
199: if (actions[i].toLowerCase().contains(str))
200: returnValue.add(actions[i]);
201: }
202:
203: return returnValue.toArray(new String[returnValue.size()]);
204: } //}}}
205:
206: //{{{ complete() method
207: private void complete(boolean insertLongestPrefix) {
208: String text = action.getText().trim();
209: String[] completions = getCompletions(text);
210: if (completions.length == 1) {
211: if (insertLongestPrefix)
212: action.setText(completions[0]);
213: } else if (completions.length != 0) {
214: if (insertLongestPrefix) {
215: String prefix = MiscUtilities.getLongestPrefix(
216: completions, true);
217: if (prefix.contains(text))
218: action.setText(prefix);
219: }
220:
221: if (popup != null)
222: popup.setModel(completions);
223: else
224: popup = new CompletionPopup(completions);
225: return;
226: }
227:
228: if (popup != null) {
229: popup.dispose();
230: popup = null;
231: }
232: } //}}}
233:
234: //}}}
235:
236: //{{{ Inner classes
237:
238: //{{{ ActionHandler class
239: class ActionHandler implements ActionListener {
240: public void actionPerformed(ActionEvent evt) {
241: if (evt.getSource() == close)
242: view.removeToolBar(ActionBar.this );
243: else
244: invoke();
245: }
246: } //}}}
247:
248: //{{{ DocumentHandler class
249: class DocumentHandler implements DocumentListener {
250: //{{{ insertUpdate() method
251: public void insertUpdate(DocumentEvent evt) {
252: if (popup != null)
253: complete(false);
254: } //}}}
255:
256: //{{{ removeUpdate() method
257: public void removeUpdate(DocumentEvent evt) {
258: if (popup != null)
259: complete(false);
260: } //}}}
261:
262: //{{{ changedUpdate() method
263: public void changedUpdate(DocumentEvent evt) {
264: }
265: //}}}
266: } //}}}
267:
268: //{{{ ActionTextField class
269: class ActionTextField extends HistoryTextField {
270: boolean repeat;
271: boolean nonDigit;
272:
273: ActionTextField() {
274: super ("action");
275: setSelectAllOnFocus(true);
276: }
277:
278: public boolean isManagingFocus() {
279: return false;
280: }
281:
282: public boolean getFocusTraversalKeysEnabled() {
283: return false;
284: }
285:
286: public void processKeyEvent(KeyEvent evt) {
287: evt = KeyEventWorkaround.processKeyEvent(evt);
288: if (evt == null)
289: return;
290:
291: switch (evt.getID()) {
292: case KeyEvent.KEY_TYPED:
293: char ch = evt.getKeyChar();
294: if (!nonDigit && Character.isDigit(ch)) {
295: super .processKeyEvent(evt);
296: repeat = true;
297: repeatCount = Integer.parseInt(action.getText());
298: } else {
299: nonDigit = true;
300: if (repeat) {
301: passToView(evt);
302: } else
303: super .processKeyEvent(evt);
304: }
305: break;
306: case KeyEvent.KEY_PRESSED:
307: int keyCode = evt.getKeyCode();
308: if (evt.isActionKey() || evt.isControlDown()
309: || evt.isAltDown() || evt.isMetaDown()
310: || keyCode == KeyEvent.VK_BACK_SPACE
311: || keyCode == KeyEvent.VK_DELETE
312: || keyCode == KeyEvent.VK_ENTER
313: || keyCode == KeyEvent.VK_TAB
314: || keyCode == KeyEvent.VK_ESCAPE) {
315: nonDigit = true;
316: if (repeat) {
317: passToView(evt);
318: break;
319: } else if (keyCode == KeyEvent.VK_TAB) {
320: complete(true);
321: evt.consume();
322: } else if (keyCode == KeyEvent.VK_ESCAPE) {
323: evt.consume();
324: if (popup != null) {
325: popup.dispose();
326: popup = null;
327: action.requestFocus();
328: } else {
329: if (temp)
330: view.removeToolBar(ActionBar.this );
331: view.getEditPane().focusOnTextArea();
332: }
333: break;
334: } else if ((keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_DOWN)
335: && popup != null) {
336: popup.list.processKeyEvent(evt);
337: break;
338: }
339: }
340: super .processKeyEvent(evt);
341: break;
342: }
343: }
344:
345: private void passToView(final KeyEvent evt) {
346: if (temp)
347: view.removeToolBar(ActionBar.this );
348: view.getTextArea().requestFocus();
349: SwingUtilities.invokeLater(new Runnable() {
350: public void run() {
351: view.getTextArea().requestFocus();
352: view.getInputHandler().setRepeatCount(repeatCount);
353: view.getInputHandler().processKeyEvent(evt,
354: View.ACTION_BAR, false);
355: }
356: });
357: }
358:
359: public void addNotify() {
360: super .addNotify();
361: repeat = nonDigit = false;
362: }
363: } //}}}
364:
365: //{{{ CompletionPopup class
366: class CompletionPopup extends JWindow {
367: CompletionList list;
368:
369: //{{{ CompletionPopup constructor
370: CompletionPopup(String[] actions) {
371: super (view);
372:
373: setContentPane(new JPanel(new BorderLayout()) {
374: /**
375: * Returns if this component can be traversed by pressing the
376: * Tab key. This returns false.
377: */
378: public boolean isManagingFocus() {
379: return false;
380: }
381:
382: /**
383: * Makes the tab key work in Java 1.4.
384: */
385: public boolean getFocusTraversalKeysEnabled() {
386: return false;
387: }
388: });
389:
390: list = new CompletionList(actions);
391: list.setVisibleRowCount(8);
392: list.addMouseListener(new MouseHandler());
393: list.setSelectedIndex(0);
394: list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
395:
396: // stupid scrollbar policy is an attempt to work around
397: // bugs people have been seeing with IBM's JDK -- 7 Sep 2000
398: JScrollPane scroller = new JScrollPane(list,
399: ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
400: ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
401:
402: getContentPane().add(scroller, BorderLayout.CENTER);
403:
404: GUIUtilities.requestFocus(this , list);
405:
406: pack();
407: Point p = new Point(0, -getHeight());
408: SwingUtilities.convertPointToScreen(p, action);
409: setLocation(p);
410: setVisible(true);
411:
412: KeyHandler keyHandler = new KeyHandler();
413: addKeyListener(keyHandler);
414: list.addKeyListener(keyHandler);
415: } //}}}
416:
417: //{{{ setModel() method
418: void setModel(String[] actions) {
419: list.setListData(actions);
420: list.setSelectedIndex(0);
421: } //}}}
422:
423: //{{{ MouseHandler class
424: class MouseHandler extends MouseAdapter {
425: public void mouseClicked(MouseEvent evt) {
426: invoke();
427: }
428: } //}}}
429:
430: //{{{ CompletionList class
431: class CompletionList extends JList {
432: CompletionList(Object[] data) {
433: super (data);
434: }
435:
436: // we need this public not protected
437: public void processKeyEvent(KeyEvent evt) {
438: super .processKeyEvent(evt);
439: }
440: } //}}}
441:
442: //{{{ KeyHandler class
443: class KeyHandler extends KeyAdapter {
444: public void keyTyped(KeyEvent evt) {
445: action.processKeyEvent(evt);
446: }
447:
448: public void keyPressed(KeyEvent evt) {
449: int keyCode = evt.getKeyCode();
450: if (keyCode == KeyEvent.VK_ESCAPE)
451: action.processKeyEvent(evt);
452: else if (keyCode == KeyEvent.VK_ENTER)
453: invoke();
454: else if (keyCode == KeyEvent.VK_UP) {
455: int selected = list.getSelectedIndex();
456: if (selected == 0) {
457: list
458: .setSelectedIndex(list.getModel()
459: .getSize() - 1);
460: evt.consume();
461: }
462: } else if (keyCode == KeyEvent.VK_DOWN) {
463: int selected = list.getSelectedIndex();
464: if (selected == list.getModel().getSize() - 1) {
465: list.setSelectedIndex(0);
466: evt.consume();
467: }
468: }
469: }
470: } //}}}
471: } //}}}
472:
473: //}}}
474: }
|