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

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

The newest version!
/*
 * 02/26/2004
 *
 * SyntaxScheme.java - The set of colors and tokens used by an RSyntaxTextArea
 * to color tokens.
 * 
 * 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.Graphics2D;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import javax.swing.text.StyleContext;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;


/**
 * The set of colors and styles used by an RSyntaxTextArea to
 * color tokens.

* You can use this class to programmatically set the fonts and colors used in * an RSyntaxTextArea, but for more powerful, externalized control, consider * using {@link Theme}s instead. * * @author Robert Futrell * @version 1.0 * @see Theme */ public class SyntaxScheme implements Cloneable, TokenTypes { private Style[] styles; private static final String VERSION = "*ver1"; /** * Creates a color scheme that either has all color values set to * a default value or set to null. * * @param useDefaults If true, all color values will * be set to default colors; if false, all colors * will be initially null. */ public SyntaxScheme(boolean useDefaults) { styles = new Style[DEFAULT_NUM_TOKEN_TYPES]; if (useDefaults) { restoreDefaults(null); } } /** * Creates a default color scheme. * * @param baseFont The base font to use. Keywords will be a bold version * of this font, and comments will be an italicized version of this * font. */ public SyntaxScheme(Font baseFont) { this(baseFont, true); } /** * Creates a default color scheme. * * @param baseFont The base font to use. Keywords will be a bold version * of this font, and comments will be an italicized version of this * font. * @param fontStyles Whether bold and italic should be used in the scheme * (vs. all tokens using a plain font). */ public SyntaxScheme(Font baseFont, boolean fontStyles) { styles = new Style[DEFAULT_NUM_TOKEN_TYPES]; restoreDefaults(baseFont, fontStyles); } /** * Changes the "base font" for this syntax scheme. This is called by * RSyntaxTextArea when its font changes via * setFont(). This looks for tokens that use a derivative of * the text area's old font (but bolded and/or italicized) and make them * use the new font with those stylings instead. This is desirable because * most programmers prefer a single font to be used in their text editor, * but might want bold (say for keywords) or italics. * * @param old The old font of the text area. * @param font The new font of the text area. */ void changeBaseFont(Font old, Font font) { for (int i=0; itrue if this color scheme and * otherScheme are the same scheme; * false otherwise. */ @Override public boolean equals(Object otherScheme) { // No need for null check; instanceof takes care of this for us, // i.e. "if (!(null instanceof Foo))" evaluates to "true". if (!(otherScheme instanceof SyntaxScheme)) { return false; } Style[] otherSchemes = ((SyntaxScheme)otherScheme).styles; int length = styles.length; for (int i=0; i"$rrggbb". * * @param c The color. * @return The string representation of the color. */ private static final String getHexString(Color c) { return "$" + Integer.toHexString((c.getRGB() & 0xffffff)+0x1000000). substring(1); } /** * Returns the specified style. * * @param index The index of the style. * @return The style. * @see #setStyle(int, Style) * @see #getStyleCount() */ public Style getStyle(int index) { return styles[index]; } /** * Returns the number of styles. * * @return The number of styles. * @see #getStyle(int) */ public int getStyleCount() { return styles.length; } /** * Used by third party implementors e.g. SquirreL SQL. Most applications do * not need to call this method. *

* Note that the returned array is not a copy of the style data; editing the * array will modify the styles used by any RSyntaxTextArea * using this scheme. * * @return The style array. * @see #setStyles(Style[]) */ public Style[] getStyles() { return styles; } /** * This is implemented to be consistent with {@link #equals(Object)}. * This is a requirement to keep FindBugs happy. * * @return The hash code for this object. */ @Override public int hashCode() { // Keep me fast. Iterating over *all* syntax schemes contained is // probably much slower than a "bad" hash code here. int hashCode = 0; int count = styles.length; for (int i=0; i * * Consider using the {@link Theme} class for saving and loading RSTA * styles rather than using this API. * * @param baseFont The font to use as the "base" for the syntax scheme. * If this is null, a default monospaced font is used. * @param in The stream to load from. It is up to the caller to close this * stream when they are done. * @return The syntax scheme. * @throws IOException If an IO error occurs. */ public static SyntaxScheme load(Font baseFont, InputStream in) throws IOException { if (baseFont==null) { baseFont = RSyntaxTextArea.getDefaultFont(); } return SyntaxSchemeLoader.load(baseFont, in); } /** * Loads a syntax highlighting color scheme from a string created from * toCommaSeparatedString. This method is useful for saving * and restoring color schemes.

* * Consider using the {@link Theme} class for saving and loading RSTA * styles rather than using this API. * * @param string A string generated from {@link #toCommaSeparatedString()}. * @return A color scheme. * @see #toCommaSeparatedString() */ public static SyntaxScheme loadFromString(String string) { return loadFromString(string, DEFAULT_NUM_TOKEN_TYPES); } /** * Loads a syntax highlighting color scheme from a string created from * toCommaSeparatedString. This method is useful for saving * and restoring color schemes.

* * Consider using the {@link Theme} class for saving and loading RSTA * styles rather than using this API. * * @param string A string generated from {@link #toCommaSeparatedString()}. * @param tokenTypeCount The number of token types saved in this string. * This should be the number of token types saved by your custom * SyntaxScheme subclass, * or {@link TokenTypes#DEFAULT_NUM_TOKEN_TYPES} if you used the * standard implementation (which most people will). * @return A color scheme. * @see #loadFromString(String) * @see #toCommaSeparatedString() */ public static SyntaxScheme loadFromString(String string, int tokenTypeCount) { SyntaxScheme scheme = new SyntaxScheme(true); try { if (string!=null) { String[] tokens = string.split(",", -1); // Check the version string, use defaults if incompatible if (tokens.length==0 || !VERSION.equals(tokens[0])) { return scheme; // Still set to defaults } int tokenCount = tokenTypeCount*7 + 1; // Version string if (tokens.length!=tokenCount) { throw new Exception( "Not enough tokens in packed color scheme: expected " + tokenCount + ", found " + tokens.length); } // Use StyleContext to create fonts to get composite fonts for // Asian glyphs. StyleContext sc = StyleContext.getDefaultStyleContext(); // Loop through each token style. Format: // "index,(fg|-),(bg|-),(t|f),((font,style,size)|(-,,))" for (int i=0; i keep fg as null fg = stringToColor(temp); } Color bg = null; temp = tokens[pos+2]; if (!"-".equals(temp)) { // "-" => keep bg as null bg = stringToColor(temp); } // Check for "true" or "false" since we don't want to // accidentally suck in an int representing the next // packed color, and any string != "true" means false. temp = tokens[pos+3]; if (!"t".equals(temp) && !"f".equals(temp)) throw new Exception("Expected 't' or 'f', found " + temp); boolean underline = "t".equals(temp); Font font = null; String family = tokens[pos+4]; if (!"-".equals(family)) { font = sc.getFont(family, Integer.parseInt(tokens[pos+5]), // style Integer.parseInt(tokens[pos+6])); // size } scheme.styles[i] = new Style(fg, bg, font, underline); } } } catch (Exception e) { e.printStackTrace(); } return scheme; } void refreshFontMetrics(Graphics2D g2d) { // It is assumed that any rendering hints are already applied to g2d. for (int i=0; inull, then a default monospaced font is * used. */ public void restoreDefaults(Font baseFont) { restoreDefaults(baseFont, true); } /** * Restores all colors and fonts to their default values. * * @param baseFont The base font to use when creating this scheme. If * this is null, then a default monospaced font is * used. * @param fontStyles Whether bold and italic should be used in the scheme * (vs. all tokens using a plain font). */ public void restoreDefaults(Font baseFont, boolean fontStyles) { // Colors used by tokens. Color comment = new Color(0,128,0); Color docComment = new Color(164,0,0); Color markupComment = new Color(0, 96, 0); Color keyword = Color.BLUE; Color dataType = new Color(0,128,128); Color function = new Color(173,128,0); Color preprocessor = new Color(128,128,128); Color operator = new Color(128, 64, 64); Color regex = new Color(0,128,164); Color variable = new Color(255,153,0); Color literalNumber = new Color(100,0,200); Color literalString = new Color(220,0,156); Color error = new Color(148,148,0); // (Possible) special font styles for keywords and comments. if (baseFont==null) { baseFont = RSyntaxTextArea.getDefaultFont(); } Font commentFont = baseFont; Font keywordFont = baseFont; if (fontStyles) { // WORKAROUND for Sun JRE bug 6282887 (Asian font bug in 1.4/1.5) // That bug seems to be hidden now, see 6289072 instead. StyleContext sc = StyleContext.getDefaultStyleContext(); Font boldFont = sc.getFont(baseFont.getFamily(), Font.BOLD, baseFont.getSize()); Font italicFont = sc.getFont(baseFont.getFamily(), Font.ITALIC, baseFont.getSize()); commentFont = italicFont;//baseFont.deriveFont(Font.ITALIC); keywordFont = boldFont;//baseFont.deriveFont(Font.BOLD); } styles[COMMENT_EOL] = new Style(comment, null, commentFont); styles[COMMENT_MULTILINE] = new Style(comment, null, commentFont); styles[COMMENT_DOCUMENTATION] = new Style(docComment, null, commentFont); styles[COMMENT_KEYWORD] = new Style(new Color(255,152,0), null, commentFont); styles[COMMENT_MARKUP] = new Style(Color.gray, null, commentFont); styles[RESERVED_WORD] = new Style(keyword, null, keywordFont); styles[RESERVED_WORD_2] = new Style(keyword, null, keywordFont); styles[FUNCTION] = new Style(function); styles[LITERAL_BOOLEAN] = new Style(literalNumber); styles[LITERAL_NUMBER_DECIMAL_INT] = new Style(literalNumber); styles[LITERAL_NUMBER_FLOAT] = new Style(literalNumber); styles[LITERAL_NUMBER_HEXADECIMAL] = new Style(literalNumber); styles[LITERAL_STRING_DOUBLE_QUOTE] = new Style(literalString); styles[LITERAL_CHAR] = new Style(literalString); styles[LITERAL_BACKQUOTE] = new Style(literalString); styles[DATA_TYPE] = new Style(dataType, null, keywordFont); styles[VARIABLE] = new Style(variable); styles[REGEX] = new Style(regex); styles[ANNOTATION] = new Style(Color.gray); styles[IDENTIFIER] = new Style(null); styles[WHITESPACE] = new Style(Color.gray); styles[SEPARATOR] = new Style(Color.RED); styles[OPERATOR] = new Style(operator); styles[PREPROCESSOR] = new Style(preprocessor); styles[MARKUP_TAG_DELIMITER] = new Style(Color.RED); styles[MARKUP_TAG_NAME] = new Style(Color.BLUE); styles[MARKUP_TAG_ATTRIBUTE] = new Style(new Color(63,127,127)); styles[MARKUP_TAG_ATTRIBUTE_VALUE]= new Style(literalString); styles[MARKUP_COMMENT] = new Style(markupComment, null, commentFont); styles[MARKUP_DTD] = new Style(function); styles[MARKUP_PROCESSING_INSTRUCTION] = new Style(preprocessor); styles[MARKUP_CDATA] = new Style(new Color(0xcc6600)); styles[MARKUP_CDATA_DELIMITER] = new Style(new Color(0x008080)); styles[MARKUP_ENTITY_REFERENCE] = new Style(dataType); styles[ERROR_IDENTIFIER] = new Style(error); styles[ERROR_NUMBER_FORMAT] = new Style(error); styles[ERROR_STRING_DOUBLE] = new Style(error); styles[ERROR_CHAR] = new Style(error); // Issue #34: If an application modifies TokenTypes to add new built-in // token types, we'll get NPEs if not all styles are initialized. for (int i=0; i$', it is assumed to be in hex, otherwise it is * assumed to be decimal. So, for example, both of these: *

	 * "$00ff00"
	 * "65280"
	 * 
* will return new Color(0, 255, 0). * * @param s The string to evaluate. * @return The color. */ private static final Color stringToColor(String s) { // Check for decimal as well as hex, for backward // compatibility (fix from GwynEvans on forums) char ch = s.charAt(0); return new Color((ch=='$' || ch=='#') ? Integer.parseInt(s.substring(1),16) : Integer.parseInt(s)); } /** * Returns this syntax highlighting scheme as a comma-separated list of * values as follows: *
    *
  • If a color is non-null, it is added as a 24-bit integer * of the form ((r<<16) | (g<<8) | (b)); if it is * null, it is added as "-,". *
  • The font and style (bold/italic) is added as an integer like so: * "family, style, size". *
  • The entire syntax highlighting scheme is thus one long string of * color schemes of the format "i,[fg],[bg],uline,[style], * where: *
      *
    • i is the index of the syntax scheme. *
    • fg and bg are the foreground and background * colors for the scheme, and may be null (represented by * -). *
    • uline is whether or not the font should be * underlined, and is either t or f. *
    • style is the family,style,size * triplet described above. *
    *
* * @return A string representing the rgb values of the colors. * @see #loadFromString(String) */ public String toCommaSeparatedString() { StringBuilder sb = new StringBuilder(VERSION); sb.append(','); for (int i=0; iSyntaxScheme from an XML file. */ private static class SyntaxSchemeLoader extends DefaultHandler { private Font baseFont; private SyntaxScheme scheme; public SyntaxSchemeLoader(Font baseFont) { scheme = new SyntaxScheme(baseFont); } public static SyntaxScheme load(Font baseFont, InputStream in) throws IOException { SyntaxSchemeLoader parser = null; try { XMLReader reader = XMLReaderFactory.createXMLReader(); parser = new SyntaxSchemeLoader(baseFont); parser.baseFont = baseFont; reader.setContentHandler(parser); InputSource is = new InputSource(in); is.setEncoding("UTF-8"); reader.parse(is); } catch (SAXException se) { throw new IOException(se.toString()); } return parser.scheme; } @Override public void startElement(String uri, String localName, String qName, Attributes attrs) { 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(scheme); } catch (IllegalArgumentException e) { e.printStackTrace(); return; } catch (IllegalAccessException e) { e.printStackTrace(); return; } String fgStr = attrs.getValue("fg"); if (fgStr!=null) { Color fg = stringToColor(fgStr); scheme.styles[index].foreground = fg; } String bgStr = attrs.getValue("bg"); if (bgStr!=null) { Color bg = stringToColor(bgStr); scheme.styles[index].background = bg; } 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; } scheme.styles[index].font = baseFont.deriveFont(style); } String ulineStr = attrs.getValue("underline"); if (ulineStr!=null) { boolean uline= Boolean.valueOf(ulineStr).booleanValue(); scheme.styles[index].underline = uline; } } } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy