/*
* 02/24/2004
*
* RSyntaxTextAreaUI.java - UI for an RSyntaxTextArea.
* Copyright (C) 2004 Robert Futrell
* robert_futrell at users.sourceforge.net
* http://fifesoft.com/rsyntaxtextarea
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.fife.ui.rsyntaxtextarea;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.beans.PropertyChangeEvent;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.InputMapUIResource;
import javax.swing.text.*;
import org.fife.ui.rtextarea.RTextArea;
import org.fife.ui.rtextarea.RTextAreaUI;
/**
* UI used by <code>RSyntaxTextArea</code>. This allows us to implement syntax highlighting.
*
* @author Robert Futrell
* @version 0.1
*/
public class RSyntaxTextAreaUI extends RTextAreaUI {
private static final String SHARED_ACTION_MAP_NAME = "RSyntaxTextAreaUI.actionMap";
private static final String SHARED_INPUT_MAP_NAME = "RSyntaxTextAreaUI.inputMap";
private static final EditorKit defaultKit = new RSyntaxTextAreaEditorKit();
public static ComponentUI createUI(JComponent ta) {
return new RSyntaxTextAreaUI(ta);
}
/**
* Constructor.
*/
public RSyntaxTextAreaUI(JComponent rSyntaxTextArea) {
super(rSyntaxTextArea);
}
/**
* Creates the view for an element.
*
* @param elem
* The element.
* @return The view.
*/
public View create(Element elem) {
RTextArea c = getRTextArea();
if (c instanceof RSyntaxTextArea) {
RSyntaxTextArea area = (RSyntaxTextArea) c;
View v;
if (area.getLineWrap())
v = new WrappedSyntaxView(elem);
else
v = new SyntaxView(elem);
return v;
}
return null;
}
/**
* Creates the highlighter to use for syntax text areas.
*
* @return The highlighter.
*/
protected Highlighter createHighlighter() {
return new RSyntaxTextAreaHighlighter();
}
/**
* Returns the name to use to cache/fetch the shared action map. This should be overridden by subclasses if the
* subclass has its own custom editor kit to install, so its actions get picked up.
*
* @return The name of the cached action map.
*/
protected String getActionMapName() {
return SHARED_ACTION_MAP_NAME;
}
/**
* Fetches the EditorKit for the UI.
*
* @param tc
* The text component for which this UI is installed.
* @return The editor capabilities.
* @see javax.swing.plaf.TextUI#getEditorKit
*/
public EditorKit getEditorKit(JTextComponent tc) {
return defaultKit;
}
/**
* Get the InputMap to use for the UI.
* <p>
*
* This method is not named <code>getInputMap()</code> because there is a package-private method in
* <code>BasicTextAreaUI</code> with that name. Thus, creating a new method with that name causes certain compilers
* to issue warnings that you are not actually overriding the original method (since it is package-private).
*/
protected InputMap getRTextAreaInputMap() {
InputMap map = new InputMapUIResource();
InputMap shared = (InputMap) UIManager.get(SHARED_INPUT_MAP_NAME);
if (shared == null) {
shared = new RSyntaxTextAreaDefaultInputMap();
UIManager.put(SHARED_INPUT_MAP_NAME, shared);
}
// KeyStroke[] keys = shared.allKeys();
// for (int i=0; i<keys.length; i++)
// System.err.println(keys[i] + " -> " + shared.get(keys[i]));
map.setParent(shared);
return map;
}
/**
* Paints the text area's background.
*
* @param g
* The graphics component on which to paint.
*/
protected void paintBackground(Graphics g) {
super.paintBackground(g);
paintMatchedBracket(g);
}
/**
* Paints the "matched bracket", if any.
*
* @param g
* The graphics context.
*/
protected void paintMatchedBracket(Graphics g) {
// We must add "-1" to the height because otherwise we'll paint below
// the region that gets invalidated.
RSyntaxTextArea rsta = (RSyntaxTextArea) textArea;
if (rsta.isBracketMatchingEnabled()) {
Rectangle match = rsta.match;
if (match != null) {
if (rsta.getAnimateBracketMatching()) {
g.setColor(rsta.getMatchedBracketBGColor());
g.fillRoundRect(match.x, match.y, match.width, match.height - 1, 5, 5);
g.setColor(rsta.getMatchedBracketBorderColor());
g.drawRoundRect(match.x, match.y, match.width, match.height - 1, 5, 5);
}
else {
g.setColor(rsta.getMatchedBracketBGColor());
g.fillRect(match.x, match.y, match.width, match.height - 1);
g.setColor(rsta.getMatchedBracketBorderColor());
g.drawRect(match.x, match.y, match.width, match.height - 1);
}
}
}
}
/**
* Gets called whenever a bound property is changed on this UI's <code>RSyntaxTextArea</code>.
*
* @param e
* The property change event.
*/
protected void propertyChange(PropertyChangeEvent e) {
String name = e.getPropertyName();
// If they change the syntax scheme, we must do this so that
// WrappedSyntaxView(_TEST) updates its child views properly.
if (name.equals(RSyntaxTextArea.SYNTAX_SCHEME_PROPERTY)) {
modelChanged();
}
// Everything else is general to all RTextAreas.
else {
super.propertyChange(e);
}
}
/**
* Updates the view. This should be called when the underlying <code>RSyntaxTextArea</code> changes its syntax
* editing style.
*/
public void refreshSyntaxHighlighting() {
modelChanged();
}
/**
* Returns the y-coordinate of the line containing a specified offset.
* <p>
*
* This is faster than calling <code>modelToView(offs).y</code>, so it is preferred if you do not need the actual
* bounding box.
*
* @param offs
* The offset info the document.
* @return The y-coordinate of the top of the offset, or <code>-1</code> if this text area doesn't yet have a
* positive size.
* @throws BadLocationException
* If <code>offs</code> isn't a valid offset into the document.
*/
public int yForLineContaining(int offs) throws BadLocationException {
Rectangle alloc = getVisibleEditorRect();
if (alloc != null) {
RSTAView view = (RSTAView) getRootView(textArea).getView(0);
return view.yForLineContaining(alloc, offs);
}
return -1;
}
}