/*
* 01/06/2009
* MarkOccurrencesSupport.java - Handles marking all occurrences of the
* currently selected identifier in a text area.
* 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.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.Caret;
import org.fife.ui.rtextarea.SmartHighlightPainter;
/**
* Marks all occurrences of the token at the current caret position, if it is an
* identifier.
*
* @author Robert Futrell
* @version 1.0
* @see OccurrenceMarker
*/
class MarkOccurrencesSupport implements CaretListener, ActionListener {
private RSyntaxTextArea textArea;
private Timer timer;
private SmartHighlightPainter p;
/**
* The default color used to mark occurrences.
*/
static final Color DEFAULT_COLOR = new Color(224, 224, 224);
/**
* The default delay.
*/
static final int DEFAULT_DELAY_MS = 1000;
/**
* Constructor. Creates a listener with a 1 second delay.
*/
MarkOccurrencesSupport() {
this(DEFAULT_DELAY_MS);
}
/**
* Constructor.
*
* @param delay The delay between when the caret last moves and when the text
* should be scanned for matching occurrences. This should be in
* milliseconds.
*/
MarkOccurrencesSupport(int delay) {
this(delay, DEFAULT_COLOR);
}
/**
* Constructor.
*
* @param delay The delay between when the caret last moves and when the text
* should be scanned for matching occurrences. This should be in
* milliseconds.
* @param color The color to use to mark the occurrences. This cannot be
* null.
*/
MarkOccurrencesSupport(int delay, Color color) {
timer = new Timer(delay, this);
timer.setRepeats(false);
p = new SmartHighlightPainter();
setColor(color);
}
/**
* Called after the caret has been moved and a fixed time delay has elapsed.
* This locates and highlights all occurrences of the identifier at the caret
* position, if any.
*
* * Callers should not call this method directly, but should rather prefer * {@link #doMarkOccurrences()} to mark occurrences. * * @param e The event. * @see #doMarkOccurrences() */ @Override public void actionPerformed(ActionEvent e) { // Don't do anything if they are selecting text. Caret c = textArea.getCaret(); if (c.getDot() != c.getMark()) { return; } RSyntaxDocument doc = (RSyntaxDocument) textArea.getDocument(); OccurrenceMarker occurrenceMarker = doc.getOccurrenceMarker(); boolean occurrencesChanged = false; if (occurrenceMarker != null) { doc.readLock(); try { Token t = occurrenceMarker.getTokenToMark(textArea); if (t != null && occurrenceMarker.isValidType(textArea, t) && !RSyntaxUtilities.isNonWordChar(t)) { clear(); RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter) textArea.getHighlighter(); occurrenceMarker.markOccurrences(doc, t, h, p); // textArea.repaint(); // TODO: Do a textArea.repaint() instead of repainting each // marker as it's added if count is huge occurrencesChanged = true; } } finally { doc.readUnlock(); // time = System.currentTimeMillis() - time; // System.out.println("MarkOccurrencesSupport took: " + time + " // ms"); } } if (occurrencesChanged) { textArea.fireMarkedOccurrencesChanged(); } } /** * Called when the caret moves in the text area. * * @param e The event. */ @Override public void caretUpdate(CaretEvent e) { timer.restart(); } /** * Removes all highlights added to the text area by this listener. */ void clear() { if (textArea != null) { RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter) textArea.getHighlighter(); h.clearMarkOccurrencesHighlights(); } } /** * Immediately marks all occurrences of the token at the current caret position. */ public void doMarkOccurrences() { timer.stop(); actionPerformed(null); } /** * Returns the color being used to mark occurrences. * * @return The color being used. * @see #setColor(Color) */ public Color getColor() { return (Color) p.getPaint(); } /** * Returns the delay, in milliseconds. * * @return The delay. * @see #setDelay(int) */ public int getDelay() { return timer.getDelay(); } /** * Returns whether a border is painted around marked occurrences. * * @return Whether a border is painted. * @see #setPaintBorder(boolean) * @see #getColor() */ public boolean getPaintBorder() { return p.getPaintBorder(); } /** * Installs this listener on a text area. If it is already installed on another * text area, it is uninstalled first. * * @param textArea The text area to install on. */ public void install(RSyntaxTextArea textArea) { if (this.textArea != null) { uninstall(); } this.textArea = textArea; textArea.addCaretListener(this); if (textArea.getMarkOccurrencesColor() != null) { setColor(textArea.getMarkOccurrencesColor()); } } /** * Sets the color to use when marking occurrences. * * @param color The color to use. * @see #getColor() * @see #setPaintBorder(boolean) */ public void setColor(Color color) { p.setPaint(color); if (textArea != null) { clear(); caretUpdate(null); // Force a highlight repaint. } } /** * Sets the delay between the last caret position change and when the text is * scanned for matching identifiers. A delay is needed to prevent repeated * scanning while the user is typing. * * @param delay The new delay. * @see #getDelay() */ public void setDelay(int delay) { timer.setInitialDelay(delay); } /** * Toggles whether a border is painted around marked highlights. * * @param paint Whether to paint a border. * @see #getPaintBorder() * @see #setColor(Color) */ public void setPaintBorder(boolean paint) { if (paint != p.getPaintBorder()) { p.setPaintBorder(paint); if (textArea != null) { textArea.repaint(); } } } /** * Uninstalls this listener from the current text area. Does nothing if it not * currently installed on any text area. * * @see #install(RSyntaxTextArea) */ public void uninstall() { if (textArea != null) { clear(); textArea.removeCaretListener(this); } } }