/*
* 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;
}
}
}