/* * 04/23/2009 * * RSyntaxTextAreaHighlighter.java - Highlighter for RSyntaxTextAreas. * * This library is distributed under a modified BSD license. See the included * RSyntaxTextArea.License.txt file for details. */ package org.fife.ui.rsyntaxtextarea; import java.awt.Color; import java.awt.Graphics; import java.awt.Shape; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.swing.plaf.TextUI; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.Element; import javax.swing.text.JTextComponent; import javax.swing.text.View; import org.fife.ui.rsyntaxtextarea.parser.Parser; import org.fife.ui.rsyntaxtextarea.parser.ParserNotice; import org.fife.ui.rtextarea.RTextAreaHighlighter; import org.fife.ui.rtextarea.SmartHighlightPainter; /** * The highlighter implementation used by {@link RSyntaxTextArea}s. It knows to * always paint "marked occurrences" highlights below selection highlights, * and squiggle underline highlights above all other highlights.<p> * * Most of this code is copied from javax.swing.text.DefaultHighlighter; * unfortunately, we cannot re-use much of it since it is package private. * * @author Robert Futrell * @version 1.0 */ public class RSyntaxTextAreaHighlighter extends RTextAreaHighlighter { /** * Marked occurrences in the document (to be painted separately from * other highlights). */ private List<SyntaxLayeredHighlightInfoImpl> markedOccurrences; /** * Highlights from document parsers. These should be painted "on top of" * all other highlights to ensure they are always above the selection. */ private List<SyntaxLayeredHighlightInfoImpl> parserHighlights; /** * The default color used for parser notices when none is specified. */ private static final Color DEFAULT_PARSER_NOTICE_COLOR = Color.RED; /** * Constructor. */ public RSyntaxTextAreaHighlighter() { markedOccurrences = new ArrayList<SyntaxLayeredHighlightInfoImpl>(); parserHighlights = new ArrayList<SyntaxLayeredHighlightInfoImpl>(0); // Often unused } /** * Adds a special "marked occurrence" highlight. * * @param start * @param end * @param p * @return A tag to reference the highlight later. * @throws BadLocationException * @see #clearMarkOccurrencesHighlights() */ Object addMarkedOccurrenceHighlight(int start, int end, SmartHighlightPainter p) throws BadLocationException { Document doc = textArea.getDocument(); TextUI mapper = textArea.getUI(); // Always layered highlights for marked occurrences. SyntaxLayeredHighlightInfoImpl i = new SyntaxLayeredHighlightInfoImpl(); i.setPainter(p); i.setStartOffset(doc.createPosition(start)); // HACK: Use "end-1" to prevent chars the user types at the "end" of // the highlight to be absorbed into the highlight (default Highlight // behavior). i.setEndOffset(doc.createPosition(end-1)); markedOccurrences.add(i); mapper.damageRange(textArea, start, end); return i; } /** * Adds a highlight from a parser. * * @param notice The notice from a {@link Parser}. * @return A tag with which to reference the highlight. * @throws BadLocationException * @see #clearParserHighlights() * @see #clearParserHighlights(Parser) */ HighlightInfo addParserHighlight(ParserNotice notice, HighlightPainter p) throws BadLocationException { Document doc = textArea.getDocument(); TextUI mapper = textArea.getUI(); int start = notice.getOffset(); int end = 0; if (start==-1) { // Could just define an invalid line number int line = notice.getLine(); Element root = doc.getDefaultRootElement(); if (line>=0 && line<root.getElementCount()) { Element elem = root.getElement(line); start = elem.getStartOffset(); end = elem.getEndOffset(); } } else { end = start + notice.getLength(); } // Always layered highlights for parser highlights. SyntaxLayeredHighlightInfoImpl i = new SyntaxLayeredHighlightInfoImpl(); i.setPainter(p); i.setStartOffset(doc.createPosition(start)); // HACK: Use "end-1" to prevent chars the user types at the "end" of // the highlight to be absorbed into the highlight (default Highlight // behavior). i.setEndOffset(doc.createPosition(end-1)); i.notice = notice;//i.color = notice.getColor(); parserHighlights.add(i); mapper.damageRange(textArea, start, end); return i; } /** * Removes all "marked occurrences" highlights from the view. * * @see #addMarkedOccurrenceHighlight(int, int, SmartHighlightPainter) */ void clearMarkOccurrencesHighlights() { // Don't remove via an iterator; since our List is an ArrayList, this // implies tons of System.arrayCopy()s for (HighlightInfo info : markedOccurrences) { repaintListHighlight(info); } markedOccurrences.clear(); } /** * Removes all parser highlights. * * @see #addParserHighlight(ParserNotice, javax.swing.text.Highlighter.HighlightPainter) */ void clearParserHighlights() { // Don't remove via an iterator; since our List is an ArrayList, this // implies tons of System.arrayCopy()s for (int i=0; i<parserHighlights.size(); i++) { repaintListHighlight(parserHighlights.get(i)); } parserHighlights.clear(); } /** * Removes all of the highlights for a specific parser. * * @param parser The parser. */ public void clearParserHighlights(Parser parser) { Iterator<SyntaxLayeredHighlightInfoImpl> i = parserHighlights.iterator(); for (; i.hasNext(); ) { SyntaxLayeredHighlightInfoImpl info = i.next(); if (info.notice.getParser()==parser) { if (info.width > 0 && info.height > 0) { textArea.repaint(info.x, info.y, info.width, info.height); } i.remove(); } } } /** * {@inheritDoc} */ @Override public void deinstall(JTextComponent c) { super.deinstall(c); markedOccurrences.clear(); parserHighlights.clear(); } /** * Returns a list of "marked occurrences" in the text area. If there are * no marked occurrences, this will be an empty list. * * @return The list of marked occurrences, or an empty list if none. The * contents of this list will be of type {@link DocumentRange}. */ public List<DocumentRange> getMarkedOccurrences() { List<DocumentRange> list = new ArrayList<DocumentRange>(markedOccurrences.size()); for (HighlightInfo info : markedOccurrences) { int start = info.getStartOffset(); int end = info.getEndOffset() + 1; // HACK DocumentRange range = new DocumentRange(start, end); list.add(range); } return list; } @Override public void paintLayeredHighlights(Graphics g, int lineStart, int lineEnd, Shape viewBounds, JTextComponent editor, View view) { paintListLayered(g, lineStart,lineEnd, viewBounds, editor, view, markedOccurrences); super.paintLayeredHighlights(g, lineStart, lineEnd, viewBounds, editor, view); paintListLayered(g, lineStart,lineEnd, viewBounds, editor, view, parserHighlights); } /** * Removes a parser highlight from this view. * * @param tag The reference to the highlight. * @see #addParserHighlight(ParserNotice, javax.swing.text.Highlighter.HighlightPainter) */ void removeParserHighlight(HighlightInfo tag) { repaintListHighlight(tag); parserHighlights.remove(tag); } /** * Highlight info implementation used for parser notices and marked * occurrences. */ private static class SyntaxLayeredHighlightInfoImpl extends LayeredHighlightInfoImpl { ParserNotice notice;//Color color; // Used only by Parser highlights. @Override public Color getColor() { //return color; Color color = null; if (notice!=null) { color = notice.getColor(); if (color==null) { color = DEFAULT_PARSER_NOTICE_COLOR; } } return color; } } }