All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.fife.ui.rsyntaxtextarea.Theme Maven / Gradle / Ivy

The newest version!
/*
 * 10/30/2011
 *
 * Theme.java - A color theme for RSyntaxTextArea.
 * 
 * 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.Font;
import java.awt.SystemColor;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.reflect.Field;

import javax.swing.UIManager;
import javax.swing.plaf.ColorUIResource;
import javax.swing.text.StyleContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

import org.fife.io.UnicodeWriter;
import org.fife.ui.rtextarea.Gutter;
import org.fife.ui.rtextarea.RTextArea;


/**
 * A theme is a set of fonts and colors to use to style RSyntaxTextArea.
 * Themes are defined in XML files that are validated against
 * themes.dtd.  This provides applications and other consumers
 * with an easy way to style RSyntaxTextArea without having to use the API.

* * Sample themes are included in the source tree under the /themes * folder, but are not a part of the built RSyntaxTextArea jar. Hosting * applications are free to ship and use these themes as-is, modify them, or * create their own.

* * Note that to save a Theme via {@link #save(OutputStream)}, * you must currently create a Theme from a text area wrapped in * an RTextScrollPane, so that the color information for the * gutter can be retrieved. * * @author Robert Futrell * @version 1.0 */ public class Theme { private Font baseFont; private Color bgColor; private Color caretColor; private boolean useSelctionFG; private Color selectionFG; private Color selectionBG; private boolean selectionRoundedEdges; private Color currentLineHighlight; private boolean fadeCurrentLineHighlight; private Color marginLineColor; private Color markAllHighlightColor; private Color markOccurrencesColor; private boolean markOccurrencesBorder; private Color matchedBracketFG; private Color matchedBracketBG; private boolean matchedBracketHighlightBoth; private boolean matchedBracketAnimate; private Color hyperlinkFG; private Color[] secondaryLanguages; private SyntaxScheme scheme; private Color gutterBorderColor; private Color activeLineRangeColor; private boolean iconRowHeaderInheritsGutterBG; private Color lineNumberColor; private String lineNumberFont; private int lineNumberFontSize; private Color foldIndicatorFG; private Color foldBG; /** * Private constructor, used when loading from a stream. * * @param baseFont The default font to use for any "base font" properties * not specified in the theme XML. If this is null, * a default monospaced font will be used. */ private Theme(Font baseFont) { // Optional fields that require a default value. this.baseFont = baseFont!=null ? baseFont : RTextArea.getDefaultFont(); secondaryLanguages = new Color[3]; activeLineRangeColor = Gutter.DEFAULT_ACTIVE_LINE_RANGE_COLOR; } /** * Creates a theme from an RSyntaxTextArea. It should be contained in * an RTextScrollPane to get all gutter color information. * * @param textArea The text area. */ public Theme(RSyntaxTextArea textArea) { baseFont = textArea.getFont(); bgColor = textArea.getBackground(); caretColor = textArea.getCaretColor(); useSelctionFG = textArea.getUseSelectedTextColor(); selectionFG = textArea.getSelectedTextColor(); selectionBG = textArea.getSelectionColor(); selectionRoundedEdges = textArea.getRoundedSelectionEdges(); currentLineHighlight = textArea.getCurrentLineHighlightColor(); fadeCurrentLineHighlight = textArea.getFadeCurrentLineHighlight(); marginLineColor = textArea.getMarginLineColor(); markAllHighlightColor = textArea.getMarkAllHighlightColor(); markOccurrencesColor = textArea.getMarkOccurrencesColor(); markOccurrencesBorder = textArea.getPaintMarkOccurrencesBorder(); matchedBracketBG = textArea.getMatchedBracketBGColor(); matchedBracketFG = textArea.getMatchedBracketBorderColor(); matchedBracketHighlightBoth = textArea.getPaintMatchedBracketPair(); matchedBracketAnimate = textArea.getAnimateBracketMatching(); hyperlinkFG = textArea.getHyperlinkForeground(); int count = textArea.getSecondaryLanguageCount(); secondaryLanguages = new Color[count]; for (int i=0; i0 ? lineNumberFontSize : baseFont.getSize(); Font font = getFont(fontName, Font.PLAIN, fontSize); gutter.setLineNumberFont(font); gutter.setFoldIndicatorForeground(foldIndicatorFG); gutter.setFoldBackground(foldBG); } } private static final String colorToString(Color c) { int color = c.getRGB() & 0xffffff; String str = Integer.toHexString(color); while (str.length()<6) { str = "0" + str; } return str; } /** * Returns the default selection background color to use if "default" is * specified in a theme. * * @return The default selection background to use. * @see #getDefaultSelectionFG() */ private static final Color getDefaultBG() { Color c = UIManager.getColor("nimbusLightBackground"); if (c==null) { // Don't search for "text", as Nimbus defines that as the text // component "text" color, but Basic LAFs use it for text // component backgrounds! Nimbus also defines TextArea.background // as too dark, not what it actually uses for text area backgrounds c = UIManager.getColor("TextArea.background"); if (c==null) { c = new ColorUIResource(SystemColor.text); } } return c; } /** * Returns the default selection background color to use if "default" is * specified in a theme. * * @return The default selection background to use. * @see #getDefaultSelectionFG() */ private static final Color getDefaultSelectionBG() { Color c = UIManager.getColor("TextArea.selectionBackground"); if (c==null) { c = UIManager.getColor("textHighlight"); if (c==null) { c = UIManager.getColor("nimbusSelectionBackground"); if (c==null) { c = new ColorUIResource(SystemColor.textHighlight); } } } return c; } /** * Returns the default "selected text" color to use if "default" is * specified in a theme. * * @return The default selection foreground color to use. * @see #getDefaultSelectionBG() */ private static Color getDefaultSelectionFG() { Color c = UIManager.getColor("TextArea.selectionForeground"); if (c==null) { c = UIManager.getColor("textHighlightText"); if (c==null) { c = UIManager.getColor("nimbusSelectedText"); if (c==null) { c = new ColorUIResource(SystemColor.textHighlightText); } } } return c; } /** * Returns the specified font. * * @param family The font family. * @param style The style of font. * @param size The size of the font. * @return The font. */ private static Font getFont(String family, int style, int size) { // Use StyleContext to get a composite font for Asian glyphs. StyleContext sc = StyleContext.getDefaultStyleContext(); return sc.getFont(family, style, size); } /** * Loads a theme. * * @param in The input stream to read from. This will be closed when this * method returns. * @return The theme. * @throws IOException If an IO error occurs. * @see #save(OutputStream) */ public static Theme load(InputStream in) throws IOException { return load(in, null); } /** * Loads a theme. * * @param in The input stream to read from. This will be closed when this * method returns. * @param baseFont The default font to use for any "base font" properties * not specified in the theme XML. If this is null, * a default monospaced font will be used. * @return The theme. * @throws IOException If an IO error occurs. * @see #save(OutputStream) */ public static Theme load(InputStream in, Font baseFont) throws IOException { Theme theme = new Theme(baseFont); BufferedInputStream bin = new BufferedInputStream(in); try { XmlHandler.load(theme, bin); } finally { bin.close(); } return theme; } /** * Saves this theme to an output stream. * * @param out The output stream to write to. * @throws IOException If an IO error occurs. * @see #load(InputStream) */ public void save(OutputStream out) throws IOException { BufferedOutputStream bout = new BufferedOutputStream(out); try { DocumentBuilder db = DocumentBuilderFactory.newInstance(). newDocumentBuilder(); DOMImplementation impl = db.getDOMImplementation(); Document doc = impl.createDocument(null, "RSyntaxTheme", null); Element root = doc.getDocumentElement(); root.setAttribute("version", "1.0"); Element elem = doc.createElement("baseFont"); if (!baseFont.getFamily().equals( RSyntaxTextArea.getDefaultFont().getFamily())) { elem.setAttribute("family", baseFont.getFamily()); } elem.setAttribute("size", Integer.toString(baseFont.getSize())); root.appendChild(elem); elem = doc.createElement("background"); elem.setAttribute("color", colorToString(bgColor)); root.appendChild(elem); elem = doc.createElement("caret"); elem.setAttribute("color", colorToString(caretColor)); root.appendChild(elem); elem = doc.createElement("selection"); elem.setAttribute("useFG", Boolean.toString(useSelctionFG)); elem.setAttribute("fg", colorToString(selectionFG)); elem.setAttribute("bg", colorToString(selectionBG)); elem.setAttribute("roundedEdges", Boolean.toString(selectionRoundedEdges)); root.appendChild(elem); elem = doc.createElement("currentLineHighlight"); elem.setAttribute("color", colorToString(currentLineHighlight)); elem.setAttribute("fade", Boolean.toString(fadeCurrentLineHighlight)); root.appendChild(elem); elem = doc.createElement("marginLine"); elem.setAttribute("fg", colorToString(marginLineColor)); root.appendChild(elem); elem = doc.createElement("markAllHighlight"); elem.setAttribute("color", colorToString(markAllHighlightColor)); root.appendChild(elem); elem = doc.createElement("markOccurrencesHighlight"); elem.setAttribute("color", colorToString(markOccurrencesColor)); elem.setAttribute("border", Boolean.toString(markOccurrencesBorder)); root.appendChild(elem); elem = doc.createElement("matchedBracket"); elem.setAttribute("fg", colorToString(matchedBracketFG)); elem.setAttribute("bg", colorToString(matchedBracketBG)); elem.setAttribute("highlightBoth", Boolean.toString(matchedBracketHighlightBoth)); elem.setAttribute("animate", Boolean.toString(matchedBracketAnimate)); root.appendChild(elem); elem = doc.createElement("hyperlinks"); elem.setAttribute("fg", colorToString(hyperlinkFG)); root.appendChild(elem); elem = doc.createElement("secondaryLanguages"); for (int i=0; i0) { elem.setAttribute("lineNumberFontSize", Integer.toString(lineNumberFontSize)); } root.appendChild(elem); elem = doc.createElement("foldIndicator"); elem.setAttribute("fg", colorToString(foldIndicatorFG)); elem.setAttribute("iconBg", colorToString(foldBG)); root.appendChild(elem); elem = doc.createElement("iconRowHeader"); elem.setAttribute("activeLineRange", colorToString(activeLineRangeColor)); elem.setAttribute("inheritsGutterBG", Boolean.toString(iconRowHeaderInheritsGutterBG)); root.appendChild(elem); elem = doc.createElement("tokenStyles"); Field[] fields = TokenTypes.class.getFields(); for (int i=0; i * "$00ff00" * "00ff00" *

* will return new Color(0, 255, 0). * * @param s The string to evaluate. * @return The color. */ private static final Color stringToColor(String s) { return stringToColor(s, null); } /** * Returns the color represented by a string. The input is expected to * be a 6-digit hex string, optionally prefixed by a '$'. For example, * either of the following: *
	 * "$00ff00"
	 * "00ff00"
	 * 
* will return new Color(0, 255, 0). * * @param s The string to evaluate. * @param defaultVal The color to use if s is * "default". * @return The color. */ private static final Color stringToColor(String s, Color defaultVal) { if (s==null || "default".equalsIgnoreCase(s)) { return defaultVal; } if (s.length()==6 || s.length()==7) { if (s.charAt(0)=='$') { s = s.substring(1); } return new Color(Integer.parseInt(s, 16)); } return null; } /** * Loads a SyntaxScheme from an XML file. */ private static class XmlHandler extends DefaultHandler { private Theme theme; @Override public void error(SAXParseException e) throws SAXException { throw e; } @Override public void fatalError(SAXParseException e) throws SAXException { throw e; } public static void load(Theme theme, InputStream in) throws IOException { SAXParserFactory spf = SAXParserFactory.newInstance(); spf.setValidating(true); try { SAXParser parser = spf.newSAXParser(); XMLReader reader = parser.getXMLReader(); XmlHandler handler = new XmlHandler(); handler.theme = theme; reader.setEntityResolver(handler); reader.setContentHandler(handler); reader.setDTDHandler(handler); reader.setErrorHandler(handler); InputSource is = new InputSource(in); is.setEncoding("UTF-8"); reader.parse(is); } catch (/*SAX|ParserConfiguration*/Exception se) { se.printStackTrace(); throw new IOException(se.toString()); } } private static final int parseInt(Attributes attrs, String attr, int def) { int value = def; String temp = attrs.getValue(attr); if (temp != null) { try { value = Integer.parseInt(temp); } catch (NumberFormatException nfe) { nfe.printStackTrace(); } } return value; } @Override public InputSource resolveEntity(String publicID, String systemID) throws SAXException { return new InputSource(getClass(). getResourceAsStream("themes/theme.dtd")); } @Override public void startElement(String uri, String localName, String qName, Attributes attrs) { if ("background".equals(qName)) { String color = attrs.getValue("color"); if (color!=null) { theme.bgColor = stringToColor(color, getDefaultBG()); } else { String img = attrs.getValue("image"); if (img!=null) { throw new IllegalArgumentException("Not yet implemented"); } } } // The base font to use in the editor. else if ("baseFont".equals(qName)) { int size = theme.baseFont.getSize(); String sizeStr = attrs.getValue("size"); if (sizeStr!=null) { size = Integer.parseInt(sizeStr); } String family = attrs.getValue("family"); if (family!=null) { theme.baseFont = getFont(family, Font.PLAIN, size); } else if (sizeStr!=null) { // No family specified, keep original family theme.baseFont = theme.baseFont.deriveFont(size*1f); } } else if ("caret".equals(qName)) { String color = attrs.getValue("color"); theme.caretColor = stringToColor(color); } else if ("currentLineHighlight".equals(qName)) { String color = attrs.getValue("color"); theme.currentLineHighlight = stringToColor(color); String fadeStr = attrs.getValue("fade"); boolean fade = Boolean.valueOf(fadeStr).booleanValue(); theme.fadeCurrentLineHighlight = fade; } else if ("foldIndicator".equals(qName)) { String color = attrs.getValue("fg"); theme.foldIndicatorFG = stringToColor(color); color = attrs.getValue("iconBg"); theme.foldBG = stringToColor(color); } else if ("gutterBorder".equals(qName)) { String color = attrs.getValue("color"); theme.gutterBorderColor = stringToColor(color); } else if ("iconRowHeader".equals(qName)) { String color = attrs.getValue("activeLineRange"); theme.activeLineRangeColor = stringToColor(color); String inheritBGStr = attrs.getValue("inheritsGutterBG"); theme.iconRowHeaderInheritsGutterBG = inheritBGStr==null ? false : Boolean.valueOf(inheritBGStr); } else if ("lineNumbers".equals(qName)) { String color = attrs.getValue("fg"); theme.lineNumberColor = stringToColor(color); theme.lineNumberFont = attrs.getValue("fontFamily"); theme.lineNumberFontSize = parseInt(attrs, "fontSize", -1); } else if ("marginLine".equals(qName)) { String color = attrs.getValue("fg"); theme.marginLineColor = stringToColor(color); } else if ("markAllHighlight".equals(qName)) { String color = attrs.getValue("color"); theme.markAllHighlightColor = stringToColor(color); } else if ("markOccurrencesHighlight".equals(qName)) { String color = attrs.getValue("color"); theme.markOccurrencesColor = stringToColor(color); String border = attrs.getValue("border"); theme.markOccurrencesBorder = Boolean.valueOf(border).booleanValue(); } else if ("matchedBracket".equals(qName)) { String fg = attrs.getValue("fg"); theme.matchedBracketFG = stringToColor(fg); String bg = attrs.getValue("bg"); theme.matchedBracketBG = stringToColor(bg); String highlightBoth = attrs.getValue("highlightBoth"); theme.matchedBracketHighlightBoth = Boolean.valueOf(highlightBoth).booleanValue(); String animate = attrs.getValue("animate"); theme.matchedBracketAnimate = Boolean.valueOf(animate).booleanValue(); } else if ("hyperlinks".equals(qName)) { String fg = attrs.getValue("fg"); theme.hyperlinkFG = stringToColor(fg); } else if ("language".equals(qName)) { String indexStr = attrs.getValue("index"); int index = Integer.parseInt(indexStr) - 1; if (theme.secondaryLanguages.length>index) { // Sanity Color bg = stringToColor(attrs.getValue("bg")); theme.secondaryLanguages[index] = bg; } } else if ("selection".equals(qName)) { String useStr = attrs.getValue("useFG"); theme.useSelctionFG = Boolean.valueOf(useStr).booleanValue(); String color = attrs.getValue("fg"); theme.selectionFG = stringToColor(color, getDefaultSelectionFG()); //System.out.println(theme.selectionFG); color = attrs.getValue("bg"); theme.selectionBG = stringToColor(color, getDefaultSelectionBG()); String roundedStr = attrs.getValue("roundedEdges"); theme.selectionRoundedEdges = Boolean.valueOf(roundedStr). booleanValue(); } // Start of the syntax scheme definition else if ("tokenStyles".equals(qName)) { theme.scheme = new SyntaxScheme(theme.baseFont, false); } // A style in the syntax scheme else if ("style".equals(qName)) { String type = attrs.getValue("token"); Field field = null; try { field = Token.class.getField(type); } catch (RuntimeException re) { throw re; // FindBugs } catch (Exception e) { System.err.println("Invalid token type: " + type); return; } if (field.getType()==int.class) { int index = 0; try { index = field.getInt(theme.scheme); } catch (IllegalArgumentException e) { e.printStackTrace(); return; } catch (IllegalAccessException e) { e.printStackTrace(); return; } String fgStr = attrs.getValue("fg"); Color fg = stringToColor(fgStr); theme.scheme.getStyle(index).foreground = fg; String bgStr = attrs.getValue("bg"); Color bg = stringToColor(bgStr); theme.scheme.getStyle(index).background = bg; Font font = theme.baseFont; String familyName = attrs.getValue("fontFamily"); if (familyName!=null) { font = getFont(familyName, font.getStyle(), font.getSize()); } String sizeStr = attrs.getValue("fontSize"); if (sizeStr!=null) { try { float size = Float.parseFloat(sizeStr); size = Math.max(size, 1f); font = font.deriveFont(size); } catch (NumberFormatException nfe) { nfe.printStackTrace(); } } theme.scheme.getStyle(index).font = font; boolean styleSpecified = false; boolean bold = false; boolean italic = false; String boldStr = attrs.getValue("bold"); if (boldStr!=null) { bold = Boolean.valueOf(boldStr).booleanValue(); styleSpecified = true; } String italicStr = attrs.getValue("italic"); if (italicStr!=null) { italic = Boolean.valueOf(italicStr).booleanValue(); styleSpecified = true; } if (styleSpecified) { int style = 0; if (bold) { style |= Font.BOLD; } if (italic) { style |= Font.ITALIC; } Font orig = theme.scheme.getStyle(index).font; theme.scheme.getStyle(index).font = orig.deriveFont(style); } String ulineStr = attrs.getValue("underline"); if (ulineStr!=null) { boolean uline= Boolean.valueOf(ulineStr).booleanValue(); theme.scheme.getStyle(index).underline = uline; } } } } @Override public void warning(SAXParseException e) throws SAXException { throw e; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy