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

com.jidesoft.swing.StyledLabelBuilder Maven / Gradle / Ivy

There is a newer version: 3.6.18
Show newest version
/*
 * @(#)StyledLabelBuilder.java 9/7/2011
 *
 * Copyright 2002 - 2011 JIDE Software Inc. All rights reserved.
 */

package com.jidesoft.swing;

import java.awt.*;
import java.util.*;
import java.util.List;

/**
 * StyledLabelBuilder is a quick way to define StyledLabel. It provides two ways to handle the creation and
 * modification of StyleLabels.
 * 

* The first is to use it as a builder (thus the name). This way is preferred if you want to create a StyledLabel with a * specific format and partially generic content. Example: *

StyledLabel label = new StyledLabelBuilder()
 * 	.add(file.getName())
 * 	.add(" (", Font.BOLD)
 * 	.add(file.getPath(), "italic") // using annotation style - see section two for information about annotations
 * 	.add(")", Font.BOLD)
 * 	.createLabel();
* This code would be used to create a label like "something.txt (/temp/something.txt)" with some styling (the braces * would be bold, the path would be italic). In case you find yourself reusing a specific style quite often in such a * label you might consider to create a style for it. This can be done with the help of the {@link #register}-methods. * As an example, the code above could be rewritten like this (though it only pays off when used for creation of longer * styles): *
StyledLabelBuilder builder = new StyledLabelBuilder()
 * 	.register("OPERATOR", Font.BOLD, new Color(0x000052)) // use parameters
 * 	.register("PATH", "italic, f:#0000CD"); // or style annotations
 * StyledLabel label = builder
 * 	.add(file.getName())
 * 	.add(" (", "OPERATOR")
 * 	.add(file.getPath(), "PATH,underlined") // use a style + style annotation
 * 	.add(")", "OPERATOR")
 * 	.createLabel();
* Note that we're using different font colors this time. It pays off as soon as you want to modify a specific group of * text parts or as your styles start to get more complicated. The {@link #clear()}-method is very useful if you want to * use these styles. Instead of re-creating a new builder each time, you can use the clear-method to clear the internal * buffer of text without removing the previously defined styles. *

* Let's have an example (we're going to reuse the code from above!): *

builder.clear();
 * builder
 * 	.add(file.getName())
 * 	.add(" (", "OPERATOR")
 * 	.add(file.getPath(), "PATH")
 * 	.add(")", "OPERATOR")
 *  .configure(label);
*

* Please be noted that you need escape the ":" in your text string when necessary. For example, "{00:00:00:BOLD}" need * to be changed as "{00\\:00\\:00:BOLD}. *

* If we were using Java 5, we could also do this: *

// no need to call {@link #clear()} this time
 * builder.configure(label, String.format("%s ({%s:PATH})", file.getName(), file.getPath()));
*

* Each of the {@link #add} and {@link #register} methods is the same as using the corresponding StyleRange-constructor * directly (except that you don't have to care about its start and length). *

* The second, even more advanced, way to use this class is in combination with an annotated string. Using the static * {@link #setStyledText} or {@link #createStyledLabel} methods you can create a fully styled label from just on string. * This is ideal if you need the string to be configurable or locale-specific. The usage is even more easy than the * builder-approach: StyledLabel label = StyledLabelBuilder.createStyledLabel("I'm your {first:bold} styled * {label:italic}!"); In the above example, the resulting label would have a a bold "first" and an italic * "label". Each annotation is started by a "{" and ended by a "}". The text you want to be styled accordingly is * separated from its annotations by a ":". If your text needs to contain a ":" itself, you need to escape it using the * "\" character. The same goes for "{" that are not supposed to start an annotation. You don't need to escape the "}" * at all. If it is used within the annotated string it'll be ignored. It only counts after the annotation separator * (":"). There are multiply annotations available. Each annotation offers a shortcut made up from one or two of their * characters. For example: We used "bold" and "italic" in the example above, but we could've used "b" and "i" instead. * It is also possible to combine multiple styles by separating them with a ",". As an example: {This text is * bold, italic and blue:b,i,f:blue} Instead of writing "b,i" you can also write "bi" or "bolditalic". This * example brings us to colors. They've to be started with "f" or "font" for the font-color or "l" or "line" for the * line-color or "b" or "background" for the background color. There are a lot of ways to specify a color. You may use * its HTML name (as I did in the above example) or any of these: f:(0,0,255) f:#00F l:#0000FF l:0x0000FF The "#00F" * notation is just like it is in CSS. It is the same as if you had written "#0000FF". You can get and modify the map of * color-names the parser is using with the static {@link #getColorNamesMap()}-method. *

* You saw some styles above. Here is a complete list of styles and its shortcut. *

* Font styles

  • plain or p
  • bold or b
  • italic or i
  • bolditalic or bi
Additional * styles
  • strike or s
  • doublestrike or ds
  • waved or w
  • underlined or u
  • dotted or d *
  • superscript or sp
  • subscript or sb
Global flags: You can enable global flags by using "@" at the * end of the String.
  • rows or row or r: it can take up to three parameters separately ":". The first one is * preferred rows, the second one is minimum rows and the last one is the maximum rows.
*

* * @author Patrick Gotthardt */ public class StyledLabelBuilder { private StringBuffer buffer; private List ranges; private int start; private Map styles; public StyledLabelBuilder() { buffer = new StringBuffer(); ranges = new ArrayList(); styles = new HashMap(); start = 0; } public void clear() { buffer.delete(0, buffer.length()); ranges.clear(); start = 0; } public StyledLabelBuilder register(String text, Color fontColor) { styles.put(text, new StyleRange(fontColor)); return this; } public StyledLabelBuilder register(String text, int fontStyle) { styles.put(text, new StyleRange(fontStyle)); return this; } public StyledLabelBuilder register(String text, int fontStyle, Color fontColor) { styles.put(text, new StyleRange(fontStyle, fontColor)); return this; } public StyledLabelBuilder register(String text, int fontStyle, Color fontColor, int additionalStyle) { styles.put(text, new StyleRange(start, text.length(), fontStyle, fontColor, additionalStyle)); return this; } public StyledLabelBuilder register(String text, int fontStyle, Color fontColor, int additionalStyle, Color lineColor) { styles.put(text, new StyleRange(start, text.length(), fontStyle, fontColor, additionalStyle, lineColor)); return this; } public StyledLabelBuilder register(String text, int fontStyle, Color fontColor, int additionalStyle, Color lineColor, Stroke lineStroke) { styles.put(text, new StyleRange(start, text.length(), fontStyle, fontColor, additionalStyle, lineColor, lineStroke)); return this; } public StyledLabelBuilder register(String text, int fontStyle, Color fontColor, int additionalStyle, Color lineColor, Stroke lineStroke, float fontShrinkRatio) { styles.put(text, new StyleRange(start, text.length(), fontStyle, fontColor, additionalStyle, lineColor, lineStroke, fontShrinkRatio)); return this; } public StyledLabelBuilder register(String text, int fontStyle, int additionalStyle) { styles.put(text, new StyleRange(start, text.length(), fontStyle, additionalStyle)); return this; } public StyledLabelBuilder register(String text, int fontStyle, int additionalStyle, float fontShrinkRatio) { styles.put(text, new StyleRange(start, text.length(), fontStyle, additionalStyle, fontShrinkRatio)); return this; } public StyledLabelBuilder register(String text, String format) { ParsedStyleResult result = parseStyleAnnotation(format.toCharArray(), 0, this); styles.put(text, new StyleRange(result.fontStyle, result.fontColor, result.backgroundColor, result.additionalStyle, result.lineColor)); return this; } // public StyledLabelBuilder add(String text) { buffer.append(text); start += text.length(); return this; } public StyledLabelBuilder add(String text, Color fontColor) { ranges.add(new StyleRange(start, text.length(), fontColor)); return add(text); } public StyledLabelBuilder add(String text, int fontStyle) { ranges.add(new StyleRange(start, text.length(), fontStyle)); return add(text); } public StyledLabelBuilder add(String text, int fontStyle, Color fontColor) { ranges.add(new StyleRange(start, text.length(), fontStyle, fontColor)); return add(text); } public StyledLabelBuilder add(String text, int fontStyle, Color fontColor, int additionalStyle) { ranges.add(new StyleRange(start, text.length(), fontStyle, fontColor, additionalStyle)); return add(text); } public StyledLabelBuilder add(String text, int fontStyle, Color fontColor, int additionalStyle, Color lineColor) { ranges.add(new StyleRange(start, text.length(), fontStyle, fontColor, additionalStyle, lineColor)); return add(text); } public StyledLabelBuilder add(String text, int fontStyle, Color fontColor, Color backgroundColor, int additionalStyle, Color lineColor) { ranges.add(new StyleRange(start, text.length(), fontStyle, fontColor, backgroundColor, additionalStyle, lineColor)); return add(text); } public StyledLabelBuilder add(String text, int fontStyle, Color fontColor, Color backgroundColor, int additionalStyle, Color lineColor, Stroke lineStroke) { ranges.add(new StyleRange(start, text.length(), fontStyle, fontColor, backgroundColor, additionalStyle, lineColor, lineStroke)); return add(text); } public StyledLabelBuilder add(String text, int fontStyle, Color fontColor, int additionalStyle, Color lineColor, Stroke lineStroke, float fontShrinkRatio) { ranges.add(new StyleRange(start, text.length(), fontStyle, fontColor, additionalStyle, lineColor, lineStroke, fontShrinkRatio)); return add(text); } public StyledLabelBuilder add(String text, String style) { StyleRange range = (StyleRange) styles.get(style); // not a stored style, thus it might be an annotation if (range == null) { ParsedStyleResult result = parseStyleAnnotation(style.toCharArray(), 0, this); return add(text, result.fontStyle, result.fontColor, result.backgroundColor, result.additionalStyle, result.lineColor); } return add(text, range.getFontStyle(), range.getFontColor(), range.getAdditionalStyle(), range.getLineColor(), range.getLineStroke(), range.getFontShrinkRatio()); } public StyledLabelBuilder add(String text, int fontStyle, int additionalStyle) { ranges.add(new StyleRange(start, text.length(), fontStyle, additionalStyle)); return add(text); } public StyledLabelBuilder add(String text, int fontStyle, int additionalStyle, float fontShrinkRatio) { ranges.add(new StyleRange(start, text.length(), fontStyle, additionalStyle, fontShrinkRatio)); return add(text); } public StyledLabel configure(StyledLabel label, String style) { StyledLabelBuilder.setStyledText(label, style, this); return label; } public StyledLabel configure(StyledLabel label) { label.setText(buffer.toString()); int size = ranges.size(); for (int i = 0; i < size; i++) { label.addStyleRange((StyleRange) ranges.get(i)); } return label; } public StyledLabel createLabel() { return configure(new StyledLabel()); } // complex part public static StyledLabel createStyledLabel(String text) { StyledLabel label = new StyledLabel(); setStyledText(label, text); return label; } /** * Before your call this method, you need call {link@ #parseToVoidStyledTextConfusion(String)} to make sure the text * will not contain confusion "\" or "{" * * @param label the styledLabel to be set with the text * @param text the styled text */ public static void setStyledText(StyledLabel label, String text) { setStyledText(label, text.toCharArray()); } private static void setStyledText(StyledLabel label, String text, StyledLabelBuilder builder) { setStyledText(label, text.toCharArray(), builder); } /** * Before your call this method, you need call {link@ #parseToVoidStyledTextConfusion(String)} to make sure the text * will not contain confusion "\" or "{" * * @param label the styledLabel to be set with the text * @param text the styled text */ public static void setStyledText(StyledLabel label, char[] text) { setStyledText(label, text, null); } private static void setStyledText(StyledLabel label, char[] text, StyledLabelBuilder builder) { StringBuffer labelText = new StringBuffer(text.length); boolean escaped = false; label.clearStyleRanges(); label.setLineWrap(false); int endOfText = text.length; for (int j = text.length - 1; j >= 0; j--) { if (text[j] == '@') { if (isGlobalConfiguration(label, text, j + 1)) { endOfText = j; } break; } } for (int i = 0; i < endOfText; i++) { if (escaped) { labelText.append(text[i]); escaped = false; continue; } switch (text[i]) { case '{': ParsedStyleResult result = parseStylePart(text, i + 1, builder); if (result == null) { labelText.append(text[i]); continue; } int realIndex = labelText.length(); labelText.append(result.text); if (result.text.length() > 0) { label.addStyleRange(new StyleRange( realIndex, result.text.length(), result.fontStyle, result.fontColor, result.backgroundColor, result.additionalStyle, result.lineColor)); } i = Math.max(i, result.endOffset); break; case '\\': escaped = true; break; default: labelText.append(text[i]); break; } } label.setText(labelText.toString()); } private static boolean isGlobalConfiguration(StyledLabel label, char[] text, int offset) { String globalString = new String(text, offset, text.length - offset); String[] subStringsLevel0 = globalString.split(","); if (subStringsLevel0.length <= 0 || subStringsLevel0[0] == null) { return false; } int defaultRows = 1; int maxRows = 0; int minRows = 0; int preferredWidth = 0; for (String subStringLevel0 : subStringsLevel0) { String[] subStrings = subStringLevel0.split(":"); if (subStrings.length <= 0 || subStrings[0] == null) { return false; } String property = subStrings[0].trim().toLowerCase(); if ("rows".equals(property) || "row".equals(property) || "r".equals(property)) { if (subStrings.length > 4 || subStrings.length < 1) { return false; } if (subStrings.length >= 2 && subStrings[1].trim().length() > 0) { try { defaultRows = Integer.valueOf(subStrings[1]); } catch (NumberFormatException e) { return false; } } if (subStrings.length >= 3 && subStrings[2].trim().length() > 0) { try { minRows = Integer.valueOf(subStrings[2]); } catch (NumberFormatException e) { return false; } if (minRows > defaultRows || minRows < 0) { if (subStrings[1].trim().length() > 0) { minRows = 0; } else if (minRows > defaultRows) { defaultRows = minRows; } else { minRows = 0; } } } if (subStrings.length >= 4 && subStrings[3].trim().length() > 0) { try { maxRows = Integer.valueOf(subStrings[3]); } catch (NumberFormatException e) { return false; } if (maxRows < defaultRows || maxRows < 0) { maxRows = 0; } } } else if ("w".equals(property) || "width".equals(property) || "preferredwidth".equals(property)) { if (subStrings.length != 2) { return false; } if (subStrings[1].trim().length() > 0) { try { preferredWidth = Integer.valueOf(subStrings[1]); } catch (NumberFormatException e) { return false; } } } else { return false; } } label.setLineWrap(true); label.setRows(defaultRows); label.setMaxRows(maxRows); label.setMinRows(minRows); label.setPreferredWidth(preferredWidth); if (defaultRows == 1 && maxRows == 1 && minRows == 1) { label.setLineWrap(false); } return true; } /** * This method need to be invoked to format your string before you invoke {@link #setStyledText(StyledLabel, * String)} or {@link #setStyledText(StyledLabel, char[])} * * @param originalString the original string. * @return a parsed string with "\" replaced by "\\" and "{" replaced by "\{". */ public static String parseToVoidStyledTextConfusion(String originalString) { String destString = originalString.replaceAll("\\\\", "\\\\\\\\"); destString = destString.replaceAll("\\{", "\\\\{"); return destString; } private static ParsedStyleResult parseStylePart(char[] text, int start, StyledLabelBuilder builder) { ParsedStyleResult result = new ParsedStyleResult(); int findIndex, i = start; // find end of text first findIndex = findNext(text, ':', i); int indexMatchingBracket = findMatchingBracket(text, start); if (findIndex < 0 || findIndex > indexMatchingBracket) { return null; } result.text = createTrimmedString(text, i, findIndex - 1); return parseStyleAnnotation(text, findIndex + 1, builder, result); } private static int findMatchingBracket(char[] text, int offset) { if (text.length == 0) return -1; if (offset >= text.length) return -1; // Count is 1 initially because we have already // `found' one opening bracket int count = 1; // Number of characters to check int len = text.length - offset; // Scan forwards for (int i = 0; i < len; i++, offset++) { // If text[i] == c, we have found another // opening bracket, therefore we will need // two closing brackets to complete the // match. char x = text[offset]; if (x == '{') { count++; } // If text[i] == cprime, we have found an // closing bracket, so we return i if // --count == 0 else if (x == '}') { if (--count == 0) { return offset; } } } // Nothing found return -1; } private static ParsedStyleResult parseStyleAnnotation(char[] text, int start, StyledLabelBuilder builder) { ParsedStyleResult result = new ParsedStyleResult(); return parseStyleAnnotation(text, start, builder, result); } private static ParsedStyleResult parseStyleAnnotation(char[] text, int findIndex, StyledLabelBuilder builder, ParsedStyleResult result) { int i = findIndex; char[] importantChars = {',', '}'}; boolean endOfTag = false; while (i < text.length && !endOfTag) { findIndex = findNextOf(text, importantChars, i); String style; if (findIndex == -1 || text[findIndex] == '}') { endOfTag = true; } style = createTrimmedString(text, i, findIndex == -1 ? text.length - 1 : findIndex - 1); // start with colors first - they're easiest to guess int colonIndex = style.indexOf(':'); if (colonIndex != -1) { String color = style.substring(colonIndex + 1); // the (r,g,b)-construct allows "," thus we'll have to handle it here! if (color.length() > 1) { if (color.charAt(0) == '(') { findIndex = findNext(text, ')', i + colonIndex + 1); style = createTrimmedString(text, i, findIndex + 1); color = style.substring(colonIndex + 1); // we need to do some specific checking here if (text[findIndex + 1] == '}') { endOfTag = true; } // in any case: the cursor needs to be moved forward by one findIndex++; } if (style.charAt(0) == 'f') { result.fontColor = toColor(color); } else if (style.charAt(0) == 'b') { result.backgroundColor = toColor(color); } else { result.lineColor = toColor(color); } } } else { // no color, now it's getting though if (style.equals("plain") || style.equals("p")) { result.fontStyle = Font.PLAIN; } else if (style.equals("bold") || style.equals("b")) { result.fontStyle = Font.BOLD; } else if (style.equals("italic") || style.equals("i")) { result.fontStyle = Font.ITALIC; } else if (style.equals("bolditalic") || style.equals("bi")) { result.fontStyle = Font.ITALIC + Font.BOLD; } else if (style.equals("strike") || style.equals("s")) { result.additionalStyle |= StyleRange.STYLE_STRIKE_THROUGH; } else if (style.equals("doublestrike") || style.equals("ds")) { result.additionalStyle |= StyleRange.STYLE_DOUBLE_STRIKE_THROUGH; } else if (style.equals("waved") || style.equals("w")) { result.additionalStyle |= StyleRange.STYLE_WAVED; } else if (style.equals("underlined") || style.equals("u")) { result.additionalStyle |= StyleRange.STYLE_UNDERLINED; } else if (style.equals("dotted") || style.equals("d")) { result.additionalStyle |= StyleRange.STYLE_DOTTED; } else if (style.equals("superscript") || style.equals("sp")) { result.additionalStyle |= StyleRange.STYLE_SUPERSCRIPT; } else if (style.equals("subscript") || style.equals("sb")) { result.additionalStyle |= StyleRange.STYLE_SUBSCRIPT; } else if (builder != null && builder.styles.containsKey(style)) { StyleRange range = (StyleRange) builder.styles.get(style); result.fontStyle = range.getFontStyle(); result.fontColor = range.getFontColor(); result.backgroundColor = range.getBackgroundColor(); result.additionalStyle = range.getAdditionalStyle(); result.lineColor = range.getLineColor(); } else if (style.length() > 0) { // System.err.println("Unknown style '" + style + "'"); } } i = findIndex + 1; } result.endOffset = i - 1; // done, return return result; } /** * Can be: (255, 0, 0) #FF0000 #F00 0xFF0000 red */ private static Color toColor(String str) { switch (str.charAt(0)) { case '(': int red, green, blue; int index; red = nextColorInt(str, 1); index = str.indexOf(','); green = nextColorInt(str, index + 1); index = str.indexOf(',', index + 1); blue = nextColorInt(str, index + 1); return new Color(red, green, blue); case '#': // Shorthand? if (str.length() == 4) { return new Color( getShorthandValue(str.charAt(1)), getShorthandValue(str.charAt(2)), getShorthandValue(str.charAt(3)) ); } else { return new Color(Integer.parseInt(str.substring(1), 16)); } case '0': return new Color(Integer.parseInt(str.substring(2), 16)); default: return (Color) colorNamesMap.get(str); } } private static int nextColorInt(String str, int index) { // start with adjusting the start index while (index < str.length()) { char c = str.charAt(index); // a digit? if ('0' <= c && c <= '9') { break; } else { index++; } } // that's only the maximum limit! int colorLength = index; for (; colorLength < index + 3; colorLength++) { char c = str.charAt(colorLength); // not a digit? if (c < '0' || '9' < c) { break; } } return Integer.parseInt(str.substring(index, colorLength)); } private static int getShorthandValue(char c) { c = Character.toUpperCase(c); if ('A' <= c && c <= 'F') { return colorShorthandTable[c - 'A' + 10]; } return colorShorthandTable[c - '0']; } private static int[] colorShorthandTable = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }; private static Map colorNamesMap; static { colorNamesMap = new TreeMap(); colorNamesMap.put("white", new Color(0xFFFFFF)); colorNamesMap.put("lightGray", new Color(0xC0C0C0)); colorNamesMap.put("gray", new Color(0x808080)); colorNamesMap.put("darkGray", new Color(0x404040)); colorNamesMap.put("black", new Color(0x000000)); colorNamesMap.put("red", new Color(0xFF0000)); colorNamesMap.put("pink", new Color(0xFFAFAF)); colorNamesMap.put("orange", new Color(0xFFC800)); colorNamesMap.put("yellow", new Color(0xFFFF00)); colorNamesMap.put("green", new Color(0x00FF00)); colorNamesMap.put("magenta", new Color(0xFF00FF)); colorNamesMap.put("cyan", new Color(0x00FFFF)); colorNamesMap.put("blue", new Color(0x0000FF)); } public static Map getColorNamesMap() { return colorNamesMap; } private static String createTrimmedString(char[] text, int start, int end) { for (; (text[start] == ' ' || text[start] == '\t') && start < text.length; start++) ; for (; (text[end] == ' ' || text[end] == '\t') && start < end; end--) ; // need to remove escape chars if (end >= start) { StringBuffer buffer = new StringBuffer(end - start); boolean escaped = false; for (int i = start; i <= end; i++) { if (text[i] == '\\' && !escaped) { escaped = true; } else { buffer.append(text[i]); if (escaped) { escaped = false; } } } return buffer.toString(); } else { return ""; } } private static int findNextOf(char[] text, char[] chars, int start) { boolean escaped = false; for (int i = start; i < text.length; i++) { if (escaped) { escaped = false; continue; } if (text[i] == '\\') { escaped = true; } else { for (char c : chars) { if (text[i] == c) { return i; } } } } return -1; } private static int findNext(char[] text, char c, int start) { boolean escaped = false; for (int i = start; i < text.length; i++) { if (escaped) { escaped = false; continue; } if (text[i] == '\\') { escaped = true; } else if (text[i] == c) { return i; } } return -1; } private static class ParsedStyleResult { String text; int endOffset; int fontStyle = Font.PLAIN; Color fontColor = null, lineColor = null, backgroundColor = null; int additionalStyle = 0; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy