KickJava   Java API By Example, From Geeks To Geeks.

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


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.*;
37 import javax.swing.text.*;
38 import javax.swing.event.DocumentListener JavaDoc;
39 import javax.swing.event.DocumentEvent JavaDoc;
40 import java.awt.event.ActionEvent JavaDoc;
41 import java.awt.Color JavaDoc;
42 import java.awt.Font JavaDoc;
43
44 import java.io.Serializable JavaDoc;
45
46 import edu.rice.cs.util.swing.Utilities;
47 import edu.rice.cs.util.text.ConsoleDocument;
48
49 import edu.rice.cs.drjava.DrJava;
50 import edu.rice.cs.drjava.config.OptionConstants;
51 import edu.rice.cs.drjava.config.OptionListener;
52 import edu.rice.cs.drjava.config.OptionEvent;
53 import edu.rice.cs.drjava.model.repl.*;
54 import edu.rice.cs.drjava.platform.PlatformFactory;
55 import edu.rice.cs.drjava.model.ClipboardHistoryModel;
56
57 /** Abstract class to handle hooking up a console document with its pane.
58  * @version $Id: AbstractConsoleController.java 4096 2007-01-30 02:37:18Z rcartwright $
59  */

60 public abstract class AbstractConsoleController implements Serializable JavaDoc {
61   /** Adapter for the Swing document used by the model.*/
62   protected InteractionsDJDocument _adapter;
63
64   /** Pane from the view. */
65   protected InteractionsPane _pane;
66
67   /** Style to use for default text. */
68   protected SimpleAttributeSet _defaultStyle;
69
70   /** Style to use for System.out. */
71   protected final SimpleAttributeSet _systemOutStyle;
72
73   /** Style to use for System.err. */
74   protected final SimpleAttributeSet _systemErrStyle;
75
76   // package private for testing purposes (although I haven't written tests yet)
77

78   /** Action to change focus to previous pane. */
79   Action switchToPrevPaneAction;
80
81   /** Action to change focus to next pane. */
82   Action switchToNextPaneAction;
83
84   /** Initializes the document adapter and interactions pane. Subclasses *must* call _init() at the end
85    * of their constructors.
86    */

87   protected AbstractConsoleController(InteractionsDJDocument adapter, InteractionsPane pane) {
88     _adapter = adapter;
89     _pane = pane;
90     _defaultStyle = new SimpleAttributeSet();
91     _systemOutStyle = new SimpleAttributeSet();
92     _systemErrStyle = new SimpleAttributeSet();
93   }
94
95   /** Gets the console document for this console.*/
96   public abstract ConsoleDocument getConsoleDoc();
97
98   /** Initialization method. *Must* be called in constructor by all subclasses. */
99   protected void _init() {
100     _addDocumentStyles();
101     _setupModel();
102     _setupView();
103   }
104
105   /** Adds AttributeSets as named styles to the document adapter. */
106   protected void _addDocumentStyles() {
107     // Default
108
_adapter.setDocStyle(ConsoleDocument.DEFAULT_STYLE, _defaultStyle);
109     DrJava.getConfig().addOptionListener(OptionConstants.DEFINITIONS_NORMAL_COLOR,
110                                          new OptionListener<Color JavaDoc>() {
111       public void optionChanged(OptionEvent<Color JavaDoc> oe) {
112         setDefaultFont(oe.value);
113       }
114     });
115
116     // System.out
117
_systemOutStyle.addAttributes(_defaultStyle);
118     _systemOutStyle.addAttribute(StyleConstants.Foreground,
119                                  DrJava.getConfig().getSetting(OptionConstants.SYSTEM_OUT_COLOR));
120     _adapter.setDocStyle(ConsoleDocument.SYSTEM_OUT_STYLE, _systemOutStyle);
121     DrJava.getConfig().addOptionListener(OptionConstants.SYSTEM_OUT_COLOR,
122                                          new OptionListener<Color JavaDoc>() {
123       public void optionChanged(OptionEvent<Color JavaDoc> oe) {
124         _systemOutStyle.addAttribute(StyleConstants.Foreground, oe.value);
125       }
126     });
127
128     // System.err
129
_systemErrStyle.addAttributes(_defaultStyle);
130     _systemErrStyle.addAttribute(StyleConstants.Foreground,
131                                  DrJava.getConfig().getSetting(OptionConstants.SYSTEM_ERR_COLOR));
132     _adapter.setDocStyle(ConsoleDocument.SYSTEM_ERR_STYLE, _systemErrStyle);
133     DrJava.getConfig().addOptionListener(OptionConstants.SYSTEM_ERR_COLOR,
134                                          new OptionListener<Color JavaDoc>() {
135       public void optionChanged(OptionEvent<Color JavaDoc> oe) {
136         _systemErrStyle.addAttribute(StyleConstants.Foreground, oe.value);
137       }
138     });
139   }
140
141   /** Sets the font for the document, updating all existing text. This behavior is only necessary in Mac OS X, since
142     * setFont() works fine on JTextPane on all other tested platforms. This glitch in the Mac JVM still exists as of
143     * 11-28-06 in beta Java 6.0 build 88.
144     * @param f New font to use.
145     */

146   public void setDefaultFont(Font JavaDoc f) {
147     Color JavaDoc c = DrJava.getConfig().getSetting(OptionConstants.DEFINITIONS_NORMAL_COLOR);
148     setDefaultFont(f, c);
149   }
150
151   /** Sets the color for the document, updating all existing text. This behavior is only necessary in Mac OS X, since
152     * changing the main font works on all other tested platforms.
153     * @param c New color to use.
154     */

155   public void setDefaultFont(Color JavaDoc c) {
156     Font JavaDoc f = DrJava.getConfig().getSetting(OptionConstants.FONT_MAIN);
157     setDefaultFont(f, c);
158   }
159
160   /** Sets the font and color for the document, updating all existing text. This behavior is only necessary in Mac OS
161     * X, since setFont() and changing the main font works on all other tested platforms.
162     * @param f New font to use.
163     * @param c New color to use.
164     */

165   public void setDefaultFont(Font JavaDoc f, Color JavaDoc c) {
166     if (PlatformFactory.ONLY.isMacPlatform()) {
167       SimpleAttributeSet fontSet = new SimpleAttributeSet();
168       StyleConstants.setFontFamily(fontSet, f.getFamily());
169       StyleConstants.setFontSize(fontSet, f.getSize());
170       StyleConstants.setBold(fontSet, f.isBold());
171       StyleConstants.setItalic(fontSet, f.isItalic());
172       if (c != null) {
173         StyleConstants.setForeground(fontSet, c);
174       }
175       _adapter.setCharacterAttributes(0, _adapter.getLength()+1, fontSet, false);
176       _pane.setCharacterAttributes(fontSet, false);
177       _updateStyles(fontSet);
178     }
179   }
180
181   /** Updates all document styles with the attributes contained in newSet.
182     * @param newSet Style containing new attributes to use.
183     */

184   protected void _updateStyles(AttributeSet newSet) {
185     _defaultStyle.addAttributes(newSet);
186     _systemOutStyle.addAttributes(newSet);
187     _systemErrStyle.addAttributes(newSet);
188   }
189
190   /** Sets up the model.*/
191   protected abstract void _setupModel();
192
193   /** Ensures that the caret always stays on or after the prompt, so that output is always scrolled to the bottom.
194    * (The prompt is always at the bottom.) This listener must not modify the console document itself. It is given
195    * read access to the document by Swing when it is run as a listener immediately after a document update.
196    */

197   class CaretUpdateListener implements DocumentListener JavaDoc {
198     public void insertUpdate(final DocumentEvent JavaDoc e) {
199       // Queue an asynchronous task in the event thread to update the document pane; Use SwingUtilities to ensure that
200
// the caret update is performed after the document update listeners have run. Fixes (?) bug #1571405.
201
SwingUtilities.invokeLater(new Runnable JavaDoc() {
202         public void run() {
203           
204           ConsoleDocument doc = getConsoleDoc();
205           doc.acquireReadLock(); // Grab read lock because this code is NOT run as part of document listener!
206
try {
207             int caretPos = _pane.getCaretPosition();
208             int promptPos = doc.getPromptPos();
209             int length = doc.getLength();
210             
211 // System.err.println("insertUpdate called; caretPos = " + caretPos + " docLength = " + length);
212

213             // Figure out where the prompt was before the update
214
int prevPromptPos = promptPos;
215             if (e.getOffset() < promptPos) {
216               // Insert happened before prompt,
217
// so previous position was further back
218
prevPromptPos = promptPos - e.getLength();
219             }
220             
221             if (! doc.hasPrompt()) {
222 // System.err.println("Scrolling to end of document");
223
// Scroll to the end of the document, since output has been inserted after the prompt.
224
moveToEnd();
225             }
226             // (Be careful not to move caret during a reset, when the
227
// prompt pos is temporarily far greater than the length.)
228
else if (promptPos <= length) {
229               if (caretPos < prevPromptPos) {
230                 // Caret has fallen behind prompt, so make it catch up so
231
// the new input is visible.
232
moveToPrompt();
233               }
234               else {
235                 // Caret was on or after prompt, so move it right by the size
236
// of the insert.
237
int size = promptPos - prevPromptPos;
238                 if (size > 0) moveTo(caretPos + size);
239               }
240             }
241           }
242           finally { doc.releaseReadLock(); }
243         }
244       });
245     }
246
247     public void removeUpdate(DocumentEvent JavaDoc e) { _ensureLegalCaretPos(); }
248     public void changedUpdate(DocumentEvent JavaDoc e) { _ensureLegalCaretPos(); }
249     
250     protected void _ensureLegalCaretPos() {
251       Utilities.invokeLater(new Runnable JavaDoc() {
252         public void run() {
253           ConsoleDocument doc = getConsoleDoc();
254           doc.acquireReadLock();
255           try {
256             int length = doc.getLength();
257             if (_pane.getCaretPosition() > length) _pane.setCaretPosition(length);
258           }
259           finally { doc.releaseReadLock(); }
260         }
261       });
262     }
263   }
264
265
266   /** Sets up the view. */
267   protected void _setupView() {
268     KeyStroke beginLineKey = DrJava.getConfig().getSetting(OptionConstants.KEY_BEGIN_LINE);
269     _pane.addActionForKeyStroke(beginLineKey, gotoPromptPosAction);
270     _pane.addActionForKeyStroke(KeyBindingManager.Singleton.addShiftModifier(beginLineKey),
271                                 selectToPromptPosAction);
272     KeyStroke endLineKey = DrJava.getConfig().getSetting(OptionConstants.KEY_END_LINE);
273     _pane.addActionForKeyStroke(endLineKey, gotoEndAction);
274     _pane.addActionForKeyStroke(KeyBindingManager.Singleton.addShiftModifier(endLineKey),
275                                 selectToEndAction);
276
277     DrJava.getConfig().addOptionListener(OptionConstants.KEY_BEGIN_LINE,
278                                          new OptionListener<KeyStroke>() {
279       public void optionChanged(OptionEvent<KeyStroke> oe) {
280         _pane.addActionForKeyStroke(oe.value, gotoPromptPosAction);
281         _pane.addActionForKeyStroke(KeyBindingManager.Singleton.addShiftModifier(oe.value),
282                                     selectToPromptPosAction);
283      }
284     });
285     DrJava.getConfig().addOptionListener(OptionConstants.KEY_END_LINE,
286                                          new OptionListener<KeyStroke>() {
287       public void optionChanged(OptionEvent<KeyStroke> oe) {
288         _pane.addActionForKeyStroke(oe.value, gotoEndAction);
289         _pane.addActionForKeyStroke(KeyBindingManager.Singleton.addShiftModifier(oe.value),
290                                     selectToEndAction);
291      }
292     });
293     
294     _pane.addActionForKeyStroke(DrJava.getConfig().getSetting(OptionConstants.KEY_CUT), cutAction);
295     _pane.addActionForKeyStroke(DrJava.getConfig().getSetting(OptionConstants.KEY_COPY), copyAction);
296     DrJava.getConfig().addOptionListener(OptionConstants.KEY_CUT, new OptionListener<KeyStroke>() {
297       public void optionChanged(OptionEvent<KeyStroke> oe) {
298         _pane.addActionForKeyStroke(DrJava.getConfig().getSetting(OptionConstants.KEY_CUT), cutAction);
299      }
300     });
301     DrJava.getConfig().addOptionListener(OptionConstants.KEY_COPY, new OptionListener<KeyStroke>() {
302       public void optionChanged(OptionEvent<KeyStroke> oe) {
303         _pane.addActionForKeyStroke(DrJava.getConfig().getSetting(OptionConstants.KEY_COPY), copyAction);
304      }
305     });
306   }
307   
308   /** Default cut action. */
309   Action cutAction = new DefaultEditorKit.CutAction() {
310     public void actionPerformed(ActionEvent JavaDoc e) {
311       if (_pane.getSelectedText()!=null) {
312         super.actionPerformed(e);
313         String JavaDoc s = edu.rice.cs.util.swing.Utilities.getClipboardSelection(_pane);
314         if ((s!=null) && (s.length()!=0)) { ClipboardHistoryModel.singleton().put(s); }
315       }
316     }
317   };
318   
319   /** Default copy action. */
320   Action copyAction = new DefaultEditorKit.CopyAction() {
321     public void actionPerformed(ActionEvent JavaDoc e) {
322       if (_pane.getSelectedText()!=null) {
323         super.actionPerformed(e);
324         String JavaDoc s = edu.rice.cs.util.swing.Utilities.getClipboardSelection(_pane);
325         if ((s!=null) && (s.length()!=0)){ ClipboardHistoryModel.singleton().put(s); }
326       }
327     }
328   };
329
330   /** Accessor method for the InteractionsDJDocument. */
331   public InteractionsDJDocument getDocumentAdapter() { return _adapter; }
332
333   /** Accessor method for the InteractionsPane. */
334   public InteractionsPane getPane() { return _pane; }
335
336   /** Determines if the associated console pane is currently computing.
337    * @return true iff the console is busy
338    */

339   protected boolean _busy() { return ! getConsoleDoc().hasPrompt(); }
340
341   /** Inserts a new line at the caret position. */
342   AbstractAction newLineAction = new AbstractAction() {
343     public void actionPerformed(ActionEvent JavaDoc e) { getConsoleDoc().insertNewLine(_pane.getCaretPosition()); }
344   };
345
346   /** Removes all text after the prompt. */
347   AbstractAction clearCurrentAction = new AbstractAction() {
348     public void actionPerformed(ActionEvent JavaDoc e) { getConsoleDoc().clearCurrentInput(); }
349   };
350
351   /** Goes to the end of the current input line. */
352   AbstractAction gotoEndAction = new AbstractAction() {
353     public void actionPerformed(ActionEvent JavaDoc e) { moveToEnd(); }
354   };
355
356   /** Selects to the end of the current input line. */
357   AbstractAction selectToEndAction = new AbstractAction() {
358     public void actionPerformed(ActionEvent JavaDoc e) { _pane.moveCaretPosition(getConsoleDoc().getLength()); }
359   };
360
361   /** Moves the caret to the prompt. */
362   AbstractAction gotoPromptPosAction = new AbstractAction() {
363     public void actionPerformed(ActionEvent JavaDoc e) { moveToPrompt(); }
364   };
365
366   /** Selects to the current prompt. */
367   AbstractAction selectToPromptPosAction = new AbstractAction() {
368     public void actionPerformed(ActionEvent JavaDoc e) {
369       // Selects the text between the old pos and the prompt
370
_pane.moveCaretPosition(getConsoleDoc().getPromptPos());
371     }
372   };
373
374   /** Moves the pane's caret to the end of the document. Only affects reduced_model not the document model. */
375   void moveToEnd() { moveTo(getConsoleDoc().getLength()); }
376   
377   /** Moves the pane's caret to the document's prompt. Only affects reduced_model not the document model. */
378   void moveToPrompt() { moveTo(getConsoleDoc().getPromptPos()); }
379   
380   /** Moves the pane's caret to the given position, as long as it's legal. */
381   void moveTo(int pos) {
382     // Sanity check
383
if (pos < 0) pos = 0;
384     else {
385       int maxLen = getConsoleDoc().getLength();
386       if (pos > maxLen) pos = maxLen;
387     }
388     _pane.setCaretPosition(pos);
389   }
390
391   public void setPrevPaneAction(Action a) {
392     switchToPrevPaneAction = a;
393
394     // We do this here since switchToPrevPaneAction is set after the constructor is called.
395
_pane.addActionForKeyStroke(DrJava.getConfig().getSetting(OptionConstants.KEY_PREVIOUS_PANE),
396                                 switchToPrevPaneAction);
397     DrJava.getConfig().addOptionListener(OptionConstants.KEY_PREVIOUS_PANE, new OptionListener<KeyStroke>() {
398       public void optionChanged(OptionEvent<KeyStroke> oe) {
399         _pane.addActionForKeyStroke(DrJava.getConfig().getSetting(OptionConstants.KEY_PREVIOUS_PANE),
400                                     switchToPrevPaneAction);
401       }
402     });
403   }
404
405   public void setNextPaneAction(Action a) {
406     switchToNextPaneAction = a;
407
408     // We do this here since switchToNextPaneAction is set after the
409
// constructor is called.
410
_pane.addActionForKeyStroke(DrJava.getConfig().getSetting(OptionConstants.KEY_NEXT_PANE),
411                                 switchToNextPaneAction);
412     DrJava.getConfig().addOptionListener(OptionConstants.KEY_NEXT_PANE, new OptionListener<KeyStroke>() {
413       public void optionChanged(OptionEvent<KeyStroke> oe) {
414         _pane.addActionForKeyStroke(DrJava.getConfig().getSetting(OptionConstants.KEY_NEXT_PANE),
415                                     switchToNextPaneAction);
416       }
417     });
418   }
419 }
420
Popular Tags