Clover coverage report - DrJava Test Coverage (drjava-20120304-r5456)
Coverage timestamp: Sun Mar 4 2012 03:13:23 CST
file stats: LOC: 1,446   Methods: 87
NCLOC: 818   Classes: 14
 
 Source file Conditionals Statements Methods TOTAL
DefinitionsPane.java 69.2% 71.7% 55.2% 69.1%
coverage coverage
 1    /*BEGIN_COPYRIGHT_BLOCK
 2    *
 3    * Copyright (c) 2001-2010, JavaPLT group at Rice University (drjava@rice.edu)
 4    * All rights reserved.
 5    *
 6    * Redistribution and use in source and binary forms, with or without
 7    * modification, are permitted provided that the following conditions are met:
 8    * * Redistributions of source code must retain the above copyright
 9    * notice, this list of conditions and the following disclaimer.
 10    * * Redistributions in binary form must reproduce the above copyright
 11    * notice, this list of conditions and the following disclaimer in the
 12    * documentation and/or other materials provided with the distribution.
 13    * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
 14    * names of its contributors may be used to endorse or promote products
 15    * derived from this software without specific prior written permission.
 16    *
 17    * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18    * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 19    * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 20    * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 21    * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 22    * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 23    * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 24    * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 25    * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 26    * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 27    * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 28    *
 29    * This software is Open Source Initiative approved Open Source Software.
 30    * Open Source Initative Approved is a trademark of the Open Source Initiative.
 31    *
 32    * This file is part of DrJava. Download the current version of this project
 33    * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
 34    *
 35    * END_COPYRIGHT_BLOCK*/
 36   
 37    package edu.rice.cs.drjava.ui;
 38   
 39    import javax.swing.*;
 40    import javax.swing.undo.*;
 41    import javax.swing.event.*;
 42    import javax.swing.text.*;
 43    import java.awt.*;
 44    import java.awt.event.*;
 45    import java.util.List;
 46    import java.util.LinkedList;
 47   
 48    import edu.rice.cs.plt.tuple.Pair;
 49    import edu.rice.cs.util.UnexpectedException;
 50    import edu.rice.cs.util.OperationCanceledException;
 51    import edu.rice.cs.util.swing.HighlightManager;
 52    import edu.rice.cs.util.swing.RightClickMouseAdapter;
 53    import edu.rice.cs.util.text.SwingDocument;
 54    import edu.rice.cs.drjava.model.*;
 55    import edu.rice.cs.drjava.model.definitions.CompoundUndoManager;
 56    import edu.rice.cs.drjava.model.definitions.DefinitionsEditorKit;
 57    import edu.rice.cs.drjava.model.definitions.NoSuchDocumentException;
 58    import edu.rice.cs.drjava.model.definitions.indent.Indenter;
 59    import edu.rice.cs.drjava.model.definitions.reducedmodel.ReducedModelState;
 60    import edu.rice.cs.drjava.config.*;
 61    import edu.rice.cs.drjava.DrJava;
 62    import edu.rice.cs.drjava.model.debug.Breakpoint;
 63   
 64    import static edu.rice.cs.drjava.model.definitions.reducedmodel.ReducedModelStates.*;
 65   
 66    /** The pane in which work on a given OpenDefinitionsDocument occurs. A DefinitionsPane is tied to a single document,
 67    * which cannot be changed.
 68    * @version $Id: DefinitionsPane.java 5441 2011-08-12 07:20:08Z mgricken $
 69    */
 70    public class DefinitionsPane extends AbstractDJPane implements Finalizable<DefinitionsPane> {
 71   
 72    /** This field NEEDS to be set by setEditorKit() BEFORE any DefinitonsPanes are created. */
 73    private static DefinitionsEditorKit EDITOR_KIT;
 74   
 75    /* Minimum number of characters to trigger indent warning prompt */
 76    private static int INDENT_WARNING_THRESHOLD = 200000;
 77   
 78    /** Our parent window. */
 79    private final MainFrame _mainFrame;
 80    /** Our corresponding ODD */
 81    private final OpenDefinitionsDocument _doc;
 82   
 83    private volatile UndoAction _undoAction;
 84    private volatile RedoAction _redoAction;
 85   
 86    private volatile boolean testVariable; //For Tests ONLY
 87    // private Document _defdoc;
 88   
 89    /** Flag used to determine if the user has already been warned about debugging when the document within
 90    * this defpane has been modified since its last save.
 91    */
 92    private volatile boolean _hasWarnedAboutModified = false;
 93   
 94    //boolean used to determine whether the DELETE key was the last pressed
 95    //used in fix of bug ID: 2898576
 96    private volatile boolean _isDeleteCompoundEdit;
 97   
 98    //boolean used to determine whether Ctrl-Shift-[ or Ctrl-Shift-] was the last key pressed
 99    //used in fix of bug ID: 2953661
 100    private volatile boolean _isCtrlShiftBracketDown = false;
 101   
 102    // /** Used by the centering source mechanism to ensure paints */
 103    // private boolean _updatePending = false;
 104   
 105    /** Whether to draw text as antialiased. */
 106    private volatile boolean _antiAliasText = false;
 107   
 108    /** Whether to display the right margin. */
 109    private volatile boolean _displayRightMargin = false;
 110   
 111    /** After how many columns to display the right margin. */
 112    private volatile int _numRightMarginColumns = 120;
 113   
 114    /** Maximum character width of the current main font. */
 115    private static volatile int _maxCharWidth = 0;
 116   
 117    /** Color of the right margin. */
 118    private volatile Color _rightMarginColor = Color.red;
 119   
 120    /** Our current compiler error matching highlight. */
 121    private volatile HighlightManager.HighlightInfo _errorHighlightTag = null;
 122   
 123    /** Highlight painter for bookmarks. */
 124    static volatile ReverseHighlighter.DefaultUnderlineHighlightPainter BOOKMARK_PAINTER =
 125    new ReverseHighlighter.DefaultUnderlineHighlightPainter(DrJava.getConfig().getSetting(BOOKMARK_COLOR), 3);
 126   
 127    /** Highlight painter for find results.
 128    * Keep in mind that, while the array is volatile, the elements inside of it are not! */
 129    static volatile LayeredHighlighter.LayerPainter[] FIND_RESULTS_PAINTERS;
 130   
 131    static {
 132  6 FIND_RESULTS_PAINTERS = new LayeredHighlighter.LayerPainter[FIND_RESULTS_COLORS.length+1];
 133  6 for(int i = 0; i < FIND_RESULTS_COLORS.length; ++i) {
 134  48 FIND_RESULTS_PAINTERS[i] =
 135    new ReverseHighlighter.DefaultFrameHighlightPainter(DrJava.getConfig().getSetting(FIND_RESULTS_COLORS[i]), 2);
 136    }
 137  6 FIND_RESULTS_PAINTERS[FIND_RESULTS_COLORS.length] =
 138    new ReverseHighlighter.DefaultUnderlineHighlightPainter(Color.WHITE, 0);
 139    }
 140   
 141    /** How many find result panels are using the highlight painters.
 142    * Keep in mind that, while the array is volatile, the elements inside of it are not! */
 143    static volatile int[] FIND_RESULTS_PAINTERS_USAGE = new int[FIND_RESULTS_COLORS.length];
 144   
 145    /** Highlight painter for breakpoints. */
 146    static ReverseHighlighter.DrJavaHighlightPainter BREAKPOINT_PAINTER =
 147    new ReverseHighlighter.DrJavaHighlightPainter(DrJava.getConfig().getSetting(DEBUG_BREAKPOINT_COLOR));
 148   
 149    /** Highlight painter for disabled breakpoints. */
 150    static volatile ReverseHighlighter.DrJavaHighlightPainter DISABLED_BREAKPOINT_PAINTER =
 151    new ReverseHighlighter.DrJavaHighlightPainter(DrJava.getConfig().getSetting(DEBUG_BREAKPOINT_DISABLED_COLOR));
 152   
 153    /** Highlight painter for thread's current location. */
 154    static volatile ReverseHighlighter.DrJavaHighlightPainter THREAD_PAINTER =
 155    new ReverseHighlighter.DrJavaHighlightPainter(DrJava.getConfig().getSetting(DEBUG_THREAD_COLOR));
 156   
 157    /** The name of the keymap added to the super class (saved so it can be removed). */
 158    public static final String INDENT_KEYMAP_NAME = "INDENT_KEYMAP";
 159   
 160    /** Updates match highlights. Only runs in the event thread.
 161    * @param offset caret position immediately following some form of brace; hence offset > 0.
 162    * @param opening true if the the preceding brace is "opening"
 163    */
 164  8 protected void matchUpdate(int offset, boolean opening) {
 165  8 assert EventQueue.isDispatchThread();
 166  8 assert offset > 0;
 167  8 _doc.setCurrentLocation(offset);
 168  8 _removePreviousHighlight();
 169   
 170    // Update the highlight if there is any.
 171  8 int caretPos = getCaretPosition();
 172   
 173  8 if (opening) {
 174    // getCaretPosition() will be the start of the highlight
 175   
 176  2 int to = _doc.balanceForward(); // relative distance to matching brace (if it exists)
 177  2 if (to > -1) { // matching closing brace exists
 178  1 int end = caretPos + to;
 179  1 _addHighlight(caretPos - 1, end);
 180    }
 181  2 updateStatusField();
 182    }
 183    else {
 184    // Update highlight ends with getCaretPosition()
 185   
 186  6 int from = _doc.balanceBackward(); // relative distance to matching brace (if it exists)
 187  6 if (from > -1) { // matching opening brace was found
 188  6 int start = caretPos - from;
 189  6 _addHighlight(start, caretPos);
 190   
 191  6 String matchText = _matchText(start);
 192   
 193  6 if (matchText != null) _mainFrame.updateStatusField("Bracket matches: " + matchText);
 194  0 else updateStatusField();
 195    }
 196    }
 197    }
 198   
 199    /** Updates status fields in the main frame (title bar, selected file name) when document is modified. */
 200  95 protected void updateStatusField() { _mainFrame.updateStatusField(); }
 201   
 202    /* Returns the text of the line where a matching open brace exists whenever the cursor is at a closing brace */
 203  6 private String _matchText(int braceIndex) {
 204  6 DJDocument doc = _doc;
 205  6 String docText;
 206  6 docText = doc.getText();
 207   
 208  6 char ch = docText.charAt(braceIndex);
 209  6 if ( ch == '{' || ch == '(') { //match everything before if we found a curly brace
 210  6 Character charBefore = null;
 211  6 int charBeforeIndex = braceIndex-1;
 212  6 boolean previousLine = false;
 213   
 214  4 if (charBeforeIndex != -1) charBefore = docText.charAt(charBeforeIndex);
 215   
 216  6 charBeforeIndex--;
 217   
 218  6 while (charBeforeIndex >= 0 && (charBefore == '\n' || charBefore == ' ')) {
 219  5 charBefore = docText.charAt(charBeforeIndex);
 220  2 if (!previousLine && charBefore != '\n' && charBefore != ' ') charBeforeIndex = braceIndex-1;
 221  1 if (charBefore == '\n') previousLine = true;
 222  5 charBeforeIndex--;
 223    }
 224   
 225  6 final StringBuilder returnText = new StringBuilder(docText.substring(0, charBeforeIndex+2));
 226  1 if (previousLine) returnText.append("...");
 227  6 returnText.append(ch);
 228   
 229  6 int lastNewlineIndex = returnText.lastIndexOf("\n");
 230  6 return returnText.substring(lastNewlineIndex+1);
 231    }
 232    else //not a curly brace
 233  0 return null;
 234    }
 235   
 236    /** The OptionListener for DEFINITIONS_MATCH_COLOR. */
 237    private class MatchColorOptionListener implements OptionListener<Color> {
 238  0 public void optionChanged(OptionEvent<Color> oce) {
 239  0 MATCH_PAINTER = new ReverseHighlighter.DrJavaHighlightPainter(oce.value);
 240  0 if (_matchHighlight != null) {
 241  0 int start = _matchHighlight.getStartOffset();
 242  0 int end = _matchHighlight.getEndOffset();
 243  0 _matchHighlight.remove();
 244  0 _addHighlight(start, end);
 245    }
 246    }
 247    }
 248   
 249    /** The OptionListener for COMPILER_ERROR_COLOR. */
 250    private class ErrorColorOptionListener implements OptionListener<Color> {
 251  0 public void optionChanged(OptionEvent<Color> oce) {
 252  0 ERROR_PAINTER = new ReverseHighlighter.DrJavaHighlightPainter(oce.value);
 253  0 if (_errorHighlightTag != null) {
 254  0 int start = _errorHighlightTag.getStartOffset();
 255  0 int end = _errorHighlightTag.getEndOffset();
 256  0 _errorHighlightTag.remove();
 257  0 addErrorHighlight(start, end);
 258    }
 259    }
 260    }
 261   
 262    /** The OptionListener for BOOKMARK_COLOR. */
 263    private class BookmarkColorOptionListener implements OptionListener<Color> {
 264  0 public void optionChanged(OptionEvent<Color> oce) {
 265  0 BOOKMARK_PAINTER =
 266    new ReverseHighlighter.DefaultUnderlineHighlightPainter(oce.value, BOOKMARK_PAINTER.getThickness());
 267  0 _mainFrame.refreshBookmarkHighlightPainter();
 268    }
 269    }
 270   
 271    /** The OptionListener for FIND_RESULTS_COLOR. */
 272    private static class FindResultsColorOptionListener implements OptionListener<Color> {
 273    private int _index;
 274  744 public FindResultsColorOptionListener(int i) { _index = i; }
 275  0 public void optionChanged(OptionEvent<Color> oce) {
 276  0 synchronized(FIND_RESULTS_PAINTERS) {
 277  0 FIND_RESULTS_PAINTERS[_index] = new ReverseHighlighter.DefaultFrameHighlightPainter(oce.value, 2);
 278    }
 279    }
 280    }
 281   
 282    /** The OptionListener for DEBUG_BREAKPOINT_COLOR. */
 283    private class BreakpointColorOptionListener implements OptionListener<Color> {
 284  0 public void optionChanged(OptionEvent<Color> oce) {
 285  0 BREAKPOINT_PAINTER = new ReverseHighlighter.DrJavaHighlightPainter(oce.value);
 286  0 _mainFrame.refreshBreakpointHighlightPainter();
 287    }
 288    }
 289   
 290    /** The OptionListener for DEBUG_BREAKPOINT_DISABLED_COLOR. */
 291    private class DisabledBreakpointColorOptionListener implements OptionListener<Color> {
 292  0 public void optionChanged(OptionEvent<Color> oce) {
 293  0 DISABLED_BREAKPOINT_PAINTER = new ReverseHighlighter.DrJavaHighlightPainter(oce.value);
 294  0 _mainFrame.refreshBreakpointHighlightPainter();
 295    }
 296    }
 297   
 298    /** The OptionListener for DEBUG_THREAD_COLOR. */
 299    private static class ThreadColorOptionListener implements OptionListener<Color> {
 300  0 public void optionChanged(OptionEvent<Color> oce) {
 301  0 THREAD_PAINTER = new ReverseHighlighter.DrJavaHighlightPainter(oce.value);
 302    }
 303    }
 304   
 305    /** The OptionListener for TEXT_ANTIALIAS. */
 306    private class AntiAliasOptionListener implements OptionListener<Boolean> {
 307  0 public void optionChanged(OptionEvent<Boolean> oce) {
 308  0 _antiAliasText = oce.value.booleanValue();
 309  0 DefinitionsPane.this.repaint();
 310    }
 311    }
 312   
 313    /** Listens to any undoable events in the document, and adds them to the undo manager. Must be done in the view
 314    * because the edits are stored along with the caret position at the time of the edit. Correction: document
 315    * cursor position should be used instead of caret position. Perhaps this listener should be attached to the
 316    * document.
 317    */
 318    private final UndoableEditListener _undoListener = new UndoableEditListener() {
 319   
 320    /** The function to handle what happens when an UndoableEditEvent occurs.
 321    * @param e
 322    */
 323  42 public void undoableEditHappened(UndoableEditEvent e) {
 324  42 assert EventQueue.isDispatchThread();
 325    // UndoWithPosition undo = new UndoWithPosition(e.getEdit(), _doc.getCurrentLocation());
 326  42 UndoableEdit undo = e.getEdit();
 327  42 if (! _inCompoundEdit) {
 328  19 CompoundUndoManager undoMan = _doc.getUndoManager();
 329  19 _inCompoundEdit = true;
 330  19 _compoundEditKey = undoMan.startCompoundEdit();
 331  19 getUndoAction().updateUndoState();
 332  19 getRedoAction().updateRedoState();
 333    }
 334  42 _doc.getUndoManager().addEdit(undo);
 335  42 getRedoAction().setEnabled(false);
 336    }
 337    };
 338   
 339    // /** The menu item for the "Add Watch" option. Stored in field so that it may be enabled and
 340    // * disabled depending on Debug Mode.
 341    // */
 342    // private JMenuItem _addWatchMenuItem;
 343   
 344    /** The contextual popup menu for the Definitions Pane. */
 345    private volatile JPopupMenu _popMenu;
 346   
 347    /** The mouse adapter for handling a popup menu. */
 348    private volatile PopupMenuMouseAdapter _popupMenuMA;
 349   
 350    /** Listens to caret to highlight errors as appropriate. */
 351    private volatile ErrorCaretListener _errorListener;
 352   
 353    private volatile ActionListener _setSizeListener = null;
 354   
 355    /** An action to handle indentation spawned by pressing the tab key. */
 356    private class IndentKeyActionTab extends AbstractAction {
 357   
 358    /** Handle the key typed event from the text field. */
 359  0 public void actionPerformed(ActionEvent e) {
 360    // The following commented out code was moved into the indent() method
 361    //int pos = getCaretPosition();
 362    //_doc().setCurrentLocation(pos);
 363  0 _mainFrame.hourglassOn();
 364  0 try {
 365  0 indent();
 366    } finally {
 367  0 _mainFrame.hourglassOff();
 368    }
 369    }
 370    }
 371   
 372    /** Used for indent action spawned by pressing the enter key, '{', or '}'. */
 373    private class IndentKeyAction extends AbstractAction {
 374   
 375    /** The key string ("\n"|"{"|"}") for the key pressed that invokes this
 376    * instance. Not used currently, but there for readability and possible
 377    * future use, e.g., debugging add-ons or the rewrite of the indention code.
 378    */
 379    @SuppressWarnings("unused") private final String _key;
 380   
 381    /** The default action to take when the specified key is pressed. */
 382    private final Action _defaultAction;
 383   
 384    /** Whether to perform the indent if the caret is in a String or comment. */
 385    private final boolean _indentNonCode;
 386   
 387    /** Creates an IndentKeyAction which only invokes indent if the caret is in code, and not Strings or
 388    * comments.
 389    */
 390  279 IndentKeyAction(String key, Action defaultAction) {
 391  279 this(key, defaultAction, false);
 392    }
 393   
 394    /** Creates a new IndentKeyAction with the specified parameters.
 395    * @param key name of the key, for debugging purposes
 396    * @param defaultAction action to perform in addition to indenting
 397    * @param indentNonCode whether to indent Strings and comments
 398    */
 399  372 IndentKeyAction(String key, Action defaultAction, boolean indentNonCode) {
 400  372 _key = key;
 401  372 _defaultAction = defaultAction;
 402  372 _indentNonCode = indentNonCode;
 403    }
 404   
 405    /** This method tells what the reason should be for spawning this indent event
 406    * Defaults to Indenter.IndentReason.OTHER
 407    */
 408  0 protected Indenter.IndentReason getIndentReason() { return Indenter.IndentReason.OTHER; }
 409   
 410    /** Handle the "key typed" event from the text field. Calls the default action to make sure the right things
 411    * happen, then makes a call to indentLine().
 412    */
 413  2 public void actionPerformed(ActionEvent e) {
 414    // this is a hack to get braces {} to work on Icelandic keyboard (bug 2813140)
 415    // I don't understand why {} are not entered using the default action
 416  2 if (!_isCtrlShiftBracketDown &&
 417    (e != null) &&
 418    (e.getActionCommand() != null) &&
 419    (e.getActionCommand().equals("{") || e.getActionCommand().equals("}"))) {
 420  1 ActionEvent e2 = new ActionEvent(e.getSource(),
 421    e.getID(),
 422    e.getActionCommand(),
 423    e.getWhen(),
 424    ActionEvent.SHIFT_MASK);
 425  1 _defaultAction.actionPerformed(e2);
 426    }
 427    else {
 428  1 _defaultAction.actionPerformed(e);
 429    }
 430   
 431    // Only indent if in code
 432   
 433  2 updateCurrentLocationInDoc();
 434  2 ReducedModelState state = _doc.getStateAtCurrent();
 435  1 if (state.equals(FREE) || _indentNonCode) indent(getIndentReason());
 436    }
 437    }
 438   
 439    /** Updates the current location stored in the document by setting it to the caret position. */
 440  87 public void updateCurrentLocationInDoc() {
 441  87 _doc.setCurrentLocation(getCaretPosition());
 442    }
 443   
 444    /** Special action to take care of case when tab key is pressed. */
 445    private volatile Action _indentKeyActionTab = new IndentKeyActionTab();
 446   
 447    /** Because the "default" action for the enter key is special, it must be
 448    * grabbed from the Keymap using getAction(KeyStroke), which returns the
 449    * "default" action for all keys which have behavior extending beyond
 450    * regular text keys.
 451    */
 452    private final Action _indentKeyActionLine =
 453    new IndentKeyAction("\n", (Action) getActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)),
 454    true /* indent non-code, too */ ) {
 455    /* overriding this method is important so that pressing the enter key causes
 456    * different indentation than pressing other keys, for bug 681203
 457    */
 458  1 protected Indenter.IndentReason getIndentReason() { return Indenter.IndentReason.ENTER_KEY_PRESS; }
 459    };
 460   
 461    /** Likewise, regular text keys like '{', '}', and ':' do not have special actions that are returned by
 462    * getAction(KeyStroke). To make sure these behave right, we use getDefaultAction() instead.
 463    */
 464    private final Action _indentKeyActionCurly = new IndentKeyAction("}", getKeymap().getDefaultAction());
 465    private final Action _indentKeyActionOpenCurly = new IndentKeyAction("{", getKeymap().getDefaultAction());
 466    private final Action _indentKeyActionColon = new IndentKeyAction(":", getKeymap().getDefaultAction());
 467   
 468    /** Tells us whether we currently are in the middle of a CompoundEdit for regular keystrokes.
 469    * Helps us with granular undo.
 470    */
 471    public volatile boolean _inCompoundEdit = false;
 472    private volatile int _compoundEditKey;
 473   
 474    /** Our keymap containing key bindings. Takes precedence over the default map. */
 475    final Keymap ourMap;
 476   
 477    /** Standard Constructor. Sets up all the defaults.
 478    * @param mf the parent window
 479    */
 480  93 public DefinitionsPane(MainFrame mf, final OpenDefinitionsDocument doc) {
 481  93 super(new SwingDocument());
 482   
 483  93 _mainFrame = mf;
 484   
 485  93 addFocusListener(new FocusAdapter() {
 486  0 public void focusGained(FocusEvent e) {
 487  0 _mainFrame.getModel().getDocumentNavigator().requestSelectionUpdate(doc);
 488    }
 489    });
 490   
 491  93 _doc = doc; // NOTE: _doc is final
 492   
 493    // read the initial selection/scrolling values from the document
 494    // to be set when the pane is first notified active
 495  93 _selStart = _doc.getInitialSelectionStart();
 496  93 _selEnd = _doc.getInitialSelectionEnd();
 497  93 _savedVScroll = _doc.getInitialVerticalScroll();
 498  93 _savedHScroll = _doc.getInitialHorizontalScroll();
 499   
 500    //super.setDocument(NULL_DOCUMENT);
 501  93 _resetUndo();
 502   
 503  93 Font mainFont = DrJava.getConfig().getSetting(FONT_MAIN);
 504  93 setFont(mainFont);
 505   
 506  93 setEditable(true);
 507   
 508    // add actions for indent key
 509  93 ourMap = addKeymap(INDENT_KEYMAP_NAME, getKeymap());
 510  93 ourMap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), _indentKeyActionLine);
 511  93 ourMap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), _indentKeyActionTab);
 512  93 ourMap.addActionForKeyStroke(KeyStroke.getKeyStroke('}'), _indentKeyActionCurly);
 513  93 ourMap.addActionForKeyStroke(KeyStroke.getKeyStroke('{'), _indentKeyActionOpenCurly);
 514  93 ourMap.addActionForKeyStroke(KeyStroke.getKeyStroke(':'), _indentKeyActionColon);
 515  93 setKeymap(ourMap);
 516   
 517    // Keymap map = ourMap;
 518    // KeyStroke[] ks;
 519    // ks = ourMap.getBoundKeyStrokes();
 520    // for (KeyStroke k:ks) {
 521    // System.out.println(k);
 522    // }
 523    // ourMap = ourMap.getResolveParent();
 524    // ks = ourMap.getBoundKeyStrokes();
 525    // for (KeyStroke k:ks) {
 526    // System.out.println(k);
 527    // }
 528   
 529    // this.setEditorKit(new StyledEditorKit());
 530   
 531  93 _antiAliasText = DrJava.getConfig().getSetting(TEXT_ANTIALIAS);
 532  93 _displayRightMargin = DrJava.getConfig().getSetting(DISPLAY_RIGHT_MARGIN);
 533  93 _numRightMarginColumns = DrJava.getConfig().getSetting(RIGHT_MARGIN_COLUMNS);
 534  93 _rightMarginColor = DrJava.getConfig().getSetting(RIGHT_MARGIN_COLOR);
 535   
 536  93 OptionListener<Color> cListener;
 537  93 Pair<Option<Color>, OptionListener<Color>> cPair;
 538   
 539    // Setup the color listeners. NOTE: the Foreground/Background listeners add themselves to DrJava.getConfig()
 540    // in their own constructors. Rather than refactor it, we decided to work with that design decision.
 541  93 cListener = new ForegroundColorListener(this);
 542  93 cPair = new Pair<Option<Color>, OptionListener<Color>>(OptionConstants.DEFINITIONS_NORMAL_COLOR, cListener);
 543  93 _colorOptionListeners.add(cPair);
 544   
 545  93 cListener = new BackgroundColorListener(this);
 546  93 cPair = new Pair<Option<Color>, OptionListener<Color>>(OptionConstants.DEFINITIONS_BACKGROUND_COLOR, cListener);
 547  93 _colorOptionListeners.add(cPair);
 548   
 549    // These listeners do not register themselves in their own constructors. We do.
 550  93 cListener = new MatchColorOptionListener();
 551  93 cPair = new Pair<Option<Color>, OptionListener<Color>>(OptionConstants.DEFINITIONS_MATCH_COLOR, cListener);
 552  93 _colorOptionListeners.add(cPair);
 553  93 DrJava.getConfig().addOptionListener(OptionConstants.DEFINITIONS_MATCH_COLOR, cListener);
 554   
 555  93 cListener = new ErrorColorOptionListener();
 556  93 cPair = new Pair<Option<Color>, OptionListener<Color>>(OptionConstants.COMPILER_ERROR_COLOR, cListener);
 557  93 _colorOptionListeners.add(cPair);
 558  93 DrJava.getConfig().addOptionListener(OptionConstants.COMPILER_ERROR_COLOR, cListener);
 559   
 560  93 cListener = new BookmarkColorOptionListener();
 561  93 cPair = new Pair<Option<Color>, OptionListener<Color>>(OptionConstants.BOOKMARK_COLOR, cListener);
 562  93 _colorOptionListeners.add(cPair);
 563  93 DrJava.getConfig().addOptionListener(OptionConstants.BOOKMARK_COLOR, cListener);
 564   
 565  93 for (int i = 0; i < FIND_RESULTS_COLORS.length; ++i) {
 566  744 cListener = new FindResultsColorOptionListener(i);
 567  744 cPair = new Pair<Option<Color>, OptionListener<Color>>(OptionConstants.FIND_RESULTS_COLORS[i], cListener);
 568  744 _colorOptionListeners.add(cPair);
 569  744 DrJava.getConfig().addOptionListener(OptionConstants.FIND_RESULTS_COLORS[i], cListener);
 570    }
 571   
 572  93 cListener = new BreakpointColorOptionListener();
 573  93 cPair = new Pair<Option<Color>, OptionListener<Color>>(OptionConstants.DEBUG_BREAKPOINT_COLOR, cListener);
 574  93 _colorOptionListeners.add(cPair);
 575  93 DrJava.getConfig().addOptionListener(OptionConstants.DEBUG_BREAKPOINT_COLOR, cListener);
 576   
 577  93 cListener = new DisabledBreakpointColorOptionListener();
 578  93 cPair = new Pair<Option<Color>, OptionListener<Color>>(OptionConstants.DEBUG_BREAKPOINT_DISABLED_COLOR, cListener);
 579  93 _colorOptionListeners.add(cPair);
 580  93 DrJava.getConfig().addOptionListener( OptionConstants.DEBUG_BREAKPOINT_DISABLED_COLOR, cListener);
 581   
 582  93 cListener = new ThreadColorOptionListener();
 583  93 cPair = new Pair<Option<Color>, OptionListener<Color>>(OptionConstants.DEBUG_THREAD_COLOR, cListener);
 584  93 _colorOptionListeners.add(cPair);
 585  93 DrJava.getConfig().addOptionListener( OptionConstants.DEBUG_THREAD_COLOR, cListener);
 586   
 587  93 OptionListener<Boolean> bListener = new AntiAliasOptionListener();
 588  93 Pair<Option<Boolean>, OptionListener<Boolean>> bPair =
 589    new Pair<Option<Boolean>, OptionListener<Boolean>>(OptionConstants.TEXT_ANTIALIAS, bListener);
 590  93 _booleanOptionListeners.add(bPair);
 591  93 DrJava.getConfig().addOptionListener(OptionConstants.TEXT_ANTIALIAS, bListener);
 592   
 593  93 bListener = new OptionListener<Boolean>() {
 594  0 public void optionChanged(OptionEvent<Boolean> oce) {
 595  0 _displayRightMargin = oce.value;
 596  0 DefinitionsPane.this.repaint();
 597    }
 598    };
 599  93 bPair = new Pair<Option<Boolean>, OptionListener<Boolean>>(OptionConstants.DISPLAY_RIGHT_MARGIN, bListener);
 600  93 _booleanOptionListeners.add(bPair);
 601  93 DrJava.getConfig().addOptionListener(OptionConstants.DISPLAY_RIGHT_MARGIN, bListener);
 602   
 603  93 OptionListener<Integer> iTemp = new OptionListener<Integer>() {
 604  0 public void optionChanged(OptionEvent<Integer> oce) {
 605  0 _numRightMarginColumns = oce.value;
 606  0 DefinitionsPane.this.repaint();
 607    }
 608    };
 609  93 Pair<Option<Integer>, OptionListener<Integer>> iPair =
 610    new Pair<Option<Integer>, OptionListener<Integer>>(OptionConstants.RIGHT_MARGIN_COLUMNS, iTemp);
 611  93 _integerOptionListeners.add(iPair);
 612  93 DrJava.getConfig().addOptionListener(OptionConstants.RIGHT_MARGIN_COLUMNS, iTemp);
 613   
 614  93 cListener = new OptionListener<Color>() {
 615  0 public void optionChanged(OptionEvent<Color> oce) {
 616  0 _rightMarginColor = oce.value;
 617  0 DefinitionsPane.this.repaint();
 618    }
 619    };
 620  93 cPair = new Pair<Option<Color>, OptionListener<Color>>(OptionConstants.RIGHT_MARGIN_COLOR, cListener);
 621  93 _colorOptionListeners.add(cPair);
 622  93 DrJava.getConfig().addOptionListener(OptionConstants.RIGHT_MARGIN_COLOR, cListener);
 623   
 624  93 createPopupMenu();
 625   
 626    //Add listener to components that can bring up popup menus.
 627  93 _popupMenuMA = new PopupMenuMouseAdapter();
 628  93 this.addMouseListener(_popupMenuMA);
 629  93 this.setHighlighter(new ReverseHighlighter());
 630  93 _highlightManager = new HighlightManager(this);
 631   
 632  93 int rate = this.getCaret().getBlinkRate();
 633    // Change the caret to one that doesn't remove selection highlighting when focus is lost.
 634    // Fixes bug #788295 "No highlight when find/replace switches docs".
 635    // this.setCaret(new DefaultCaret() {
 636    // public void focusLost(FocusEvent e) { setVisible(false); }
 637    // });
 638  93 this.getCaret().setBlinkRate(rate);
 639    // Utilities.showDebug("DP constructor finished");
 640   
 641  93 _isDeleteCompoundEdit = true;
 642    }
 643   
 644    /** Ends a compound edit.*/
 645  15 public void endCompoundEdit() {
 646  15 if (_inCompoundEdit) {
 647  11 CompoundUndoManager undoMan = _doc.getUndoManager();
 648  11 _inCompoundEdit = false;
 649  11 undoMan.endCompoundEdit(_compoundEditKey);
 650    }
 651    }
 652   
 653    /** Takes in any keyboard input, checks to see if it is in the keyToActionMap in KeybindingManager, if so
 654    * executes the action, otherwise checks if it contains the current platform's menu shortcut modifier and
 655    * if so, ignores that command (this disallows the execution of the UI's default actions such as
 656    * cut/copy/paste/select all), otherwise does whatever normally would be done.
 657    */
 658  37 public void processKeyEvent(KeyEvent e) {
 659  37 if (_mainFrame.getAllowKeyEvents()) {
 660   
 661   
 662    //Fixes bug ID:2813140 - "Go to Opening/Closing Brace" Shortcut Inserts { or }
 663  36 if (((e.getKeyCode() == KeyEvent.VK_OPEN_BRACKET) || (e.getKeyCode() == KeyEvent.VK_CLOSE_BRACKET)) &&
 664    ((e.getModifiers() & InputEvent.CTRL_MASK) != 0) &&
 665    ((e.getModifiers() & InputEvent.SHIFT_MASK) != 0)) {
 666    // Ctrl-Shift-Bracket
 667  0 if (e.getID() == KeyEvent.KEY_PRESSED) {
 668  0 _isCtrlShiftBracketDown = true;
 669    }
 670  0 else if (e.getID() == KeyEvent.KEY_RELEASED) {
 671  0 _isCtrlShiftBracketDown = false;
 672    }
 673    }
 674   
 675    //Fixes bug ID:2898576 - Backspace undo/redo issues
 676  36 if (e.getKeyCode() == KeyEvent.VK_BACK_SPACE && _isDeleteCompoundEdit) {
 677  2 endCompoundEdit();
 678  2 _isDeleteCompoundEdit = false;
 679    }
 680  34 else if (e.getID()==KeyEvent.KEY_PRESSED && e.getKeyCode() != KeyEvent.VK_BACK_SPACE) {
 681  10 _isDeleteCompoundEdit = true;
 682    }
 683   
 684  36 KeyStroke ks = KeyStroke.getKeyStrokeForEvent(e);
 685  36 Action a = KeyBindingManager.ONLY.get(ks);
 686   
 687    // Don't perform the action if the keystroke is NULL_KEYSTROKE (generated by some Windows keys)
 688  36 if ((ks != KeyStrokeOption.NULL_KEYSTROKE) && (a != null)) {
 689    // System.out.println("Keystroke was null");
 690  3 endCompoundEdit();
 691    // Performs the action a
 692  3 SwingUtilities.notifyAction(a, ks, e, e.getSource(), e.getModifiers());
 693   
 694    // Make sure we don't consume it again
 695  3 e.consume();
 696    }
 697    else {
 698    // Allows one step undoing of the keystrokes defined on the keymap (e.g. enter, tab, '{', '}', ':').
 699  33 Keymap km = getKeymap();
 700   
 701  33 if (km.isLocallyDefined(ks) || km.isLocallyDefined(KeyStroke.getKeyStroke(ks.getKeyChar()))) {
 702    // We're breaking up compound edits at the granularity of "enter"'s.
 703  1 if (e.getKeyCode() == KeyEvent.VK_ENTER) endCompoundEdit();
 704    // CompoundUndoManager undoMan = _doc.getUndoManager();
 705    // int key = undoMan.startCompoundEdit();
 706    // System.out.println("supering 1 " + isAltF4);
 707   
 708  4 super.processKeyEvent(e);
 709    // We call endCompoundEdit() here because one will automatically start when processKeyEvent finishes
 710    // (see the definition of _undoListener).
 711  4 endCompoundEdit();
 712    // undoMan.endCompoundEdit(key); //commented out because of frenchkeyboard fix
 713    // e.consume();
 714    }
 715    else {
 716    // The following conditional fixes bug #676586 by ignoring typed events when the meta key is down and fixes
 717    // bug #905405 "Undo Alt+Anything Causes Exception" by ignoring typed events when the alt key is down.
 718    // NOTE: no longer need to check for alt since we now only start a new compound edit if an undoable edit
 719    // actually happened.
 720  29 if ((e.getModifiers() & InputEvent.META_MASK) != 0
 721    // || ((e.getModifiers() & InputEvent.ALT_MASK) != 0)) // omitted for frenchkeyboard support
 722    && e.getKeyCode() == KeyEvent.VK_UNDEFINED) {
 723    // System.out.println("not supering 1 " + isAltF4);
 724  1 return;
 725    }
 726   
 727    // The following conditional fixes ease of use issue 693253 by checking if a typed event is
 728    // shift-delete or shift-backspace and then performing a delete or backspace operation,
 729    // respectively
 730  28 if ((e.getModifiers() & InputEvent.SHIFT_MASK) != 0) {
 731  4 int newModifiers = e.getModifiers() & ~(InputEvent.SHIFT_MASK);
 732   
 733  4 KeyStroke newKs = KeyStroke.getKeyStroke(ks.getKeyCode(), newModifiers, ks.isOnKeyRelease());
 734  4 String name = KeyBindingManager.ONLY.getName(newKs);
 735   
 736  4 if (name != null && (name.equals("Delete Previous") || name.equals("Delete Next"))) {
 737  0 endCompoundEdit();
 738    // We are unsure about the third and fourth arguments (e and e.getSource()); we simply
 739    // reuse the original values
 740  0 SwingUtilities.notifyAction(KeyBindingManager.ONLY.get(newKs), newKs, e, e.getSource(), newModifiers);
 741  0 e.consume();
 742    // System.out.println("not supering 2 " + isAltF4);
 743  0 return;
 744    }
 745    }
 746   
 747    /* If the KeyEvent is not a KEY_TYPED event, process it before we do granular undo or _inCompoundEdit may
 748    * get set incorrectly. This code breaks Alt-F4, and may break other system keybindings since the event
 749    * is consumed by us. */
 750  28 if (e.getID() != KeyEvent.KEY_TYPED) {
 751  19 super.processKeyEvent(e);
 752  19 return;
 753    }
 754    }
 755    // This if statement is for tests only
 756  5 if ((e.getModifiers() & InputEvent.ALT_MASK) != 0) testVariable = true; // ALT_MASK actually pressed
 757  8 else testVariable = false;
 758   
 759  13 super.processKeyEvent(e);
 760    }
 761    }
 762    }
 763   
 764    /** Sets the editor kit that will be used by all DefinitionsPanes.
 765    * @param editorKit The editor kit to use for new DefinitionsPanes.
 766    */
 767  38 public static void setEditorKit(DefinitionsEditorKit editorKit) { EDITOR_KIT = editorKit; }
 768   
 769    /** Update the maximum character width of the current font. We can make this static, because DrJava
 770    * only supports one font for all panes anyway. */
 771  38 public static void updateMaxCharWidth(FontMetrics metrics) {
 772  38 int[] widths = metrics.getWidths();
 773  38 _maxCharWidth = 0;
 774  38 for(int w: widths) {
 775  38 if (w > _maxCharWidth) { _maxCharWidth = w; }
 776    }
 777    }
 778   
 779    /** Enable anti-aliased text by overriding paintComponent. */
 780  0 protected void paintComponent(Graphics g) {
 781  0 if (_antiAliasText && g instanceof Graphics2D) {
 782  0 Graphics2D g2d = (Graphics2D) g;
 783  0 g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
 784    }
 785  0 super.paintComponent(g);
 786    // paint the right margin line, if enabled
 787  0 if (_displayRightMargin) {
 788  0 g.setColor(_rightMarginColor);
 789  0 Rectangle view = _scrollPane.getViewport().getViewRect();
 790  0 g.drawLine(2 + _numRightMarginColumns*_maxCharWidth, view.y,
 791    2 + _numRightMarginColumns*_maxCharWidth, view.y+view.height);
 792    }
 793    }
 794   
 795    /** Creates the popup menu for the DefinitionsPane. */
 796  93 private void createPopupMenu() {
 797    // Create the popup menu.
 798  93 _popMenu = new JPopupMenu();
 799   
 800  93 _popMenu.add(_mainFrame.cutAction);
 801  93 _popMenu.add(_mainFrame.copyAction);
 802  93 _popMenu.add(_mainFrame.pasteAction);
 803  93 _popMenu.addSeparator();
 804   
 805  93 JMenuItem indentItem = new JMenuItem("Indent Line(s)");
 806  93 indentItem.addActionListener(new AbstractAction() {
 807  0 public void actionPerformed(ActionEvent ae) {
 808  0 _mainFrame.hourglassOn();
 809  0 try {
 810  0 indent();
 811    } finally {
 812  0 _mainFrame.hourglassOff();
 813    }
 814    }
 815    });
 816  93 _popMenu.add(indentItem);
 817   
 818  93 JMenuItem commentLinesItem = new JMenuItem("Comment Line(s)");
 819  93 commentLinesItem.addActionListener(new AbstractAction() {
 820  0 public void actionPerformed( ActionEvent ae) {
 821  0 _mainFrame.hourglassOn();
 822  0 try{
 823  0 updateCurrentLocationInDoc();
 824  0 _commentLines();
 825    }
 826  0 finally{ _mainFrame.hourglassOff(); }
 827    }
 828    });
 829  93 _popMenu.add(commentLinesItem);
 830   
 831  93 JMenuItem uncommentLinesItem = new JMenuItem("Uncomment Line(s)");
 832  93 uncommentLinesItem.addActionListener ( new AbstractAction() {
 833  0 public void actionPerformed( ActionEvent ae) {
 834  0 updateCurrentLocationInDoc();
 835  0 _uncommentLines();
 836    }
 837    });
 838  93 _popMenu.add(uncommentLinesItem);
 839   
 840    /* Go to this file... */
 841  93 _popMenu.addSeparator();
 842  93 JMenuItem gotoFileUnderCursorItem = new JMenuItem("Go to File Under Cursor");
 843  93 gotoFileUnderCursorItem.addActionListener ( new AbstractAction() {
 844  0 public void actionPerformed( ActionEvent ae) {
 845  0 updateCurrentLocationInDoc();
 846  0 _mainFrame._gotoFileUnderCursor();
 847    }
 848    });
 849  93 _popMenu.add(gotoFileUnderCursorItem);
 850   
 851    /* Toggle bookmark */
 852  93 JMenuItem toggleBookmarkItem = new JMenuItem("Toggle Bookmark");
 853  93 toggleBookmarkItem.addActionListener ( new AbstractAction() {
 854    /** Toggle the selected line as a bookmark. Only runs in event thread. */
 855  0 public void actionPerformed( ActionEvent ae) {
 856    // Same menu command has the same effect as KEY_BOOKMARKS_TOGGLE
 857  0 _mainFrame.toggleBookmark();
 858    }
 859    });
 860  93 _popMenu.add(toggleBookmarkItem);
 861   
 862  93 if (_mainFrame.getModel().getDebugger().isAvailable()) {
 863  93 _popMenu.addSeparator();
 864   
 865    // Breakpoint
 866  93 JMenuItem breakpointItem = new JMenuItem("Toggle Breakpoint");
 867  93 breakpointItem.addActionListener( new AbstractAction() {
 868  0 public void actionPerformed( ActionEvent ae ) {
 869    // Make sure that the breakpoint is set on the *clicked* line, if within a selection block.
 870  0 setCaretPosition(viewToModel(_popupMenuMA.getLastMouseClick().getPoint()));
 871  0 _mainFrame.debuggerToggleBreakpoint();
 872    }
 873    });
 874  93 _popMenu.add(breakpointItem);
 875    }
 876    }
 877   
 878    /* The private MouseAdapter for responding to various clicks concerning the popup menu */
 879    private class PopupMenuMouseAdapter extends RightClickMouseAdapter {
 880   
 881    private MouseEvent _lastMouseClick = null;
 882   
 883  0 public void mousePressed(MouseEvent e) {
 884  0 super.mousePressed(e);
 885   
 886  0 _lastMouseClick = e;
 887  0 endCompoundEdit();
 888   
 889    // if not in the selected area,
 890    /*if ((viewToModel(e.getPoint()) < getSelectionStart()) ||
 891    (viewToModel(e.getPoint()) > getSelectionEnd()) ) {
 892    //move caret to clicked position, deselecting previous selection
 893    setCaretPosition(viewToModel(e.getPoint()));
 894    }*/
 895    }
 896   
 897  0 protected void _popupAction(MouseEvent e) {
 898  0 requestFocusInWindow();
 899  0 _popMenu.show(e.getComponent(), e.getX(), e.getY());
 900    }
 901   
 902  0 public MouseEvent getLastMouseClick() { return _lastMouseClick; }
 903    }
 904   
 905    /** Comments out the lines contained within the given selection. */
 906  0 private void _commentLines() {
 907  0 _mainFrame.commentLines();
 908    // _doc.commentLinesInDefinitions(getSelectionStart(), getSelectionEnd());
 909    }
 910   
 911    /** Uncomments the lines contained within the given selection. */
 912  0 private void _uncommentLines() {
 913  0 _mainFrame.uncommentLines();
 914    // _doc.uncommentLinesInDefinitions(getSelectionStart(), getSelectionEnd());
 915    }
 916   
 917    /** @return the undo action. */
 918  146 public UndoAction getUndoAction() { return _undoAction; }
 919   
 920    /** @return the redo action. */
 921  188 public RedoAction getRedoAction() { return _redoAction; }
 922   
 923    /** Get the OpenDefinitionsDocument contained in this DefinitionsPane. */
 924  26 public OpenDefinitionsDocument getOpenDefDocument() { return _doc; }
 925   
 926    /** Get the DJDocument (OpenDefinitionsDocument) contained in this pane.
 927    * Required by the super class AbstractDJPane.
 928    */
 929  105 public DJDocument getDJDocument() { return _doc; }
 930   
 931    /** Access to the pane's HighlightManager */
 932  0 public HighlightManager getHighlightManager() { return _highlightManager; }
 933   
 934    /** Set the caret position and also scroll to make sure the location is visible. Should only run in the event
 935    * thread.
 936    * @param pos Location to scroll to.
 937    */
 938  0 public void setPositionAndScroll(int pos) {
 939  0 assert EventQueue.isDispatchThread();
 940  0 try {
 941  0 setCaretPos(pos);
 942  0 scrollRectToVisible(modelToView(pos));
 943    }
 944  0 catch (BadLocationException ble) { throw new UnexpectedException(ble); }
 945    }
 946   
 947    /** Override JEditorPane's setDocument to make sure only the Document in our final OpenDefinitionsDocument
 948    * can be used.
 949    */
 950  186 public void setDocument(Document d) {
 951  186 if (_doc != null) { // When can _doc be null?
 952  0 if ((d == null) || (!d.equals(_doc))) {
 953  0 throw new IllegalStateException("Cannot set the document of a DefinitionsPane to a different document.");
 954    }
 955    }
 956  186 super.setDocument(d); // If _doc is null should we do this?
 957    }
 958   
 959  5 public boolean checkAltKey() { // For tests only
 960  5 return testVariable;
 961    }
 962   
 963    /** Add a ErrorCaretListener to this pane, keeping it accessible so its error model can be updated later. */
 964  93 public void addErrorCaretListener(ErrorCaretListener eListener) {
 965  93 _errorListener = eListener;
 966  93 addCaretListener(eListener);
 967    }
 968   
 969    /** Gets the ErrorCaretListener for this pane. */
 970  103 public ErrorCaretListener getErrorCaretListener() { return _errorListener; }
 971   
 972    /** Switches the location of the error highlight in the document if there was one. Otherwise adds the
 973    * highlight. The invariant is that there are zero or one error highlights at any time.
 974    */
 975  4 public void addErrorHighlight(int from, int to) {
 976  4 removeErrorHighlight();
 977  4 _errorHighlightTag = _highlightManager.addHighlight(from, to, ERROR_PAINTER);
 978    }
 979   
 980    /** Removes the previous compiler error highlight from the document after the cursor has moved. */
 981  205 public void removeErrorHighlight() {
 982  205 if (_errorHighlightTag != null) {
 983  3 _errorHighlightTag.remove();
 984  3 _errorHighlightTag = null;
 985    }
 986    }
 987   
 988  0 public boolean hasWarnedAboutModified() { return _hasWarnedAboutModified; }
 989   
 990  0 public void hasWarnedAboutModified( boolean hasWarned) {
 991  0 _hasWarnedAboutModified = hasWarned;
 992    }
 993   
 994  0 public void addBreakpointHighlight( Breakpoint bp ) { }
 995   
 996  0 public void removeBreakpointHighlight( Breakpoint bp) { }
 997   
 998    /** This instance of the scroll pane is here in order to allow for the definitions pane to save the
 999    * horizontal and vertical scroll
 1000    */
 1001    private volatile JScrollPane _scrollPane;
 1002   
 1003  93 public void setScrollPane(JScrollPane s) { _scrollPane = s; }
 1004   
 1005    /** Used to save the caret position, selection, and scroll when setting the definitions pane to be inactive */
 1006    private volatile int _savedVScroll;
 1007    private volatile int _savedHScroll;
 1008    private volatile int _position;
 1009    private volatile int _selStart;
 1010    private volatile int _selEnd;
 1011   
 1012    /** This function is called when the active document is changed. this function is called on the pane that is
 1013    * replaced by the new active pane. It allows the pane to "shutdown" when not in use. Currently, this procedure
 1014    * replaces the Definitions Document with a blank dummy document to help conserve memory (so that the pane will
 1015    * not be holding onto the last reference of a definitions document not allowing it to be garbage collected)
 1016    */
 1017  66 public void notifyInactive() {
 1018    // we catch a NoSuchDocumentException here because during a close/closeAll
 1019    // the model closes the definitions document before the MainFrame switches
 1020    // out the panes. If this is the case, then the following code does not
 1021    // need to be run.
 1022  66 try {
 1023    // Sync caret with location before switching
 1024  66 updateCurrentLocationInDoc();
 1025   
 1026    // Remove any error highlighting in the old def pane
 1027  66 removeErrorHighlight();
 1028   
 1029  66 _position = _doc.getCurrentLocation();
 1030  66 _selStart = super.getSelectionStart();
 1031  66 _selEnd = super.getSelectionEnd();
 1032   
 1033  66 _savedVScroll = _scrollPane.getVerticalScrollBar().getValue();
 1034  66 _savedHScroll = _scrollPane.getHorizontalScrollBar().getValue();
 1035   
 1036  66 super.setDocument(NULL_DOCUMENT);
 1037    }
 1038    catch(NoSuchDocumentException e) {
 1039    // This exception was just thrown because the document was just
 1040    // closed and so this pane will soon be garbage collected.
 1041    // We don't need to do any more cleanup.
 1042    }
 1043    }
 1044   
 1045    /** This function is called when switching a pane to be the active document pane. It allows the pane to do whatever
 1046    * "startUp" is required. Since setInactive swapped out the document for a dummy document, we need to reload the
 1047    * actual document and reset its caret position to the saved location. Only runs in event thread.
 1048    */
 1049  104 public void notifyActive() {
 1050  104 assert ! _mainFrame.isVisible() || EventQueue.isDispatchThread();
 1051  104 super.setDocument(_doc);
 1052  88 if (_doc.getUndoableEditListeners().length == 0) _resetUndo();
 1053   
 1054  104 int len = _doc.getLength();
 1055  104 if (len < _position || len < _selEnd) {
 1056    // the document changed since we're set inactive
 1057    //so set selection to be none
 1058  3 _position = len;
 1059  3 _selStart = len;
 1060  3 _selEnd = len;
 1061    }
 1062  104 if (_position == _selStart) {
 1063  104 setCaretPosition(_selEnd);
 1064  104 moveCaretPosition(_selStart);
 1065  104 _doc.setCurrentLocation(_selStart);
 1066    }
 1067    else {
 1068  0 setCaretPosition(_selStart);
 1069  0 moveCaretPosition(_selEnd);
 1070  0 _doc.setCurrentLocation(_selEnd);
 1071    }
 1072  104 _scrollPane.getVerticalScrollBar().setValue(_savedVScroll);
 1073  104 _scrollPane.getHorizontalScrollBar().setValue(_savedHScroll);
 1074    // Explicitly set scrollbar policies fixing bug #1445898
 1075  104 _scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
 1076  104 _scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
 1077    }
 1078   
 1079    // if the pane is inactive, use the state stored in the fields, otherwise use the super method
 1080  6 public int getSelectionStart() {
 1081  0 if (getDocument() == NULL_DOCUMENT) return _selStart;
 1082  6 else return super.getSelectionStart();
 1083    }
 1084   
 1085  6 public int getSelectionEnd() {
 1086  0 if (getDocument() == NULL_DOCUMENT) return _selEnd;
 1087  6 else return super.getSelectionEnd();
 1088    }
 1089   
 1090  3 public int getVerticalScroll() {
 1091  0 if (getDocument() == NULL_DOCUMENT) return _savedVScroll;
 1092  3 else return _scrollPane.getVerticalScrollBar().getValue();
 1093    }
 1094   
 1095  3 public int getHorizontalScroll() {
 1096  0 if (getDocument() == NULL_DOCUMENT) return _savedHScroll;
 1097  3 else return _scrollPane.getHorizontalScrollBar().getValue();
 1098    }
 1099   
 1100    /** Returns the current line of the definitions pane. This is a 1-based number.
 1101    * @return current line of the definitions pane, >=1 */
 1102  0 public int getCurrentLine() { return _doc.getLineOfOffset(getCaretPosition())+1; }
 1103    // try {
 1104    // int pos = getCaretPosition();
 1105    // FontMetrics metrics = getFontMetrics(getFont());
 1106    // Rectangle startRect = modelToView(pos);
 1107    // if (startRect == null) return 1;
 1108    // //top left position is (3,3), so font size<=6 will be off
 1109    // return (new Double (startRect.getY() / metrics.getHeight()).intValue() + 1);
 1110    // } catch (BadLocationException e) {
 1111    // // This shouldnt happen b/c we retrieve the caret pos before calling modelToView
 1112    // throw new UnexpectedException(e);
 1113    // }
 1114    // }
 1115   
 1116    /** Determines current line using logic in DefinitionsDocument. Does it differ from getCurrentLine()? */
 1117  0 public int getCurrentLinefromDoc() { return _doc.getCurrentLine(); }
 1118   
 1119  0 public int getCurrentCol() { return _doc.getCurrentCol(); }
 1120   
 1121  63 public void setSize(int width, int height) {
 1122  63 super.setSize(width, height);
 1123  0 if (_setSizeListener != null) _setSizeListener.actionPerformed(null);
 1124    }
 1125   
 1126    // public void addSetSizeListener(ActionListener listener) { _setSizeListener = listener; }
 1127    // public void removeSetSizeListener() { _setSizeListener = null; }
 1128   
 1129    /** Centers the view (pane) on the specified offset. */
 1130  1 public void centerViewOnOffset(int offset) {
 1131  1 assert EventQueue.isDispatchThread();
 1132  1 try {
 1133  1 FontMetrics metrics = getFontMetrics(getFont());
 1134  1 JViewport defViewPort = _mainFrame.getDefViewport();
 1135  1 double viewWidth = defViewPort.getWidth();
 1136  1 double viewHeight = defViewPort.getHeight();
 1137    // Scroll to make sure this item is visible
 1138    // Centers the selection in the viewport
 1139  1 Rectangle startRect;
 1140  1 startRect = modelToView(offset);
 1141   
 1142  1 if (startRect != null) {
 1143  0 int startRectX = (int) startRect.getX();
 1144  0 int startRectY = (int) startRect.getY();
 1145  0 startRect.setLocation(startRectX - (int)(viewWidth*.5), startRectY - (int)(viewHeight*.5));
 1146  0 Point endPoint = new Point(startRectX + (int)(viewWidth*.5),
 1147    startRectY + (int)(viewHeight*.5) + metrics.getHeight()/2);
 1148   
 1149    // Add the end rect onto the start rect to make a rectangle
 1150    // that encompasses the entire selection
 1151  0 startRect.add(endPoint);
 1152   
 1153  0 scrollRectToVisible(startRect);
 1154    }
 1155    // removeSetSizeListener(); // Why? None was added
 1156   
 1157  1 setCaretPos(offset);
 1158    }
 1159  0 catch (BadLocationException e) { throw new UnexpectedException(e); }
 1160    }
 1161   
 1162  0 public void centerViewOnLine(int lineNumber) {
 1163  0 FontMetrics metrics = getFontMetrics(getFont());
 1164  0 Point p = new Point(0, metrics.getHeight() * (lineNumber));
 1165  0 int offset = this.viewToModel(p);
 1166  0 this.centerViewOnOffset(offset);
 1167    }
 1168   
 1169    /** This method overrides a broken version in JTextComponent. It allows selection to proceed backwards as well as
 1170    * forwards. If selection is backwards, then the caret ends up at the start of the selection rather than the end.
 1171    */
 1172  2 public void select(int selectionStart, int selectionEnd) {
 1173  2 setCaretPosition(selectionStart);
 1174  2 moveCaretPosition(selectionEnd); // What about the caret position in the reduced model? It is updated by a listener.
 1175    }
 1176   
 1177    /** Reset the document Undo list. */
 1178  0 public void resetUndo() {
 1179  0 _doc.getUndoManager().discardAllEdits();
 1180   
 1181  0 _undoAction.updateUndoState();
 1182  0 _redoAction.updateRedoState();
 1183    }
 1184   
 1185    /** Reset the document Undo list. */
 1186  181 private void _resetUndo() {
 1187  93 if (_undoAction == null) _undoAction = new UndoAction();
 1188  93 if (_redoAction == null) _redoAction = new RedoAction();
 1189   
 1190  181 _doc.resetUndoManager();
 1191   
 1192  181 getDocument().addUndoableEditListener(_undoListener);
 1193  181 _undoAction.updateUndoState();
 1194  181 _redoAction.updateRedoState();
 1195    }
 1196   
 1197   
 1198    /** Overriding this method ensures that all new documents created in this editor pane use our editor
 1199    * kit (and thus our model).
 1200    */
 1201  186 protected EditorKit createDefaultEditorKit() {
 1202    //return _editorKit;
 1203  186 return EDITOR_KIT;
 1204    }
 1205   
 1206    /** Prompts the user whether or not they wish to indent, if the selection size is very large.
 1207    * @return true if the indent is to be completed
 1208    * @param selStart - the selection start
 1209    * @param selEnd - the selection end
 1210    */
 1211  1 protected boolean shouldIndent(int selStart, int selEnd) {
 1212  1 if (selEnd > (selStart + INDENT_WARNING_THRESHOLD)) {
 1213  0 Object[] options = {"Yes", "No"};
 1214  0 int n = JOptionPane.showOptionDialog
 1215    (_mainFrame,
 1216    "Re-indenting this block may take a long time. Are you sure?",
 1217    "Confirm Re-indent",
 1218    JOptionPane.YES_NO_OPTION,
 1219    JOptionPane.QUESTION_MESSAGE,
 1220    null,
 1221    options,
 1222    options[1]);
 1223  0 switch (n) {
 1224  0 case JOptionPane.CANCEL_OPTION:
 1225  0 case JOptionPane.CLOSED_OPTION:
 1226  0 case JOptionPane.NO_OPTION:
 1227  0 return false;
 1228  0 default:
 1229  0 return true;
 1230    }
 1231    }
 1232  1 return true;
 1233    }
 1234   
 1235    /** Indent the given selection, for the given reason, in the current document.
 1236    * @param selStart - the selection start
 1237    * @param selEnd - the selection end
 1238    * @param reason - the reason for the indent
 1239    * @param pm - the ProgressMonitor used by the indenter
 1240    */
 1241  1 protected void indentLines(int selStart, int selEnd, Indenter.IndentReason reason, ProgressMonitor pm) {
 1242    //_mainFrame.hourglassOn();
 1243    // final int key = _doc.getUndoManager().startCompoundEdit(); //Commented out in regards to French KeyBoard Fix
 1244  1 assert EventQueue.isDispatchThread();
 1245  1 try {
 1246  1 _doc.indentLines(selStart, selEnd, reason, pm);
 1247  1 endCompoundEdit();
 1248  1 setCaretPosition(_doc.getCurrentLocation()); // redundant?
 1249    }
 1250    catch(OperationCanceledException oce) {
 1251    // if canceled, undo the indent; but first, end compound edit
 1252  0 endCompoundEdit();
 1253  0 _doc.getUndoManager().undo();
 1254    // pm = null, so cancel can't be pressed
 1255  0 throw new UnexpectedException(oce);
 1256    }
 1257    }
 1258   
 1259    /** Saved option listeners kept in this field so they can be removed for garbage collection */
 1260    private List<Pair<Option<Color>, OptionListener<Color>>> _colorOptionListeners =
 1261    new LinkedList<Pair<Option<Color>, OptionListener<Color>>>();
 1262   
 1263    private List<Pair<Option<Boolean>, OptionListener<Boolean>>> _booleanOptionListeners =
 1264    new LinkedList<Pair<Option<Boolean>, OptionListener<Boolean>>>();
 1265   
 1266    private List<Pair<Option<Integer>, OptionListener<Integer>>> _integerOptionListeners =
 1267    new LinkedList<Pair<Option<Integer>, OptionListener<Integer>>>();
 1268   
 1269    /** Called when the definitions pane is released from duty. This frees up any option listeners that are holding
 1270    * references to this object so this can be garbage collected.
 1271    */
 1272  37 public void close() {
 1273  37 for (Pair<Option<Color>, OptionListener<Color>> p: _colorOptionListeners) {
 1274  629 DrJava.getConfig().removeOptionListener(p.first(), p.second());
 1275    }
 1276  37 for (Pair<Option<Boolean>, OptionListener<Boolean>> p: _booleanOptionListeners) {
 1277  74 DrJava.getConfig().removeOptionListener(p.first(), p.second());
 1278    }
 1279  37 for (Pair<Option<Integer>, OptionListener<Integer>> p: _integerOptionListeners) {
 1280  37 DrJava.getConfig().removeOptionListener(p.first(), p.second());
 1281    }
 1282  37 _colorOptionListeners.clear();
 1283  37 _booleanOptionListeners.clear();
 1284  37 _integerOptionListeners.clear();
 1285   
 1286  37 ourMap.removeBindings();
 1287  37 removeKeymap(ourMap.getName());
 1288   
 1289  37 _popMenu.removeAll();
 1290    }
 1291   
 1292    /** The undo action. */
 1293    public class UndoAction extends AbstractAction {
 1294   
 1295    /** Constructor. */
 1296  93 private UndoAction() {
 1297  93 super("Undo");
 1298  93 setEnabled(false);
 1299    }
 1300   
 1301    /** What to do when user chooses to undo.
 1302    * @param e
 1303    */
 1304  1 public void actionPerformed(ActionEvent e) {
 1305  1 try {
 1306    // UndoableEdit edit = _doc.getNextUndo();
 1307    // int pos = -1;
 1308    // if (edit != null && edit instanceof UndoWithPosition) {
 1309    // pos = ((UndoWithPosition)edit).getPosition();
 1310    // }
 1311    //
 1312    // if (pos > -1) {
 1313    // //centerViewOnOffset(pos);
 1314    // setCaretPosition(pos);
 1315    // }
 1316  1 _doc.getUndoManager().undo();
 1317  1 _doc.updateModifiedSinceSave();
 1318  1 _mainFrame.updateStatusField();
 1319    }
 1320    catch (CannotUndoException ex) {
 1321  0 throw new UnexpectedException(ex);
 1322    }
 1323  1 updateUndoState();
 1324  1 _redoAction.updateRedoState();
 1325    }
 1326   
 1327    /** Updates the undo list, i.e., where we are as regards undo and redo. */
 1328  224 protected void updateUndoState() {
 1329  224 if (_doc.undoManagerCanUndo()) {
 1330  31 setEnabled(true);
 1331  31 putValue(Action.NAME, _doc.getUndoManager().getUndoPresentationName());
 1332    }
 1333    else {
 1334  193 setEnabled(false);
 1335  193 putValue(Action.NAME, "Undo");
 1336    }
 1337    }
 1338    }
 1339   
 1340    /** Redo action. */
 1341    public class RedoAction extends AbstractAction {
 1342   
 1343    /** Constructor. */
 1344  93 private RedoAction() {
 1345  93 super("Redo");
 1346  93 setEnabled(false);
 1347    }
 1348   
 1349    /** In the event that the user chooses to redo something, this is what's called.
 1350    * @param e
 1351    */
 1352  0 public void actionPerformed(ActionEvent e) {
 1353  0 try {
 1354    // UndoableEdit edit = _doc.getNextRedo();
 1355    // int pos = -1;
 1356    // if (edit instanceof UndoWithPosition) {
 1357    // pos = ((UndoWithPosition)edit).getPosition();
 1358    // }
 1359  0 _doc.getUndoManager().redo();
 1360   
 1361    // if (pos > -1) {
 1362    // //centerViewOnOffset(pos);
 1363    // setCaretPosition(pos);
 1364    // }
 1365  0 _doc.updateModifiedSinceSave();
 1366  0 _mainFrame.updateStatusField();
 1367    } catch (CannotRedoException ex) {
 1368  0 throw new UnexpectedException(ex);
 1369    }
 1370  0 updateRedoState();
 1371  0 _undoAction.updateUndoState();
 1372    }
 1373   
 1374    /** Updates the redo state, i.e., where we are as regards undo and redo. */
 1375  224 protected void updateRedoState() {
 1376  224 if (_doc.undoManagerCanRedo()) {
 1377  5 setEnabled(true);
 1378  5 putValue(Action.NAME, _doc.getUndoManager().getRedoPresentationName());
 1379    }
 1380    else {
 1381  219 setEnabled(false);
 1382  219 putValue(Action.NAME, "Redo");
 1383    }
 1384    }
 1385    }
 1386   
 1387    // /** Wrapper for UndoableEdit that pairs UndoableEdits with their caret positions */
 1388    // private class UndoWithPosition implements UndoableEdit {
 1389    // private final UndoableEdit _undo;
 1390    // private final int _pos;
 1391    //
 1392    // public UndoWithPosition(UndoableEdit undo, int pos) {
 1393    // _undo = undo;
 1394    // _pos = pos;
 1395    // }
 1396    //
 1397    // public int getPosition() { return _pos; }
 1398    // public boolean addEdit(UndoableEdit ue) { return _undo.addEdit(ue); }
 1399    // public boolean canRedo() { return _undo.canRedo(); }
 1400    // public boolean canUndo() { return _undo.canUndo(); }
 1401    // public void die() { _undo.die(); }
 1402    // public String getPresentationName() { return _undo.getPresentationName(); }
 1403    // public String getUndoPresentationName() { return _undo.getUndoPresentationName(); }
 1404    // public String getRedoPresentationName() { return _undo.getRedoPresentationName(); }
 1405    // public boolean isSignificant() { return _undo.isSignificant(); }
 1406    //
 1407    // public void redo() {
 1408    // _undo.redo();
 1409    // if (_pos > -1) {
 1410    // _doc.setCurrentLocation(_pos); // probably unnecessary
 1411    // setCaretPosition(_pos);
 1412    // }
 1413    // }
 1414    //
 1415    // public boolean replaceEdit(UndoableEdit ue) { return _undo.replaceEdit(ue); }
 1416    //
 1417    // public void undo() {
 1418    // if (_pos > -1) {
 1419    // _doc.setCurrentLocation(_pos);
 1420    // setCaretPosition(_pos);
 1421    // }
 1422    // _undo.undo();
 1423    // }
 1424    // }
 1425   
 1426    /** This list of listeners to notify when we are finalized */
 1427    private List<FinalizationListener<DefinitionsPane>> _finalizationListeners =
 1428    new LinkedList<FinalizationListener<DefinitionsPane>>();
 1429   
 1430    /** Registers a finalization listener with the specific instance of the ddoc. NOTE: this should only be used by test
 1431    * cases. This policy ensures that we don't spring memory leaks by allowing our unit tests to keep track of
 1432    * whether objects are being finalized (garbage collected).
 1433    * @param fl the listener to register
 1434    */
 1435  6 public void addFinalizationListener(FinalizationListener<DefinitionsPane> fl) { _finalizationListeners.add(fl); }
 1436   
 1437  0 public List<FinalizationListener<DefinitionsPane>> getFinalizationListeners() { return _finalizationListeners; }
 1438   
 1439    /** This method is called when this object becomes unreachable. Since this class implements
 1440    * edu.rice.cs.drjava.model.Finalizable, it must notify its listeners.
 1441    */
 1442  26 protected void finalize() {
 1443  26 FinalizationEvent<DefinitionsPane> fe = new FinalizationEvent<DefinitionsPane>(this);
 1444  6 for (FinalizationListener<DefinitionsPane> fl: _finalizationListeners) fl.finalized(fe);
 1445    }
 1446    }