KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > rice > cs > drjava > ui > InteractionsController


1 /*BEGIN_COPYRIGHT_BLOCK
2  *
3  * This file is part of DrJava. Download the current version of this project from http://www.drjava.org/
4  * or http://sourceforge.net/projects/drjava/
5  *
6  * DrJava Open Source License
7  *
8  * Copyright (C) 2001-2005 JavaPLT group at Rice University (javaplt@rice.edu). All rights reserved.
9  *
10  * Developed by: Java Programming Languages Team, Rice University, http://www.cs.rice.edu/~javaplt/
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
13  * documentation files (the "Software"), to deal with the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
15  * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
16  *
17  * - Redistributions of source code must retain the above copyright notice, this list of conditions and the
18  * following disclaimers.
19  * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
20  * following disclaimers in the documentation and/or other materials provided with the distribution.
21  * - Neither the names of DrJava, the JavaPLT, Rice University, nor the names of its contributors may be used to
22  * endorse or promote products derived from this Software without specific prior written permission.
23  * - Products derived from this software may not be called "DrJava" nor use the term "DrJava" as part of their
24  * names without prior written permission from the JavaPLT group. For permission, write to javaplt@rice.edu.
25  *
26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
27  * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28  * CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
29  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
30  * WITH THE SOFTWARE.
31  *
32 END_COPYRIGHT_BLOCK*/

33
34 package edu.rice.cs.drjava.ui;
35
36 import javax.swing.AbstractAction JavaDoc;
37 import javax.swing.BorderFactory JavaDoc;
38 import javax.swing.Action JavaDoc;
39 import javax.swing.ActionMap JavaDoc;
40 import javax.swing.InputMap JavaDoc;
41 import javax.swing.JTextArea JavaDoc;
42 import javax.swing.KeyStroke JavaDoc;
43 import javax.swing.SwingUtilities JavaDoc;
44
45 import javax.swing.border.Border JavaDoc;
46
47 import javax.swing.text.AttributeSet JavaDoc;
48 import javax.swing.text.BadLocationException JavaDoc;
49 import javax.swing.text.DefaultEditorKit JavaDoc;
50 import javax.swing.text.SimpleAttributeSet JavaDoc;
51 import javax.swing.text.StyleConstants JavaDoc;
52 import javax.swing.text.DefaultStyledDocument JavaDoc;
53
54 import java.awt.event.ActionEvent JavaDoc;
55 import java.awt.event.KeyEvent JavaDoc;
56 import java.awt.Toolkit JavaDoc;
57 import java.awt.Color JavaDoc;
58 import java.awt.RenderingHints JavaDoc;
59 import java.awt.Graphics JavaDoc;
60 import java.awt.Graphics2D JavaDoc;
61
62 import java.io.File JavaDoc;
63
64 import java.util.EventListener JavaDoc;
65 import java.util.Vector JavaDoc;
66
67 import edu.rice.cs.drjava.DrJava;
68 import edu.rice.cs.drjava.config.OptionConstants;
69 import edu.rice.cs.drjava.config.OptionListener;
70 import edu.rice.cs.drjava.config.OptionEvent;
71
72 import edu.rice.cs.drjava.model.repl.InputListener;
73 import edu.rice.cs.drjava.model.repl.InteractionsDocument;
74 import edu.rice.cs.drjava.model.repl.InteractionsDJDocument;
75 import edu.rice.cs.drjava.model.repl.InteractionsListener;
76 import edu.rice.cs.drjava.model.repl.InteractionsModel;
77
78 import edu.rice.cs.util.swing.Utilities;
79 import edu.rice.cs.util.text.ConsoleDocument;
80 import edu.rice.cs.util.CompletionMonitor;
81 import edu.rice.cs.util.Lambda;
82 import edu.rice.cs.util.UnexpectedException;
83
84 /** This class installs listeners and actions between an InteractionsDocument in the model and an InteractionsPane
85  * in the view. We may want to refactor this class into a different package. <p>
86  * (The PopupConsole was introduced in version 1.29 of this file)
87  *
88  * @version $Id: InteractionsController.java 4033 2006-11-16 20:16:51Z rcartwright $
89  */

90 public class InteractionsController extends AbstractConsoleController {
91   
92   private static final String JavaDoc INPUT_ENTERED_NAME = "Input Entered";
93   private static final String JavaDoc INSERT_NEWLINE_NAME = "Insert Newline";
94
95   /** Style for System.in box */
96   public static final String JavaDoc INPUT_BOX_STYLE = "input.box.style";
97   
98   /** The symbol used in the document for the input box. */
99   public static final String JavaDoc INPUT_BOX_SYMBOL = "[component]";
100   
101   /** InteractionsModel to handle interpretation. */
102   private InteractionsModel _model;
103
104   /** Document from the model.*/
105   private InteractionsDocument _doc;
106
107   /** Style to use for error messages. */
108   private SimpleAttributeSet JavaDoc _errStyle;
109
110   /** Style to use for debug messages. */
111   private final SimpleAttributeSet JavaDoc _debugStyle;
112
113   /** Lambda used to input text into the embedded System.in input box */
114   private Lambda<String JavaDoc, String JavaDoc> _insertTextCommand;
115   
116   /**
117    * Runnable command used to force the System.in input to complete <p>
118    *
119    * <b>NOTE:</b> This command must be executed on swing's event handling thread.
120    */

121   private Runnable JavaDoc _inputCompletionCommand;
122     
123   /**
124    * A lock used to ensure that the _insertTextCommand and
125    * _inputCompletionCommand are set synchronously
126    */

127   private Object JavaDoc _consoleInputCommandLock = new Object JavaDoc();
128   
129   /** Default implementation of the insert text command */
130   private static final Lambda<String JavaDoc, String JavaDoc> _defaultInsertTextCommand =
131     new Lambda<String JavaDoc,String JavaDoc>() {
132       public String JavaDoc apply(String JavaDoc input) {
133         throw new UnsupportedOperationException JavaDoc("Cannot insert text. There is no console input in progress");
134       }
135     };
136   
137   /** Default implementation of the input completion command */
138   private static final Runnable JavaDoc _defaultInputCompletionCommand =
139     new Runnable JavaDoc() {
140       public void run() {
141         // Do nothing
142
}
143     };
144   
145   /** Listens for input requests from System.in, displaying an input box as needed. */
146   protected InputListener _inputListener = new InputListener() {
147     public String JavaDoc getConsoleInput() {
148       
149       final InputBox box = new InputBox();
150       final CompletionMonitor completionMonitor = new CompletionMonitor();
151       
152       Runnable JavaDoc inputCompletionCommand = new Runnable JavaDoc() {
153         public void run() {
154           // Reset the commands to their default inactive state
155
_setConsoleInputCommands(_defaultInputCompletionCommand, _defaultInsertTextCommand);
156           
157           box.dissableInputs();
158                     
159           completionMonitor.set();
160           
161           // Move the cursor back to the end of the interactions pane
162
_pane.setEditable(true);
163           _pane.setCaretPosition(_doc.getLength());
164           _pane.requestFocus();
165         }
166       };
167       
168       Lambda<String JavaDoc,String JavaDoc> insertTextCommand = box.makeInsertTextCommand();
169       
170       box.setInputCompletionCommand(inputCompletionCommand);
171       
172       _setConsoleInputCommands(inputCompletionCommand, insertTextCommand);
173       
174       // Embed the input box into the interactions pane.
175
// This operation must be performed in the UI thread
176
SwingUtilities.invokeLater(new Runnable JavaDoc() {
177         public void run() {
178           
179           _pane.setEditable(true);
180           
181           int pos = _doc.getPositionBeforePrompt();
182           _doc.insertBeforeLastPrompt(" ", _doc.DEFAULT_STYLE);
183            
184           javax.swing.text.MutableAttributeSet JavaDoc inputAttributes = _pane.getInputAttributes();
185           inputAttributes.removeAttributes(inputAttributes);
186           StyleConstants.setComponent(inputAttributes, box);
187           try {
188             DefaultStyledDocument.ElementSpec JavaDoc[] specs = new DefaultStyledDocument.ElementSpec JavaDoc[]{
189               new DefaultStyledDocument.ElementSpec JavaDoc(inputAttributes, DefaultStyledDocument.ElementSpec.ContentType, INPUT_BOX_SYMBOL.toCharArray(), 0, 11)
190             };
191             
192             // update input box style
193
_adapter.setDocStyle(INPUT_BOX_STYLE, inputAttributes);
194             
195             // and insert the symbol for the input box with the correct style
196
_doc.insertBeforeLastPrompt(INPUT_BOX_SYMBOL, INPUT_BOX_STYLE);
197           }
198           finally {
199             inputAttributes.removeAttributes(inputAttributes);
200           }
201           
202           _doc.insertBeforeLastPrompt("\n", _doc.DEFAULT_STYLE);
203           
204           box.setVisible(true);
205           box.requestFocus();
206
207           _pane.setEditable(false);
208         }
209       });
210       fireConsoleInputStarted();
211       
212       // Wait for the inputCompletionCommand to be invoked
213
completionMonitor.waitOne();
214             
215       String JavaDoc text = box.getText() + "\n";
216       fireConsoleInputCompleted(text);
217       
218       return text;
219     }
220   };
221   
222   private Vector JavaDoc<ConsoleStateListener> _consoleStateListeners;
223   
224   private InteractionsListener _viewListener = new InteractionsListener() {
225     public void interactionStarted() { }
226     public void interactionEnded() { }
227     public void interactionErrorOccurred(int offset, int length) { }
228     
229     public void interpreterResetting() {
230       Runnable JavaDoc command = new Runnable JavaDoc() {
231         public void run() {
232           _adapter.clearColoring();
233           _pane.resetPrompts();
234         }
235       };
236       Utilities.invokeLater(command);
237     }
238     
239     public void interpreterReady(File JavaDoc wd) { }
240     public void interpreterResetFailed(Throwable JavaDoc t) { }
241     public void interpreterExited(int status) { }
242     public void interpreterChanged(boolean inProgress) { }
243     public void interactionIncomplete() { }
244     public void slaveJVMUsed() { }
245   };
246
247   /** Glue together the given model and a new view.
248    * @param model An InteractionsModel
249    * @param adapter InteractionsDJDocument being used by the model's doc
250    */

251   public InteractionsController(final InteractionsModel model, InteractionsDJDocument adapter) {
252     this(model, adapter,
253          new InteractionsPane(adapter) {
254            public int getPromptPos() {
255              return model.getDocument().getPromptPos();
256            }
257          });
258   }
259
260   /** Glue together the given model and view.
261    * @param model An InteractionsModel
262    * @param adapter InteractionsDJDocument being used by the model's doc
263    * @param pane An InteractionsPane
264    */

265   public InteractionsController(InteractionsModel model, InteractionsDJDocument adapter,
266                                 InteractionsPane pane) {
267     super(adapter, pane);
268     DefaultEditorKit JavaDoc d = pane.EDITOR_KIT;
269     
270     for (Action JavaDoc a : d.getActions()) {
271       if (a.getValue(Action.NAME).equals(DefaultEditorKit.upAction)) defaultUpAction = a;
272       if (a.getValue(Action.NAME).equals(DefaultEditorKit.downAction)) defaultDownAction = a;
273     }
274     
275     _model = model;
276     _doc = model.getDocument();
277     _errStyle = new SimpleAttributeSet JavaDoc();
278     _debugStyle = new SimpleAttributeSet JavaDoc();
279
280     _model.setInputListener(_inputListener);
281     _model.addListener(_viewListener);
282     
283     _inputCompletionCommand = _defaultInputCompletionCommand;
284     _insertTextCommand = _defaultInsertTextCommand;
285     _consoleStateListeners = new Vector JavaDoc<ConsoleStateListener>();
286     
287     _init();
288   }
289   
290   public void addConsoleStateListener(ConsoleStateListener listener) {
291     _consoleStateListeners.add(listener);
292   }
293   
294   public void removeConsoleStateListener(ConsoleStateListener listener) {
295     _consoleStateListeners.remove(listener);
296   }
297   
298   private void fireConsoleInputStarted() {
299     for(ConsoleStateListener listener : _consoleStateListeners) {
300       listener.consoleInputStarted(this);
301     }
302   }
303   
304   private void fireConsoleInputCompleted(String JavaDoc text) {
305     for(ConsoleStateListener listener : _consoleStateListeners) { listener.consoleInputCompleted(text, this); }
306   }
307   
308   /** Gets the input listener for console input requests.
309    * @return the input listener for console input requests.
310    */

311   public InputListener getInputListener() { return _inputListener; }
312
313   /** Forces console input to complete without the user hitting <Enter>. Called by MainFrame when reset is called so
314     * that this lock is released. This method is thread safe.
315     * @throws UnsupportedOperationException If the interactions pane is not receiving console input
316     */

317   public void interruptConsoleInput() { SwingUtilities.invokeLater(_inputCompletionCommand); }
318   
319   /** Inserts text into the console. This method is thread safe.
320     * @param input The text to insert into the console input box
321     * @throws UnsupportedOperationException If the the interactions pane is not receiving console input
322     */

323   public void insertConsoleText(String JavaDoc input) {
324     synchronized(_consoleInputCommandLock) { _insertTextCommand.apply(input); }
325   }
326
327   /** Accessor method for the InteractionsModel.
328     * @return the interactions model
329     */

330   public InteractionsModel getInteractionsModel() { return _model; }
331
332   /**
333    * Allows the abstract superclass to use the document.
334    * @return the InteractionsDocument
335    */

336   public ConsoleDocument getConsoleDoc() { return _doc; }
337
338   /** Accessor method for the InteractionsDocument. */
339   public InteractionsDocument getDocument() { return _doc; }
340
341   /** Adds AttributeSets as named styles to the document adapter. */
342   protected void _addDocumentStyles() {
343     // Add AbstractConsoleController styles
344
super._addDocumentStyles();
345
346     // Error
347
_errStyle.addAttributes(_defaultStyle);
348     _errStyle.addAttribute(StyleConstants.Foreground,
349                            DrJava.getConfig().getSetting(OptionConstants.INTERACTIONS_ERROR_COLOR));
350     _errStyle.addAttribute(StyleConstants.Bold, Boolean.TRUE);
351     _adapter.setDocStyle(InteractionsDocument.ERROR_STYLE, _errStyle);
352     DrJava.getConfig().addOptionListener(OptionConstants.INTERACTIONS_ERROR_COLOR, new OptionListener<Color JavaDoc>() {
353       public void optionChanged(OptionEvent<Color JavaDoc> oe) {
354         _errStyle.addAttribute(StyleConstants.Foreground, oe.value);
355       }
356     });
357
358     // Debug
359
_debugStyle.addAttributes(_defaultStyle);
360     _debugStyle.addAttribute(StyleConstants.Foreground,
361                              DrJava.getConfig().getSetting(OptionConstants.DEBUG_MESSAGE_COLOR));
362     _debugStyle.addAttribute(StyleConstants.Bold, Boolean.TRUE);
363     _adapter.setDocStyle(InteractionsDocument.DEBUGGER_STYLE, _debugStyle);
364     DrJava.getConfig().addOptionListener(OptionConstants.DEBUG_MESSAGE_COLOR, new OptionListener<Color JavaDoc>() {
365       public void optionChanged(OptionEvent<Color JavaDoc> oe) {
366         _debugStyle.addAttribute(StyleConstants.Foreground, oe.value);
367       }
368     });
369   }
370
371   /** Updates all document styles with the attributes contained in newSet. This behavior is only used in Mac OS X,
372    * JDK 1.4.1, since setFont() works fine on JTextPane on all other tested platforms.
373    * @param newSet Style containing new attributes to use.
374    */

375   protected void _updateStyles(AttributeSet JavaDoc newSet) {
376     super._updateStyles(newSet);
377     _errStyle.addAttributes(newSet);
378     StyleConstants.setBold(_errStyle, true); // ensure err is always bold
379
_debugStyle.addAttributes(newSet);
380     StyleConstants.setBold(_debugStyle, true); // ensure debug is always bold
381
}
382
383   /** Adds listeners to the model. */
384   protected void _setupModel() {
385     _adapter.addDocumentListener(new CaretUpdateListener());
386     _doc.setBeep(_pane.getBeep());
387   }
388
389   /** Adds actions to the view. */
390   protected void _setupView() {
391     super._setupView();
392
393     // Get proper cross-platform mask.
394
int mask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
395
396     // Add actions with keystrokes
397
_pane.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), evalAction);
398     _pane.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, java.awt.Event.SHIFT_MASK), newLineAction);
399     _pane.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_B, mask), clearCurrentAction);
400
401     // Up and down need to be bound both for keypad and not
402
_pane.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_KP_UP, 0), moveUpAction);
403     _pane.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), moveUpAction);
404     _pane.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_UP, mask), historyPrevAction);
405     _pane.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_KP_DOWN, 0), moveDownAction);
406     _pane.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), moveDownAction);
407     _pane.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, mask), historyNextAction);
408     _pane.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), historyReverseSearchAction);
409     _pane.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, java.awt.Event.SHIFT_MASK),
410                                 historyForwardSearchAction);
411
412
413     // Left needs to be prevented from rolling cursor back before the prompt.
414
// Both left and right should lock when caret is before the prompt.
415
// Caret is allowed before the prompt for the purposes of mouse-based copy-
416
// and-paste.
417
_pane.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_KP_LEFT, 0), moveLeftAction);
418     _pane.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), moveLeftAction);
419     _pane.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_KP_RIGHT, 0), moveRightAction);
420     _pane.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), moveRightAction);
421
422     // Prevent previous word action from going past the prompt
423
_pane.addActionForKeyStroke(DrJava.getConfig().getSetting(OptionConstants.KEY_PREVIOUS_WORD), prevWordAction);
424     DrJava.getConfig().addOptionListener(OptionConstants.KEY_PREVIOUS_WORD, new OptionListener<KeyStroke JavaDoc>() {
425       public void optionChanged(OptionEvent<KeyStroke JavaDoc> oe) {
426         _pane.addActionForKeyStroke(DrJava.getConfig().getSetting(OptionConstants.KEY_PREVIOUS_WORD), prevWordAction);
427       }
428     });
429
430     _pane.addActionForKeyStroke(DrJava.getConfig().getSetting(OptionConstants.KEY_NEXT_WORD), nextWordAction);
431     DrJava.getConfig().addOptionListener(OptionConstants.KEY_NEXT_WORD, new OptionListener<KeyStroke JavaDoc>() {
432       public void optionChanged(OptionEvent<KeyStroke JavaDoc> oe) {
433         _pane.addActionForKeyStroke(DrJava.getConfig().getSetting(OptionConstants.KEY_NEXT_WORD), nextWordAction);
434       }
435     });
436   }
437   
438   /**
439    * Sets the commands used to manipulate the console input process. Since the console
440    * is accessed from multiple threads, all access to these commands is protected by
441    * the _consoleInputCommandLock.
442    */

443   private void _setConsoleInputCommands(Runnable JavaDoc inputCompletionCommand, Lambda<String JavaDoc,String JavaDoc> insertTextCommand) {
444     synchronized(_consoleInputCommandLock) {
445       _insertTextCommand = insertTextCommand;
446       _inputCompletionCommand = inputCompletionCommand;
447     }
448   }
449
450   // The fields below were made package private for testing purposes.
451

452   /** Evaluates the interaction on the current line. */
453   AbstractAction JavaDoc evalAction = new AbstractAction JavaDoc() {
454     public void actionPerformed(ActionEvent JavaDoc e) {
455       if (! _adapter.inCommentBlock()) {
456         Thread JavaDoc command = new Thread JavaDoc("Evaluating Interaction") {
457           public void run() { _model.interpretCurrentInteraction(); }
458         };
459         command.start();
460       }
461       else {
462         _model.addNewLine();
463         _model.interactionContinues();
464       }
465     }
466   };
467
468   /** Recalls the previous command from the history. */
469   AbstractAction JavaDoc historyPrevAction = new AbstractAction JavaDoc() {
470     public void actionPerformed(ActionEvent JavaDoc e) {
471       if (!_busy()) {
472         if (_doc.recallPreviousInteractionInHistory()) moveToEnd();
473         if (!_isCursorAfterPrompt()) moveToPrompt();
474       }
475     }
476   };
477
478   /** Recalls the next command from the history. */
479   AbstractAction JavaDoc historyNextAction = new AbstractAction JavaDoc() {
480     public void actionPerformed(ActionEvent JavaDoc e) {
481       if (!_busy()) {
482         if (_doc.recallNextInteractionInHistory() || !_isCursorAfterPrompt()) moveToPrompt();
483       }
484     }
485   };
486   
487   /** Added feature for up. If the cursor is on the first line of the current interaction, it goes into the history.
488    * Otherwise, stays within the current interaction
489    */

490   AbstractAction JavaDoc moveUpAction = new AbstractAction JavaDoc() {
491     public void actionPerformed(ActionEvent JavaDoc e) {
492       if (!_busy()) {
493         if (_shouldGoIntoHistory(_doc.getPromptPos(), _pane.getCaretPosition()))
494           historyPrevAction.actionPerformed(e);
495         else {
496           defaultUpAction.actionPerformed(e);
497           if (! _isCursorAfterPrompt()) moveToPrompt();
498         }
499       }
500     }
501   };
502     
503   /**
504    * Added feature for down. If the cursor is on the last line of the current interaction, it goes into the history.
505    * Otherwise, stays within the current interaction
506    */

507   AbstractAction JavaDoc moveDownAction = new AbstractAction JavaDoc() {
508     public void actionPerformed(ActionEvent JavaDoc e) {
509       if (!_busy()) {
510         if (_shouldGoIntoHistory(_pane.getCaretPosition(), _adapter.getLength()))
511           historyNextAction.actionPerformed(e);
512         else defaultDownAction.actionPerformed(e);
513       }
514     }
515   };
516   
517   /** Tests whether or not to move into the history
518    * @return true iff there are no "\n" characters between the start and the end
519    */

520   private boolean _shouldGoIntoHistory(int start, int end) {
521     if (_isCursorAfterPrompt() && end >= start) {
522       String JavaDoc text = "";
523       try { text = _adapter.getText(start, end - start); }
524       catch(BadLocationException JavaDoc ble) {
525         throw new UnexpectedException(ble); //The conditional should prevent this from ever happening
526
}
527       if (text.indexOf("\n") != -1) return false;
528     }
529     return true;
530   }
531   
532   private boolean _isCursorAfterPrompt() { return _pane.getCaretPosition() >= _doc.getPromptPos(); }
533   
534   Action JavaDoc defaultUpAction;
535   Action JavaDoc defaultDownAction;
536   
537   /** Reverse searches in the history. */
538   AbstractAction JavaDoc historyReverseSearchAction = new AbstractAction JavaDoc() {
539     public void actionPerformed(ActionEvent JavaDoc e) {
540       if (!_busy()) {
541         _doc.reverseSearchInteractionsInHistory();
542         moveToEnd();
543       }
544     }
545   };
546
547   /** Forward searches in the history. */
548   AbstractAction JavaDoc historyForwardSearchAction = new AbstractAction JavaDoc() {
549     public void actionPerformed(ActionEvent JavaDoc e) {
550       if (!_busy()) {
551         _doc.forwardSearchInteractionsInHistory();
552         moveToEnd();
553       }
554     }
555   };
556
557   /** Moves the caret left or wraps around. */
558   AbstractAction JavaDoc moveLeftAction = new AbstractAction JavaDoc() {
559     public void actionPerformed(ActionEvent JavaDoc e) {
560       if (!_busy()) {
561         int position = _pane.getCaretPosition();
562         if (position < _doc.getPromptPos()) {
563           moveToPrompt();
564         }
565         else if (position == _doc.getPromptPos()) {
566           // Wrap around to the end
567
moveToEnd();
568         }
569         else { // position > _doc.getPromptPos()
570
_pane.setCaretPosition(position - 1);
571         }
572       }
573     }
574   };
575
576   /** Moves the caret right or wraps around. */
577   AbstractAction JavaDoc moveRightAction = new AbstractAction JavaDoc() {
578     public void actionPerformed(ActionEvent JavaDoc e) {
579       int position = _pane.getCaretPosition();
580       if (position < _doc.getPromptPos()) {
581         moveToEnd();
582       }
583       else if (position >= _doc.getLength()) {
584         // Wrap around to the start
585
moveToPrompt();
586       }
587       else { // position between prompt and end
588
_pane.setCaretPosition(position + 1);
589       }
590     }
591   };
592
593   /** Skips back one word. Doesn't move past the prompt. */
594   AbstractAction JavaDoc prevWordAction = new AbstractAction JavaDoc() {
595     public void actionPerformed(ActionEvent JavaDoc e) {
596       int position = _pane.getCaretPosition();
597       int promptPos = _doc.getPromptPos();
598       if (position < promptPos) {
599         moveToPrompt();
600       }
601       else if (position == promptPos) {
602         // Wrap around to the end
603
moveToEnd();
604       }
605      else {
606         _pane.getActionMap().get(DefaultEditorKit.previousWordAction).actionPerformed(e);
607       }
608     }
609   };
610
611   /** Skips forward one word. Doesn't move past the prompt. */
612   AbstractAction JavaDoc nextWordAction = new AbstractAction JavaDoc() {
613     public void actionPerformed(ActionEvent JavaDoc e) {
614       int position = _pane.getCaretPosition();
615       int promptPos = _doc.getPromptPos();
616       if (position < promptPos) {
617         moveToEnd();
618       }
619       else if (position >= _doc.getLength()) {
620         // Wrap around to the start
621
moveToPrompt();
622       }
623       else {
624         _pane.getActionMap().get(DefaultEditorKit.nextWordAction).actionPerformed(e);
625       }
626     }
627   };
628   
629
630   /** A box that can be inserted into the interactions pane for separate input. */
631   private static class InputBox extends JTextArea JavaDoc {
632     private static final int BORDER_WIDTH = 1;
633     private static final int INNER_BUFFER_WIDTH = 3;
634     private static final int OUTER_BUFFER_WIDTH = 2;
635     private Color JavaDoc _bgColor = DrJava.getConfig().getSetting(OptionConstants.DEFINITIONS_BACKGROUND_COLOR);
636     private Color JavaDoc _fgColor = DrJava.getConfig().getSetting(OptionConstants.DEFINITIONS_NORMAL_COLOR);
637     private Color JavaDoc _sysInColor = DrJava.getConfig().getSetting(OptionConstants.SYSTEM_IN_COLOR);
638     private boolean _antiAliasText = DrJava.getConfig().getSetting(OptionConstants.TEXT_ANTIALIAS);
639     
640     public InputBox() {
641       setForeground(_sysInColor);
642       setBackground(_bgColor);
643       setCaretColor(_fgColor);
644       setBorder(_createBorder());
645       setLineWrap(true);
646       
647       DrJava.getConfig().addOptionListener(OptionConstants.DEFINITIONS_NORMAL_COLOR,
648                                            new OptionListener<Color JavaDoc>() {
649         public void optionChanged(OptionEvent<Color JavaDoc> oe) {
650           _fgColor = oe.value;
651           setBorder(_createBorder());
652           setCaretColor(oe.value);
653         }
654       });
655       DrJava.getConfig().addOptionListener(OptionConstants.DEFINITIONS_BACKGROUND_COLOR,
656                                            new OptionListener<Color JavaDoc>() {
657         public void optionChanged(OptionEvent<Color JavaDoc> oe) {
658           _bgColor = oe.value;
659           setBorder(_createBorder());
660           setBackground(oe.value);
661         }
662       });
663       DrJava.getConfig().addOptionListener(OptionConstants.SYSTEM_IN_COLOR,
664                                            new OptionListener<Color JavaDoc>() {
665         public void optionChanged(OptionEvent<Color JavaDoc> oe) {
666           _sysInColor = oe.value;
667           setForeground(oe.value);
668         }
669       });
670       DrJava.getConfig().addOptionListener(OptionConstants.TEXT_ANTIALIAS,
671                                            new OptionListener<Boolean JavaDoc>() {
672         public void optionChanged(OptionEvent<Boolean JavaDoc> oce) {
673           _antiAliasText = oce.value.booleanValue();
674           InputBox.this.repaint();
675         }
676       });
677       
678       // Add the input listener for <Shift+Enter>
679
Action JavaDoc newLineAction = new AbstractAction JavaDoc() {
680         public void actionPerformed(ActionEvent JavaDoc e) {
681            insert("\n", getCaretPosition());
682         }
683       };
684       
685       InputMap JavaDoc im = getInputMap(WHEN_FOCUSED);
686       im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,java.awt.Event.SHIFT_MASK), INSERT_NEWLINE_NAME);
687       
688       ActionMap JavaDoc am = getActionMap();
689       am.put(INSERT_NEWLINE_NAME, newLineAction);
690        
691     }
692         
693     private Border JavaDoc _createBorder() {
694       Border JavaDoc outerouter = BorderFactory.createLineBorder(_bgColor, OUTER_BUFFER_WIDTH);
695       Border JavaDoc outer = BorderFactory.createLineBorder(_fgColor, BORDER_WIDTH);
696       Border JavaDoc inner = BorderFactory.createLineBorder(_bgColor, INNER_BUFFER_WIDTH);
697       Border JavaDoc temp = BorderFactory.createCompoundBorder(outer, inner);
698       return BorderFactory.createCompoundBorder(outerouter, temp);
699     }
700     /** Enable anti-aliased text by overriding paintComponent. */
701     protected void paintComponent(Graphics JavaDoc g) {
702       if (_antiAliasText && g instanceof Graphics2D JavaDoc) {
703         Graphics2D JavaDoc g2d = (Graphics2D JavaDoc)g;
704         g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
705       }
706       super.paintComponent(g);
707     }
708     
709     /**
710      * Specifies what to do when the <Enter> key is hit.
711      */

712     public void setInputCompletionCommand(final Runnable JavaDoc command) {
713       InputMap JavaDoc im = getInputMap(WHEN_FOCUSED);
714       im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0), INPUT_ENTERED_NAME);
715       
716       ActionMap JavaDoc am = getActionMap();
717       am.put(INPUT_ENTERED_NAME, new AbstractAction JavaDoc() {
718         public void actionPerformed(ActionEvent JavaDoc e) {
719           command.run();
720         }
721       });
722     }
723     
724     /**
725      * Generates a lambda that can be used to insert text into this input box
726      * @return A lambda that inserts the given text into the textbox when applied
727      */

728     public Lambda<String JavaDoc,String JavaDoc> makeInsertTextCommand() {
729       return new Lambda<String JavaDoc, String JavaDoc>() {
730         public String JavaDoc apply(String JavaDoc input) {
731           insert(input, getCaretPosition());
732           return input;
733         }
734       };
735     }
736     
737     /**
738      * Behaves somewhat like setEnable(false) in that it dissables all
739      * input to the text box, but it does not change the appearance of the text.
740      */

741     public void dissableInputs() {
742       setEditable(false);
743       
744       ActionMap JavaDoc am = getActionMap();
745       Action JavaDoc action;
746       
747       action = am.get(INPUT_ENTERED_NAME);
748       if (action != null) {
749         action.setEnabled(false);
750       }
751       
752       action = am.get(INSERT_NEWLINE_NAME);
753       if (action != null) {
754         action.setEnabled(false);
755       }
756       
757       getCaret().setVisible(false);
758     }
759   }
760   
761   /**
762    * A listener interface that allows for others outside the interactions
763    * controller to be notified when the input console is enabled in the
764    * interactions pane.
765    */

766   public interface ConsoleStateListener extends EventListener JavaDoc {
767     
768     /**
769      * Called when the input console is started in the interactions pane
770      * <p>
771      * This method is called from the thread that initiated the console input
772      */

773     public void consoleInputStarted(InteractionsController c);
774     
775     /**
776      * Called when the console input is complete.
777      * <p>
778      * This method is called from the thread that initiated the console input
779      * @param result The text that was inputted to the console
780      */

781     public void consoleInputCompleted(String JavaDoc result, InteractionsController c);
782   
783   }
784 }
785
Popular Tags