1 33 34 package edu.rice.cs.drjava.ui; 35 36 import javax.swing.AbstractAction ; 37 import javax.swing.BorderFactory ; 38 import javax.swing.Action ; 39 import javax.swing.ActionMap ; 40 import javax.swing.InputMap ; 41 import javax.swing.JTextArea ; 42 import javax.swing.KeyStroke ; 43 import javax.swing.SwingUtilities ; 44 45 import javax.swing.border.Border ; 46 47 import javax.swing.text.AttributeSet ; 48 import javax.swing.text.BadLocationException ; 49 import javax.swing.text.DefaultEditorKit ; 50 import javax.swing.text.SimpleAttributeSet ; 51 import javax.swing.text.StyleConstants ; 52 import javax.swing.text.DefaultStyledDocument ; 53 54 import java.awt.event.ActionEvent ; 55 import java.awt.event.KeyEvent ; 56 import java.awt.Toolkit ; 57 import java.awt.Color ; 58 import java.awt.RenderingHints ; 59 import java.awt.Graphics ; 60 import java.awt.Graphics2D ; 61 62 import java.io.File ; 63 64 import java.util.EventListener ; 65 import java.util.Vector ; 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 90 public class InteractionsController extends AbstractConsoleController { 91 92 private static final String INPUT_ENTERED_NAME = "Input Entered"; 93 private static final String INSERT_NEWLINE_NAME = "Insert Newline"; 94 95 96 public static final String INPUT_BOX_STYLE = "input.box.style"; 97 98 99 public static final String INPUT_BOX_SYMBOL = "[component]"; 100 101 102 private InteractionsModel _model; 103 104 105 private InteractionsDocument _doc; 106 107 108 private SimpleAttributeSet _errStyle; 109 110 111 private final SimpleAttributeSet _debugStyle; 112 113 114 private Lambda<String , String > _insertTextCommand; 115 116 121 private Runnable _inputCompletionCommand; 122 123 127 private Object _consoleInputCommandLock = new Object (); 128 129 130 private static final Lambda<String , String > _defaultInsertTextCommand = 131 new Lambda<String ,String >() { 132 public String apply(String input) { 133 throw new UnsupportedOperationException ("Cannot insert text. There is no console input in progress"); 134 } 135 }; 136 137 138 private static final Runnable _defaultInputCompletionCommand = 139 new Runnable () { 140 public void run() { 141 } 143 }; 144 145 146 protected InputListener _inputListener = new InputListener() { 147 public String getConsoleInput() { 148 149 final InputBox box = new InputBox(); 150 final CompletionMonitor completionMonitor = new CompletionMonitor(); 151 152 Runnable inputCompletionCommand = new Runnable () { 153 public void run() { 154 _setConsoleInputCommands(_defaultInputCompletionCommand, _defaultInsertTextCommand); 156 157 box.dissableInputs(); 158 159 completionMonitor.set(); 160 161 _pane.setEditable(true); 163 _pane.setCaretPosition(_doc.getLength()); 164 _pane.requestFocus(); 165 } 166 }; 167 168 Lambda<String ,String > insertTextCommand = box.makeInsertTextCommand(); 169 170 box.setInputCompletionCommand(inputCompletionCommand); 171 172 _setConsoleInputCommands(inputCompletionCommand, insertTextCommand); 173 174 SwingUtilities.invokeLater(new Runnable () { 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 inputAttributes = _pane.getInputAttributes(); 185 inputAttributes.removeAttributes(inputAttributes); 186 StyleConstants.setComponent(inputAttributes, box); 187 try { 188 DefaultStyledDocument.ElementSpec [] specs = new DefaultStyledDocument.ElementSpec []{ 189 new DefaultStyledDocument.ElementSpec (inputAttributes, DefaultStyledDocument.ElementSpec.ContentType, INPUT_BOX_SYMBOL.toCharArray(), 0, 11) 190 }; 191 192 _adapter.setDocStyle(INPUT_BOX_STYLE, inputAttributes); 194 195 _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 completionMonitor.waitOne(); 214 215 String text = box.getText() + "\n"; 216 fireConsoleInputCompleted(text); 217 218 return text; 219 } 220 }; 221 222 private Vector <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 command = new Runnable () { 231 public void run() { 232 _adapter.clearColoring(); 233 _pane.resetPrompts(); 234 } 235 }; 236 Utilities.invokeLater(command); 237 } 238 239 public void interpreterReady(File wd) { } 240 public void interpreterResetFailed(Throwable t) { } 241 public void interpreterExited(int status) { } 242 public void interpreterChanged(boolean inProgress) { } 243 public void interactionIncomplete() { } 244 public void slaveJVMUsed() { } 245 }; 246 247 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 265 public InteractionsController(InteractionsModel model, InteractionsDJDocument adapter, 266 InteractionsPane pane) { 267 super(adapter, pane); 268 DefaultEditorKit d = pane.EDITOR_KIT; 269 270 for (Action 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 (); 278 _debugStyle = new SimpleAttributeSet (); 279 280 _model.setInputListener(_inputListener); 281 _model.addListener(_viewListener); 282 283 _inputCompletionCommand = _defaultInputCompletionCommand; 284 _insertTextCommand = _defaultInsertTextCommand; 285 _consoleStateListeners = new Vector <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 text) { 305 for(ConsoleStateListener listener : _consoleStateListeners) { listener.consoleInputCompleted(text, this); } 306 } 307 308 311 public InputListener getInputListener() { return _inputListener; } 312 313 317 public void interruptConsoleInput() { SwingUtilities.invokeLater(_inputCompletionCommand); } 318 319 323 public void insertConsoleText(String input) { 324 synchronized(_consoleInputCommandLock) { _insertTextCommand.apply(input); } 325 } 326 327 330 public InteractionsModel getInteractionsModel() { return _model; } 331 332 336 public ConsoleDocument getConsoleDoc() { return _doc; } 337 338 339 public InteractionsDocument getDocument() { return _doc; } 340 341 342 protected void _addDocumentStyles() { 343 super._addDocumentStyles(); 345 346 _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 >() { 353 public void optionChanged(OptionEvent<Color > oe) { 354 _errStyle.addAttribute(StyleConstants.Foreground, oe.value); 355 } 356 }); 357 358 _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 >() { 365 public void optionChanged(OptionEvent<Color > oe) { 366 _debugStyle.addAttribute(StyleConstants.Foreground, oe.value); 367 } 368 }); 369 } 370 371 375 protected void _updateStyles(AttributeSet newSet) { 376 super._updateStyles(newSet); 377 _errStyle.addAttributes(newSet); 378 StyleConstants.setBold(_errStyle, true); _debugStyle.addAttributes(newSet); 380 StyleConstants.setBold(_debugStyle, true); } 382 383 384 protected void _setupModel() { 385 _adapter.addDocumentListener(new CaretUpdateListener()); 386 _doc.setBeep(_pane.getBeep()); 387 } 388 389 390 protected void _setupView() { 391 super._setupView(); 392 393 int mask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); 395 396 _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 _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 _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 _pane.addActionForKeyStroke(DrJava.getConfig().getSetting(OptionConstants.KEY_PREVIOUS_WORD), prevWordAction); 424 DrJava.getConfig().addOptionListener(OptionConstants.KEY_PREVIOUS_WORD, new OptionListener<KeyStroke >() { 425 public void optionChanged(OptionEvent<KeyStroke > 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 >() { 432 public void optionChanged(OptionEvent<KeyStroke > oe) { 433 _pane.addActionForKeyStroke(DrJava.getConfig().getSetting(OptionConstants.KEY_NEXT_WORD), nextWordAction); 434 } 435 }); 436 } 437 438 443 private void _setConsoleInputCommands(Runnable inputCompletionCommand, Lambda<String ,String > insertTextCommand) { 444 synchronized(_consoleInputCommandLock) { 445 _insertTextCommand = insertTextCommand; 446 _inputCompletionCommand = inputCompletionCommand; 447 } 448 } 449 450 452 453 AbstractAction evalAction = new AbstractAction () { 454 public void actionPerformed(ActionEvent e) { 455 if (! _adapter.inCommentBlock()) { 456 Thread command = new Thread ("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 469 AbstractAction historyPrevAction = new AbstractAction () { 470 public void actionPerformed(ActionEvent e) { 471 if (!_busy()) { 472 if (_doc.recallPreviousInteractionInHistory()) moveToEnd(); 473 if (!_isCursorAfterPrompt()) moveToPrompt(); 474 } 475 } 476 }; 477 478 479 AbstractAction historyNextAction = new AbstractAction () { 480 public void actionPerformed(ActionEvent e) { 481 if (!_busy()) { 482 if (_doc.recallNextInteractionInHistory() || !_isCursorAfterPrompt()) moveToPrompt(); 483 } 484 } 485 }; 486 487 490 AbstractAction moveUpAction = new AbstractAction () { 491 public void actionPerformed(ActionEvent 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 507 AbstractAction moveDownAction = new AbstractAction () { 508 public void actionPerformed(ActionEvent 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 520 private boolean _shouldGoIntoHistory(int start, int end) { 521 if (_isCursorAfterPrompt() && end >= start) { 522 String text = ""; 523 try { text = _adapter.getText(start, end - start); } 524 catch(BadLocationException ble) { 525 throw new UnexpectedException(ble); } 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 defaultUpAction; 535 Action defaultDownAction; 536 537 538 AbstractAction historyReverseSearchAction = new AbstractAction () { 539 public void actionPerformed(ActionEvent e) { 540 if (!_busy()) { 541 _doc.reverseSearchInteractionsInHistory(); 542 moveToEnd(); 543 } 544 } 545 }; 546 547 548 AbstractAction historyForwardSearchAction = new AbstractAction () { 549 public void actionPerformed(ActionEvent e) { 550 if (!_busy()) { 551 _doc.forwardSearchInteractionsInHistory(); 552 moveToEnd(); 553 } 554 } 555 }; 556 557 558 AbstractAction moveLeftAction = new AbstractAction () { 559 public void actionPerformed(ActionEvent e) { 560 if (!_busy()) { 561 int position = _pane.getCaretPosition(); 562 if (position < _doc.getPromptPos()) { 563 moveToPrompt(); 564 } 565 else if (position == _doc.getPromptPos()) { 566 moveToEnd(); 568 } 569 else { _pane.setCaretPosition(position - 1); 571 } 572 } 573 } 574 }; 575 576 577 AbstractAction moveRightAction = new AbstractAction () { 578 public void actionPerformed(ActionEvent e) { 579 int position = _pane.getCaretPosition(); 580 if (position < _doc.getPromptPos()) { 581 moveToEnd(); 582 } 583 else if (position >= _doc.getLength()) { 584 moveToPrompt(); 586 } 587 else { _pane.setCaretPosition(position + 1); 589 } 590 } 591 }; 592 593 594 AbstractAction prevWordAction = new AbstractAction () { 595 public void actionPerformed(ActionEvent e) { 596 int position = _pane.getCaretPosition(); 597 int promptPos = _doc.getPromptPos(); 598 if (position < promptPos) { 599 moveToPrompt(); 600 } 601 else if (position == promptPos) { 602 moveToEnd(); 604 } 605 else { 606 _pane.getActionMap().get(DefaultEditorKit.previousWordAction).actionPerformed(e); 607 } 608 } 609 }; 610 611 612 AbstractAction nextWordAction = new AbstractAction () { 613 public void actionPerformed(ActionEvent 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 moveToPrompt(); 622 } 623 else { 624 _pane.getActionMap().get(DefaultEditorKit.nextWordAction).actionPerformed(e); 625 } 626 } 627 }; 628 629 630 631 private static class InputBox extends JTextArea { 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 _bgColor = DrJava.getConfig().getSetting(OptionConstants.DEFINITIONS_BACKGROUND_COLOR); 636 private Color _fgColor = DrJava.getConfig().getSetting(OptionConstants.DEFINITIONS_NORMAL_COLOR); 637 private Color _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 >() { 649 public void optionChanged(OptionEvent<Color > 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 >() { 657 public void optionChanged(OptionEvent<Color > 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 >() { 665 public void optionChanged(OptionEvent<Color > oe) { 666 _sysInColor = oe.value; 667 setForeground(oe.value); 668 } 669 }); 670 DrJava.getConfig().addOptionListener(OptionConstants.TEXT_ANTIALIAS, 671 new OptionListener<Boolean >() { 672 public void optionChanged(OptionEvent<Boolean > oce) { 673 _antiAliasText = oce.value.booleanValue(); 674 InputBox.this.repaint(); 675 } 676 }); 677 678 Action newLineAction = new AbstractAction () { 680 public void actionPerformed(ActionEvent e) { 681 insert("\n", getCaretPosition()); 682 } 683 }; 684 685 InputMap im = getInputMap(WHEN_FOCUSED); 686 im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,java.awt.Event.SHIFT_MASK), INSERT_NEWLINE_NAME); 687 688 ActionMap am = getActionMap(); 689 am.put(INSERT_NEWLINE_NAME, newLineAction); 690 691 } 692 693 private Border _createBorder() { 694 Border outerouter = BorderFactory.createLineBorder(_bgColor, OUTER_BUFFER_WIDTH); 695 Border outer = BorderFactory.createLineBorder(_fgColor, BORDER_WIDTH); 696 Border inner = BorderFactory.createLineBorder(_bgColor, INNER_BUFFER_WIDTH); 697 Border temp = BorderFactory.createCompoundBorder(outer, inner); 698 return BorderFactory.createCompoundBorder(outerouter, temp); 699 } 700 701 protected void paintComponent(Graphics g) { 702 if (_antiAliasText && g instanceof Graphics2D ) { 703 Graphics2D g2d = (Graphics2D )g; 704 g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); 705 } 706 super.paintComponent(g); 707 } 708 709 712 public void setInputCompletionCommand(final Runnable command) { 713 InputMap im = getInputMap(WHEN_FOCUSED); 714 im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0), INPUT_ENTERED_NAME); 715 716 ActionMap am = getActionMap(); 717 am.put(INPUT_ENTERED_NAME, new AbstractAction () { 718 public void actionPerformed(ActionEvent e) { 719 command.run(); 720 } 721 }); 722 } 723 724 728 public Lambda<String ,String > makeInsertTextCommand() { 729 return new Lambda<String , String >() { 730 public String apply(String input) { 731 insert(input, getCaretPosition()); 732 return input; 733 } 734 }; 735 } 736 737 741 public void dissableInputs() { 742 setEditable(false); 743 744 ActionMap am = getActionMap(); 745 Action 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 766 public interface ConsoleStateListener extends EventListener { 767 768 773 public void consoleInputStarted(InteractionsController c); 774 775 781 public void consoleInputCompleted(String result, InteractionsController c); 782 783 } 784 } 785 | Popular Tags |