001: /*BEGIN_COPYRIGHT_BLOCK
002: *
003: * Copyright (c) 2001-2007, JavaPLT group at Rice University (javaplt@rice.edu)
004: * All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions are met:
008: * * Redistributions of source code must retain the above copyright
009: * notice, this list of conditions and the following disclaimer.
010: * * Redistributions in binary form must reproduce the above copyright
011: * notice, this list of conditions and the following disclaimer in the
012: * documentation and/or other materials provided with the distribution.
013: * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
014: * names of its contributors may be used to endorse or promote products
015: * derived from this software without specific prior written permission.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
018: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
019: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
020: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
021: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
022: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
023: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
024: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
025: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
026: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
027: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028: *
029: * This software is Open Source Initiative approved Open Source Software.
030: * Open Source Initative Approved is a trademark of the Open Source Initiative.
031: *
032: * This file is part of DrJava. Download the current version of this project
033: * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
034: *
035: * END_COPYRIGHT_BLOCK*/
036:
037: package edu.rice.cs.drjava.ui;
038:
039: import edu.rice.cs.drjava.DrJava;
040: import edu.rice.cs.drjava.config.OptionConstants;
041: import edu.rice.cs.drjava.config.OptionEvent;
042: import edu.rice.cs.drjava.config.OptionListener;
043: import edu.rice.cs.drjava.model.OpenDefinitionsDocument;
044: import edu.rice.cs.drjava.model.SingleDisplayModel; //import edu.rice.cs.drjava.model.DefaultDJDocument;
045: import edu.rice.cs.drjava.model.compiler.CompilerError;
046: import edu.rice.cs.drjava.model.compiler.CompilerErrorModel;
047: import edu.rice.cs.drjava.model.ClipboardHistoryModel;
048: import edu.rice.cs.util.UnexpectedException;
049: import edu.rice.cs.util.swing.HighlightManager;
050: import edu.rice.cs.util.swing.BorderlessScrollPane;
051: import edu.rice.cs.util.text.SwingDocument;
052:
053: // TODO: Check synchronization.
054: import java.util.Hashtable;
055:
056: import javax.swing.*;
057: import javax.swing.text.*;
058: import javax.swing.text.SimpleAttributeSet;
059: import javax.swing.border.EmptyBorder;
060: import java.awt.datatransfer.*;
061: import java.awt.*;
062: import java.awt.event.MouseAdapter;
063: import java.awt.event.MouseEvent;
064: import java.awt.event.ItemListener;
065: import java.awt.event.ItemEvent;
066: import java.awt.event.ActionListener;
067: import java.awt.event.ActionEvent;
068: import java.io.IOException;
069:
070: /** This class contains common code and interfaces from CompilerErrorPanel, JUnitPanel, and JavadocErrorPanel.
071: * TODO: parameterize the types of CompilerErrors used here
072: * @version $Id: ErrorPanel.java 4255 2007-08-28 19:17:37Z mgricken $
073: */
074: public abstract class ErrorPanel extends TabbedPanel implements
075: OptionConstants {
076:
077: protected static final SimpleAttributeSet NORMAL_ATTRIBUTES = _getNormalAttributes();
078: protected static final SimpleAttributeSet BOLD_ATTRIBUTES = _getBoldAttributes();
079:
080: /** The total number of errors in the list */
081: protected volatile int _numErrors;
082: protected volatile JCheckBox _showHighlightsCheckBox;
083:
084: // TODO: is this necessary, or can we get by with installing a domain-specific
085: // model in the constructor - e.g. JavadocModel
086: protected SingleDisplayModel _model;
087:
088: private JScrollPane _scroller;
089:
090: /** This contains the _scroller and the _errorNavPanel. */
091: private JPanel _leftPanel;
092:
093: /** This contains the label, showHighlightsCheckBox, and the customPanel. */
094: private JPanel _rightPanel;
095:
096: private JPanel _errorNavPanel;
097:
098: private JPanel _errorNavButtonsPanel;
099:
100: /** This JPanel contains each child panel's specific UI components. **/
101: protected JPanel customPanel;
102:
103: private JButton _nextErrorButton;
104: private JButton _prevErrorButton;
105:
106: /** Highlight painter for selected list items. */
107: static ReverseHighlighter.DrJavaHighlightPainter _listHighlightPainter = new ReverseHighlighter.DrJavaHighlightPainter(
108: DrJava.getConfig().getSetting(COMPILER_ERROR_COLOR));
109:
110: protected static final SimpleAttributeSet _getBoldAttributes() {
111: SimpleAttributeSet s = new SimpleAttributeSet();
112: StyleConstants.setBold(s, true);
113: return s;
114: }
115:
116: protected static final SimpleAttributeSet _getNormalAttributes() {
117: SimpleAttributeSet s = new SimpleAttributeSet();
118: return s;
119: }
120:
121: public ErrorPanel(SingleDisplayModel model, MainFrame frame,
122: String tabString, String labelString) {
123: super (frame, tabString);
124: _model = model;
125:
126: _mainPanel.setLayout(new BorderLayout());
127:
128: _leftPanel = new JPanel(new BorderLayout());
129:
130: _errorNavPanel = new JPanel(new GridBagLayout());
131:
132: /******** Initialize the error navigation buttons ********/
133: _errorNavButtonsPanel = new JPanel(new BorderLayout());
134:
135: _nextErrorButton = new JButton(MainFrame.getIcon("Down16.gif"));//new JButton("Next Error");
136: _prevErrorButton = new JButton(MainFrame.getIcon("Up16.gif"));//new JButton("Prev Error");
137:
138: _nextErrorButton.setMargin(new Insets(0, 0, 0, 0));
139: _nextErrorButton.setToolTipText("Go to the next error");
140: _prevErrorButton.setMargin(new Insets(0, 0, 0, 0));
141: _prevErrorButton.setToolTipText("Go to the previous error");
142:
143: // _errorPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 3));
144: // _errorPanel.setPreferredSize(new Dimension(27,35));
145: // _errorPanel.add(_prevErrorButton);
146: // _errorPanel.add(_nextErrorButton);
147: // _uiBox.add(_errorPanel, BorderLayout.WEST);
148: _errorNavButtonsPanel.add(_prevErrorButton, BorderLayout.NORTH);
149: _errorNavButtonsPanel.add(_nextErrorButton, BorderLayout.SOUTH);
150: _errorNavButtonsPanel.setBorder(new EmptyBorder(18, 5, 18, 5)); // 5 pix padding on sides
151:
152: // JPanel middlePanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
153: // middlePanel.add(_errorNavButtonsPanel);
154:
155: _errorNavPanel.add(_errorNavButtonsPanel);//, BorderLayout.CENTER);
156: _showHighlightsCheckBox = new JCheckBox("Highlight source",
157: true);
158:
159: // _mainPanel.setMinimumSize(new Dimension(225,60));
160: // We make the vertical scrollbar always there.
161: // If we don't, when it pops up it cuts away the right edge of the
162: // text. Very bad.
163: _scroller = new BorderlessScrollPane(
164: JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
165: JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
166:
167: _leftPanel.add(_scroller, BorderLayout.CENTER);
168: _leftPanel.add(_errorNavPanel, BorderLayout.EAST);
169:
170: customPanel = new JPanel(new BorderLayout());
171: _rightPanel = new JPanel(new BorderLayout());
172: _rightPanel.setBorder(new EmptyBorder(0, 5, 0, 5)); // 5 pix padding on sides
173: // uiBox.setBorder(new EmptyBorder(5,0,0,0)); // 5 pix padding on top
174: _rightPanel.add(new JLabel(labelString, SwingConstants.LEFT),
175: BorderLayout.NORTH);
176: _rightPanel.add(customPanel, BorderLayout.CENTER);
177: _rightPanel.add(_showHighlightsCheckBox, BorderLayout.SOUTH);
178:
179: _mainPanel.add(_leftPanel, BorderLayout.CENTER);
180: _mainPanel.add(_rightPanel, BorderLayout.EAST);
181: }
182:
183: protected void setErrorListPane(final ErrorListPane elp) {
184: _scroller.setViewportView(elp);
185: _nextErrorButton.setEnabled(false);
186: _nextErrorButton.addActionListener(new ActionListener() {
187: public void actionPerformed(ActionEvent e) {
188: elp.nextError();
189: // _prevErrorButton.setEnabled(_errorListPane.hasPrevError());
190: // _nextErrorButton.setEnabled(_errorListPane.hasNextError());
191: }
192: });
193: _prevErrorButton.setEnabled(false);
194: _prevErrorButton.addActionListener(new ActionListener() {
195: public void actionPerformed(ActionEvent e) {
196: elp.prevError();
197: // _prevErrorButton.setEnabled(_errorListPane.hasPrevError());
198: // _nextErrorButton.setEnabled(_errorListPane.hasNextError());
199: }
200: });
201: }
202:
203: /** Changes the font of the error list. */
204: public void setListFont(Font f) {
205: SimpleAttributeSet set = new SimpleAttributeSet();
206: StyleConstants.setFontFamily(set, f.getFamily());
207: StyleConstants.setFontSize(set, f.getSize());
208: StyleConstants.setBold(set, f.isBold());
209: StyleConstants.setItalic(set, f.isItalic());
210:
211: _updateStyles(set);
212:
213: getErrorListPane().setFont(f);
214:
215: SwingDocument doc = getErrorListPane().getSwingDocument();
216: doc.acquireWriteLock();
217: try {
218: doc.setCharacterAttributes(0, doc.getLength() + 1, set,
219: false);
220: } finally {
221: doc.releaseWriteLock();
222: }
223: }
224:
225: /** Updates all document styles with the attributes contained in newSet.
226: * @param newSet Style containing new attributes to use.
227: */
228: protected void _updateStyles(AttributeSet newSet) {
229: NORMAL_ATTRIBUTES.addAttributes(newSet);
230: BOLD_ATTRIBUTES.addAttributes(newSet);
231: StyleConstants.setBold(BOLD_ATTRIBUTES, true); // bold should always be bold
232: }
233:
234: abstract protected ErrorListPane getErrorListPane();
235:
236: protected SingleDisplayModel getModel() {
237: return _model;
238: }
239:
240: /**
241: * This function returns the correct error model
242: */
243: abstract protected CompilerErrorModel getErrorModel();
244:
245: /** Pane to show compiler errors. Similar to a listbox (clicking selects an item) but items can each wrap, etc. */
246: public abstract class ErrorListPane extends JEditorPane implements
247: ClipboardOwner {
248: /** The custom keymap for the error list pane. */
249: protected Keymap _keymap;
250:
251: /** Index into _errorListPositions of the currently selected error. */
252: private int _selectedIndex;
253:
254: /**
255: * The start position of each error in the list. This position is the place
256: * where the error starts in the error list, as opposed to the place where
257: * the error exists in the source.
258: */
259: protected Position[] _errorListPositions;
260:
261: /** Table mapping Positions in the error list to CompilerErrors. */
262: protected final Hashtable<Position, CompilerError> _errorTable = new Hashtable<Position, CompilerError>();
263:
264: // when we create a highlight we get back a tag we can use to remove it
265: private HighlightManager.HighlightInfo _listHighlightTag = null;
266:
267: private HighlightManager _highlightManager = new HighlightManager(
268: this );
269:
270: protected MouseAdapter defaultMouseListener = new MouseAdapter() {
271: public void mousePressed(MouseEvent e) {
272: selectNothing();
273: }
274:
275: public void mouseReleased(MouseEvent e) {
276: CompilerError error = _errorAtPoint(e.getPoint());
277:
278: if (_isEmptySelection() && error != null)
279: getErrorListPane().switchToError(error);
280: else
281: selectNothing();
282: }
283: };
284:
285: // private Hashtable<Position, CompilerError> _setUpErrorTable() {
286: // return new Hashtable<Position, CompilerError>();
287: // }
288:
289: /** Constructs the CompilerErrorListPane.*/
290: public ErrorListPane() {
291: // // If we set this pane to be of type text/rtf, it wraps based on words
292: // // as opposed to based on characters.
293:
294: setContentType("text/rtf");
295: setDocument(new SwingDocument());
296: setHighlighter(new ReverseHighlighter());
297:
298: addMouseListener(defaultMouseListener);
299:
300: _selectedIndex = 0;
301: _errorListPositions = new Position[0];
302:
303: this .setFont(new Font("Courier", 0, 20));
304:
305: // We set the editor pane disabled so it won't get keyboard focus,
306: // which makes it uneditable, and so you can't select text inside it.
307: //setEnabled(false);
308:
309: // Set the editor pane to be uneditable, but allow selecting text.
310: setEditable(false);
311:
312: DrJava.getConfig().addOptionListener(COMPILER_ERROR_COLOR,
313: new CompilerErrorColorOptionListener());
314:
315: // Set the colors.
316: StyleConstants.setForeground(NORMAL_ATTRIBUTES, DrJava
317: .getConfig().getSetting(DEFINITIONS_NORMAL_COLOR));
318: StyleConstants.setForeground(BOLD_ATTRIBUTES, DrJava
319: .getConfig().getSetting(DEFINITIONS_NORMAL_COLOR));
320: setBackground(DrJava.getConfig().getSetting(
321: DEFINITIONS_BACKGROUND_COLOR));
322:
323: // Add OptionListeners for the colors.
324: DrJava.getConfig().addOptionListener(
325: DEFINITIONS_NORMAL_COLOR,
326: new ForegroundColorListener());
327: DrJava.getConfig().addOptionListener(
328: DEFINITIONS_BACKGROUND_COLOR,
329: new BackgroundColorListener());
330:
331: /* Item listener instead of change listener so that this code won't be called (twice) every time the mouse moves
332: * over the _showHighlightsCheckBox (5/26/05)
333: */
334: _showHighlightsCheckBox.addItemListener(new ItemListener() {
335: public void itemStateChanged(ItemEvent e) {
336: DefinitionsPane lastDefPane = _frame
337: .getCurrentDefPane();
338:
339: if (e.getStateChange() == ItemEvent.DESELECTED) {
340: lastDefPane.removeErrorHighlight();
341: }
342:
343: else if (e.getStateChange() == ItemEvent.SELECTED) {
344: getErrorListPane().switchToError(
345: getSelectedIndex());
346: // Commented out because they are redudant; done in switchToError(...)
347: // DefinitionsPane curDefPane = _frame.getCurrentDefPane();
348: // lastDefPane.requestFocusInWindow();
349: // lastDefPane.getCaret().setVisible(true);
350: }
351: }
352: });
353:
354: _keymap = addKeymap("ERRORLIST_KEYMAP", getKeymap());
355:
356: addActionForKeyStroke(DrJava.getConfig().getSetting(
357: OptionConstants.KEY_CUT), cutAction);
358: addActionForKeyStroke(DrJava.getConfig().getSetting(
359: OptionConstants.KEY_COPY), copyAction);
360: addActionForKeyStroke(DrJava.getConfig().getSetting(
361: OptionConstants.KEY_PASTE_FROM_HISTORY),
362: pasteAction);
363: DrJava.getConfig().addOptionListener(
364: OptionConstants.KEY_CUT,
365: new OptionListener<KeyStroke>() {
366: public void optionChanged(
367: OptionEvent<KeyStroke> oe) {
368: addActionForKeyStroke(
369: DrJava.getConfig().getSetting(
370: OptionConstants.KEY_CUT),
371: cutAction);
372: }
373: });
374: DrJava.getConfig().addOptionListener(
375: OptionConstants.KEY_COPY,
376: new OptionListener<KeyStroke>() {
377: public void optionChanged(
378: OptionEvent<KeyStroke> oe) {
379: addActionForKeyStroke(DrJava.getConfig()
380: .getSetting(
381: OptionConstants.KEY_COPY),
382: copyAction);
383: }
384: });
385: DrJava.getConfig().addOptionListener(
386: OptionConstants.KEY_PASTE_FROM_HISTORY,
387: new OptionListener<KeyStroke>() {
388: public void optionChanged(
389: OptionEvent<KeyStroke> oe) {
390: addActionForKeyStroke(
391: DrJava
392: .getConfig()
393: .getSetting(
394: OptionConstants.KEY_PASTE_FROM_HISTORY),
395: pasteAction);
396: }
397: });
398: }
399:
400: /** Gets the SwingDocument associated with this ErrorListPane. The inherited getDocument method must be preserved
401: * because the ErrorListPane constructor uses it fetch a Document that is NOT a SwingDocument. ErrorListPane
402: * immediately sets the Document corresponding to this JEditorPane to a SwingDocument and strictly maintains it as
403: * a SwingDocument, but the JEditorPane constructor binds its document to a PlainDocument and uses getDocument
404: * before ErrorListPane can set this field to a SwingDocument.
405: */
406: public SwingDocument getSwingDocument() {
407: return (SwingDocument) getDocument();
408: }
409:
410: /** Assigns the given keystroke to the given action in this pane.
411: * @param stroke keystroke that triggers the action
412: * @param action Action to perform
413: */
414: public void addActionForKeyStroke(KeyStroke stroke,
415: Action action) {
416: // we don't want multiple keys bound to the same action
417: KeyStroke[] keys = _keymap.getKeyStrokesForAction(action);
418: if (keys != null) {
419: for (int i = 0; i < keys.length; i++) {
420: _keymap.removeKeyStrokeBinding(keys[i]);
421: }
422: }
423: _keymap.addActionForKeyStroke(stroke, action);
424: setKeymap(_keymap);
425: }
426:
427: /** We lost ownership of what we put in the clipboard. */
428: public void lostOwnership(Clipboard clipboard,
429: Transferable contents) {
430: // ignore
431: }
432:
433: /** Default cut action. */
434: Action cutAction = new DefaultEditorKit.CutAction() {
435: public void actionPerformed(ActionEvent e) {
436: if (getSelectedText() != null) {
437: super .actionPerformed(e);
438: String s = edu.rice.cs.util.swing.Utilities
439: .getClipboardSelection(ErrorListPane.this );
440: if ((s != null) && (s.length() != 0)) {
441: ClipboardHistoryModel.singleton().put(s);
442: }
443: }
444: }
445: };
446:
447: /** Default copy action. */
448: Action copyAction = new DefaultEditorKit.CopyAction() {
449: public void actionPerformed(ActionEvent e) {
450: if (getSelectedText() != null) {
451: super .actionPerformed(e);
452: String s = edu.rice.cs.util.swing.Utilities
453: .getClipboardSelection(ErrorListPane.this );
454: if ((s != null) && (s.length() != 0)) {
455: ClipboardHistoryModel.singleton().put(s);
456: }
457: }
458: }
459: };
460:
461: /** No-op paste action. */
462: Action pasteAction = new DefaultEditorKit.PasteAction() {
463: public void actionPerformed(ActionEvent e) {
464: }
465: };
466:
467: /** Returns true if the errors should be highlighted in the source
468: * @return the status of the JCheckBox _showHighlightsCheckBox
469: */
470: public boolean shouldShowHighlightsInSource() {
471: return _showHighlightsCheckBox.isSelected();
472: }
473:
474: /** Get the index of the current error in the error array. */
475: public int getSelectedIndex() {
476: return _selectedIndex;
477: }
478:
479: /** Returns CompilerError associated with the given visual coordinates. Returns null if none. */
480: protected CompilerError _errorAtPoint(Point p) {
481: int modelPos = viewToModel(p);
482:
483: if (modelPos == -1)
484: return null;
485:
486: // Find the first error whose position preceeds this model position
487: int errorNum = -1;
488: for (int i = 0; i < _errorListPositions.length; i++) {
489: if (_errorListPositions[i].getOffset() <= modelPos)
490: errorNum = i;
491: else
492: break; // we've gone past the correct error; the last value was right
493: }
494:
495: if (errorNum >= 0)
496: return _errorTable.get(_errorListPositions[errorNum]);
497: return null;
498: }
499:
500: /** Returns the index into _errorListPositions corresponding to the given CompilerError. */
501: private int _getIndexForError(CompilerError error) {
502:
503: if (error == null)
504: throw new IllegalArgumentException(
505: "Couldn't find index for null error");
506:
507: for (int i = 0; i < _errorListPositions.length; i++) {
508: CompilerError e = _errorTable
509: .get(_errorListPositions[i]);
510: if (error.equals(e))
511: return i;
512: }
513:
514: throw new IllegalArgumentException(
515: "Couldn't find index for error " + error);
516: }
517:
518: /** Returns true if the text selection interval is empty. */
519: protected boolean _isEmptySelection() {
520: return getSelectionStart() == getSelectionEnd();
521: }
522:
523: /** Update the pane which holds the list of errors for the viewer. */
524: protected void updateListPane(boolean done) {
525: try {
526: _errorListPositions = new Position[_numErrors];
527: _errorTable.clear();
528:
529: if (_numErrors == 0)
530: _updateNoErrors(done);
531: else
532: _updateWithErrors();
533: } catch (BadLocationException e) {
534: throw new UnexpectedException(e);
535: }
536:
537: // Force UI to redraw
538: // revalidate();
539: }
540:
541: abstract protected void _updateNoErrors(boolean done)
542: throws BadLocationException;
543:
544: abstract protected void _updateWithErrors()
545: throws BadLocationException;
546:
547: /** Gets the message indicating the number of errors and warnings.*/
548: protected String _getNumErrorsMessage(String failureName,
549: String failureMeaning) {
550: StringBuilder numErrMsg;
551:
552: /** Used for display purposes only */
553: int numCompErrs = getErrorModel().getNumCompErrors();
554: int numWarnings = getErrorModel().getNumWarnings();
555:
556: if (!getErrorModel().hasOnlyWarnings()) {
557: numErrMsg = new StringBuilder(numCompErrs + " "
558: + failureName); //failureName = error or test (for compilation and JUnit testing respectively)
559: if (numCompErrs > 1)
560: numErrMsg.append("s");
561: if (numWarnings > 0)
562: numErrMsg
563: .append(" and " + numWarnings + " warning");
564: }
565:
566: else
567: numErrMsg = new StringBuilder(numWarnings + " warning");
568:
569: if (numWarnings > 1)
570: numErrMsg.append("s");
571:
572: numErrMsg.append(" " + failureMeaning + ":\n");
573: return numErrMsg.toString();
574: }
575:
576: /**
577: * Gets the message to title the block containing only errors.
578: */
579: protected String _getErrorTitle() {
580: CompilerErrorModel cem = getErrorModel();
581: if (cem.getNumCompErrors() > 1)
582: return "--------------\n*** Errors ***\n--------------\n";
583: if (cem.getNumCompErrors() > 0)
584: return "-------------\n*** Error ***\n-------------\n";
585: return "";
586: }
587:
588: /**
589: * Gets the message to title the block containing only warnings.
590: */
591: protected String _getWarningTitle() {
592: CompilerErrorModel cem = getErrorModel();
593: if (cem.getNumWarnings() > 1)
594: return "--------------\n** Warnings **\n--------------\n";
595: if (cem.getNumWarnings() > 0)
596: return "-------------\n** Warning **\n-------------\n";
597: return "";
598: }
599:
600: /** Used to show that the last compile was unsuccessful.*/
601: protected void _updateWithErrors(String failureName,
602: String failureMeaning, SwingDocument doc)
603: throws BadLocationException {
604: // Print how many errors
605: String numErrsMsg = _getNumErrorsMessage(failureName,
606: failureMeaning);
607: doc.append(numErrsMsg, BOLD_ATTRIBUTES);
608:
609: _insertErrors(doc);
610: setDocument(doc);
611:
612: // Select the first error if there are some errors (i.e. does not select if there are only warnings)
613: if (!getErrorModel().hasOnlyWarnings())
614: getErrorListPane().switchToError(0);
615: }
616:
617: /** Returns true if there is an error after the selected error. */
618: public boolean hasNextError() {
619: return this .getSelectedIndex() + 1 < _numErrors;
620: }
621:
622: /** Returns true if there is an error before the selected error. */
623: public boolean hasPrevError() {
624: return this .getSelectedIndex() > 0;
625: }
626:
627: /** Switches to the next error. */
628: public void nextError() {
629: // Select the error
630: if (hasNextError()) {
631: this ._selectedIndex += 1;
632: // Utilities.showDebug("selected index in nextError is " + _selectedIndex + " _numErrors is " + _numErrors);
633: getErrorListPane().switchToError(
634: this .getSelectedIndex());
635: }
636: }
637:
638: /** Switches to the previous error. */
639: public void prevError() {
640: // Select the error
641: if (hasPrevError()) {
642: this ._selectedIndex -= 1;
643: getErrorListPane().switchToError(
644: this .getSelectedIndex());
645: }
646: }
647:
648: /** Inserts all of the errors into the given document.
649: * @param doc the document into which to insert the errors
650: */
651: protected void _insertErrors(SwingDocument doc)
652: throws BadLocationException {
653: CompilerErrorModel cem = getErrorModel();
654: int numErrors = cem.getNumErrors();
655:
656: //Added this counter in order to add errors and warnings in correct order and select them correctly
657: //Previous version used errorNum as a counter, but this doesn't work anymore because we are not doing
658: //errors and variables at the same time.
659: int errorPositionInListOfErrors = 0;
660: // Show errors first and warnings second
661:
662: String errorTitle = _getErrorTitle();
663: if (cem.getNumWarnings() > 0)
664: doc.append(errorTitle, BOLD_ATTRIBUTES);
665:
666: for (int errorNum = 0; errorNum < numErrors; errorNum++) {
667: int startPos = doc.getLength();
668: CompilerError err = cem.getError(errorNum);
669:
670: if (!err.isWarning()) {
671: _insertErrorText(err, doc);
672: Position pos = doc.createPosition(startPos);
673: _errorListPositions[errorPositionInListOfErrors] = pos;
674: _errorTable.put(pos, err);
675: errorPositionInListOfErrors++;
676: }
677: }
678:
679: String warningTitle = _getWarningTitle();
680: if (cem.getNumCompErrors() > 0)
681: doc.append(warningTitle, BOLD_ATTRIBUTES);
682:
683: for (int errorNum = 0; errorNum < numErrors; errorNum++) {
684: int startPos = doc.getLength();
685: CompilerError err = cem.getError(errorNum);
686:
687: if (err.isWarning()) {
688: _insertErrorText(err, doc);
689: Position pos = doc.createPosition(startPos);
690: _errorListPositions[errorPositionInListOfErrors] = pos;
691: _errorTable.put(pos, err);
692: errorPositionInListOfErrors++;
693: }
694: }
695: }
696:
697: /** Prints a message for the given error
698: * @param error the error to print
699: * @param doc the document in the error pane
700: */
701: protected void _insertErrorText(CompilerError error,
702: SwingDocument doc) throws BadLocationException {
703: // Show file and line number
704: doc.append("File: ", BOLD_ATTRIBUTES);
705: String fileAndLineNumber = error.getFileMessage()
706: + " [line: " + error.getLineMessage() + "]";
707: doc.append(fileAndLineNumber + "\n", NORMAL_ATTRIBUTES);
708:
709: if (error.isWarning())
710: doc.append(_getWarningText(), BOLD_ATTRIBUTES);
711: else
712: doc.append(_getErrorText(), BOLD_ATTRIBUTES);
713:
714: doc.append(error.message(), NORMAL_ATTRIBUTES);
715: doc.append("\n", NORMAL_ATTRIBUTES);
716: }
717:
718: /** Returns the string to identify a warning. */
719: protected String _getWarningText() {
720: return "Warning: ";
721: }
722:
723: /** Returns the string to identify an error. */
724: protected String _getErrorText() {
725: return "Error: ";
726: }
727:
728: /** When the selection of the current error changes, remove the highlight in the error pane. */
729: protected void _removeListHighlight() {
730: if (_listHighlightTag != null) {
731: _listHighlightTag.remove();
732: _listHighlightTag = null;
733: }
734: // _prevErrorButton.setEnabled(false);
735: // _nextErrorButton.setEnabled(false);
736: }
737:
738: /** Don't select any errors in the error pane. */
739: public void selectNothing() {
740: // _selectedIndex = -1;
741: _removeListHighlight();
742:
743: // Remove highlight from the defPane that has it
744: _frame.getCurrentDefPane().removeErrorHighlight();
745: }
746:
747: /** Selects the given error inside the error list pane. */
748: public void selectItem(CompilerError error) {
749: // Utilities.showDebug("selectItem(" + error + ") called");
750: try {
751: // Find corresponding index
752: int i = _getIndexForError(error);
753:
754: _selectedIndex = i;
755: // Utilities.showDebug("selected index = " + i);
756: _removeListHighlight();
757:
758: int startPos = _errorListPositions[i].getOffset();
759: // Utilities.showDebug("startPos = " + startPos);
760:
761: // end pos is either the end of the document (if this is the last error)
762: // or the end of the error if the last error (i.e. before the warnings title)
763: // or the char where the next error starts
764: int endPos;
765: if (i + 1 >= (_numErrors))
766: endPos = getDocument().getLength();
767: else {
768: endPos = _errorListPositions[i + 1].getOffset();
769: // Utilities.showDebug("endPos(before) = " + endPos);
770: CompilerError nextError = _errorTable
771: .get(_errorListPositions[i + 1]);
772: // Utilities.showDebug("nextError = " + nextError);
773: if (!error.isWarning() && nextError.isWarning())
774: endPos = endPos - _getWarningTitle().length();
775: // Utilities.showDebug("endPos(after) = " + endPos);
776: }
777:
778: // Utilities.showDebug("startpos = " + startPos + " endpos = " + endPos);
779:
780: try {
781: _listHighlightTag = _highlightManager.addHighlight(
782: startPos, endPos, _listHighlightPainter);
783:
784: // If first error, show number of errors and warnings preferentially to showing the error
785: // Otherwise, scroll to make sure this item is visible
786: Rectangle startRect;
787: if (i == 0)
788: startRect = modelToView(0);
789:
790: else
791: startRect = modelToView(startPos);
792:
793: Rectangle endRect = modelToView(endPos - 1);
794:
795: if (startRect != null && endRect != null) {
796: // Add the end rect onto the start rect to make a rectangle
797: // that encompasses the entire error
798: startRect.add(endRect);
799:
800: //System.err.println("scrll vis: " + startRect);
801:
802: scrollRectToVisible(startRect);
803: _updateScrollButtons();
804: } else {
805: // Utilities.showDebug("Either startRect or endRect is null!");
806: // Couldn't draw the box to highlight, so don't highlight anything
807: _removeListHighlight();
808: }
809: } catch (BadLocationException badBadLocation) {
810: }
811:
812: } catch (IllegalArgumentException iae) {
813: // This shouldn't be happening, but it was reported in bug 704006.
814: // (_getIndexForError throws it.)
815: // We'll at least fail a little more gracefully.
816: _removeListHighlight();
817: }
818: }
819:
820: protected void _updateScrollButtons() {
821: if (hasNextError()) {
822: _nextErrorButton.setEnabled(true);
823: } else {
824: _nextErrorButton.setEnabled(false);
825: }
826: if (hasPrevError()) {
827: _prevErrorButton.setEnabled(true);
828: } else {
829: _prevErrorButton.setEnabled(false);
830: }
831: }
832:
833: /** Change all state to select a new error, including moving the caret to the error, if a corresponding position
834: * exists.
835: * @param error The error to switch to
836: */
837: void switchToError(CompilerError error) {
838: // Utilities.showDebug("ErrorPanel.switchToError called");
839: if (error == null)
840: return;
841:
842: SingleDisplayModel model = getModel();
843:
844: DefinitionsPane prevPane = _frame.getCurrentDefPane();
845: prevPane.removeErrorHighlight(); // hide previous error highlight
846: OpenDefinitionsDocument prevDoc = prevPane
847: .getOpenDefDocument();
848:
849: if (error.file() != null) {
850: try {
851: OpenDefinitionsDocument doc = model
852: .getDocumentForFile(error.file());
853: CompilerErrorModel errorModel = getErrorModel();
854:
855: Position pos = errorModel.getPosition(error); // null if error has no Position
856: // Utilities.showDebug("The position of the error is: " + pos);
857: // switch to correct def pane and move caret to error position
858: // Utilities.showDebug("active document being set to " + doc + " in ErrorPanel.switchToError");
859:
860: if (!prevDoc.equals(doc)) {
861: model.addToBrowserHistory();
862: model.setActiveDocument(doc);
863: } else
864: model.refreshActiveDocument();
865:
866: // Utilities.showDebug("setting active document has completed");
867:
868: DefinitionsPane defPane = _frame
869: .getCurrentDefPane();
870:
871: if (pos != null) {
872: int errPos = pos.getOffset();
873: if (errPos >= 0 && errPos <= doc.getLength()) {
874: defPane.centerViewOnOffset(errPos);
875:
876: /* The folowing fixes a bug where, if two consecutive errors are in the same position, the previous error
877: * is unhighlighted and the new error is not highlighted because the CaretListener does not act because there
878: * is no change in caret position. (This is the only place where updateHighlight was called from before) */
879: defPane.getErrorCaretListener()
880: .updateHighlight(errPos);
881: }
882:
883: }
884: // The following line is a brute force hack that fixed a bug plaguing the DefinitionsPane immediately after a compilation
885: // with errors. In some cases (which were consistently reproducible), the DefinitionsPane editing functions would break
886: // whereby the keystrokes had their usual meaning but incorrect updates were performed in the DefintionsPane. For example,
887: // the display behaved as if the editor were in "overwrite" mode.
888: // _frame._switchDefScrollPane(); // resets an out-of-kilter DefinitionsPane on the first error after a compilation
889: defPane.requestFocusInWindow();
890: defPane.getCaret().setVisible(true);
891: } catch (IOException ioe) {
892: // Don't highlight the source if file can't be opened
893: }
894: }
895: // Utilities.showDebug("Calling selectItem(...) from switchToError");
896: /* setActiveDocument(doc) selects the first error corresponding to the current position (caret location) but this may not
897: * be the correct error if there are multiple errors for this this position. The following selects the correct error.*/
898: getErrorListPane().selectItem(error);
899: }
900:
901: /** Another interface to switchToError.
902: * @param index Index into the array of positions in the CompilerErrorListPane
903: */
904: void switchToError(int index) {
905: if ((index >= 0) && (index < _errorListPositions.length)) {
906: Position pos = _errorListPositions[index];
907: CompilerError error = _errorTable.get(pos);
908: switchToError(error);
909: }
910: }
911:
912: /** The OptionListener for compiler COMPILER_ERROR_COLOR */
913: private class CompilerErrorColorOptionListener implements
914: OptionListener<Color> {
915:
916: public void optionChanged(OptionEvent<Color> oce) {
917: _listHighlightPainter = new ReverseHighlighter.DrJavaHighlightPainter(
918: oce.value);
919: if (_listHighlightTag != null) {
920: _listHighlightTag.refresh(_listHighlightPainter);
921: }
922: }
923: }
924:
925: /** The OptionListener for compiler DEFINITIONS_NORMAL_COLOR */
926: private class ForegroundColorListener implements
927: OptionListener<Color> {
928: public void optionChanged(OptionEvent<Color> oce) {
929: StyleConstants.setForeground(NORMAL_ATTRIBUTES,
930: oce.value);
931: StyleConstants
932: .setForeground(BOLD_ATTRIBUTES, oce.value);
933:
934: // Re-attribute the existing text with the new color.
935: SwingDocument doc = getErrorListPane()
936: .getSwingDocument();
937: SimpleAttributeSet set = new SimpleAttributeSet();
938: set.addAttribute(StyleConstants.Foreground, oce.value);
939: doc.acquireWriteLock();
940: try {
941: doc.setCharacterAttributes(0, doc.getLength(), set,
942: false);
943: } finally {
944: doc.releaseWriteLock();
945: }
946: // ErrorListPane.this.repaint();
947: }
948: }
949:
950: /** The OptionListener for compiler DEFINITIONS_BACKGROUND_COLOR. */
951: private class BackgroundColorListener implements
952: OptionListener<Color> {
953: public void optionChanged(OptionEvent<Color> oce) {
954: setBackground(oce.value);
955: ErrorListPane.this.repaint();
956: }
957: }
958: }
959: }
|