/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is NetBeans. The Initial Developer of the Original
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
* Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.editor;
import javax.swing.*;
import java.awt.*;
import java.util.HashMap;
/**
* Immutable class that stores font and foreground and background colors.
* The coloring can be applied to either the drawing context, component
* or some other coloring. Applying first checks whether each of the font
* and the colors is non-null. If it's null the particular thing is left unchanged.
* For example to change the background color only the Coloring must
* be created with the font and foreground color being null.
* It's also possible to use a more advanced way to apply the coloring
* by using the modes. There are two modes - font-mode and color-mode.
* The deafult font-mode simply overwrites the whole font. The font-mode
* constants allow to change the underlying font by only changing some
* of the font-name, style or size. The <tt>FONT_MODE_APPLY_STYLE</tt>
* for example will only apply the style of the coloring's font.
* The "rest" of the coloring's font (name and size in this case)
* is not used so there can be any valid values ("Monospaced" and 12 for example).
* The colors can use the alpha value.
* It's also possible to set the color of the underline line
* or the color of the strikethrough line.
*
* Generally the editor uses two sets of the colorings to colorize the text.
* They are component-set and printing-set. The component-set is used
* for the editor component while the printing-set is used solely
* for colorizing the printed text.
*
*
* @author Miloslav Metelka
* @version 1.00
*/
public final class Coloring implements java.io.Serializable {
/** Apply only the name from the font. This flag can be combined with
* other FONT_MODE flags. When using solely this constant
* the "rest" of the coloring's font (style and size in this case)
* is unused so there can be any valid values used to create the font
* (Font.PLAIN and 12 for example).
*/
public static final int FONT_MODE_APPLY_NAME = 1;
/** Apply only the style from the font. This flag can be combined with
* other FONT_MODE flags. When using solely this constant
* the "rest" of the coloring's font (name and size in this case)
* is unused so there can be any valid values used
* ("Monospaced" and 12 for example).
*/
public static final int FONT_MODE_APPLY_STYLE = 2;
/** Apply only the size from the font. This flag can be combined with
* other FONT_MODE flags. When using solely this constant
* the "rest" of the coloring's font (name and style in this case)
* is unused so there can be any valid values used to create the font
* ("Monospaced" and Font.PLAIN for example).
*/
public static final int FONT_MODE_APPLY_SIZE = 4;
/** Replace the underlying font by the coloring's font.
* This value is a binary combination of
* FONT_MODE_APPLY_NAME, FONT_MODE_APPLY_STYLE, FONT_MODE_APPLY_SIZE.
*/
public static final int FONT_MODE_DEFAULT
= FONT_MODE_APPLY_NAME | FONT_MODE_APPLY_STYLE | FONT_MODE_APPLY_SIZE;
/** Font */
private Font font;
/** Font mode */
private int fontMode;
/** Foreground color */
private Color foreColor;
/** Background color */
private Color backColor;
/** Underline line color */
private Color underlineColor;
/** Wave underline line color */
private Color waveUnderlineColor;
/** Strikethrough line color */
private Color strikeThroughColor;
/** Cache holding the [original-font, derived-font] pairs
* and also original [fore-color, derived-fore-color] pairs.
* This helps to avoid the repetitive computations of the
* derived font and foreground-color (the backround-color is
* handled through different cache) and it also avoids the repetitive
* creations of the derived font and color objects.
*/
private transient HashMap fontAndForeColorCache;
/** Cache holding the [back-color, derived-back-color] pairs */
private transient HashMap backColorCache;
static final long serialVersionUID =-1382649127124476675L;
/** Construct empty coloring */
public Coloring() {
this(null, null, null);
}
/** Construct new coloring */
public Coloring(Font font, Color foreColor, Color backColor) {
this(font, FONT_MODE_DEFAULT, foreColor, backColor, null, null);
}
/** Construct new coloring */
public Coloring(Font font, int fontMode, Color foreColor, Color backColor) {
this(font, fontMode, foreColor, backColor, null, null);
}
/** Construct new coloring */
public Coloring(Font font, int fontMode, Color foreColor,
Color backColor, Color underlineColor, Color strikeThroughColor) {
this(font, fontMode, foreColor, backColor, underlineColor, strikeThroughColor, null);
}
/** Construct new coloring */
public Coloring(Font font, int fontMode, Color foreColor,
Color backColor, Color underlineColor, Color strikeThroughColor, Color waveUnderlineColor) {
font = (fontMode != 0) ? font : null;
fontMode = (font != null) ? fontMode : FONT_MODE_DEFAULT;
this.font = font;
this.fontMode = fontMode;
this.foreColor = foreColor;
this.backColor = backColor;
this.underlineColor = underlineColor;
this.strikeThroughColor = strikeThroughColor;
this.waveUnderlineColor = waveUnderlineColor;
checkCaches();
}
private void checkCaches() {
// Possibly create the caches
boolean createFontCache = (font != null && fontMode != 0 && fontMode != FONT_MODE_DEFAULT);
boolean createForeColorCache = (foreColor != null && hasAlpha(foreColor));
if (createFontCache || createForeColorCache) {
fontAndForeColorCache = new HashMap(
(createFontCache && createForeColorCache) ? 47 : 23);
}
if (backColor != null && hasAlpha(backColor)) {
backColorCache = new HashMap(23);
}
}
/** Whether the color has non-default alpha. */
private boolean hasAlpha(Color c) {
return ((c.getRGB() & 0xFF000000) != 0xFF000000);
}
/** Getter for font */
public Font getFont() {
return font;
}
/** Getter for font-mode */
public int getFontMode() {
return fontMode;
}
/** Getter for foreground color */
public Color getForeColor() {
return foreColor;
}
/** Getter for background color */
public Color getBackColor() {
return backColor;
}
/** Getter for underline line color */
public Color getUnderlineColor() {
return underlineColor;
}
/** Getter for wave underline line color */
public Color getWaveUnderlineColor() {
return waveUnderlineColor;
}
/** Getter for strikethrough line color */
public Color getStrikeThroughColor() {
return strikeThroughColor;
}
/** Modify the given font according to the font-mode */
private Font modifyFont(Font f) {
return new Font(
((fontMode & FONT_MODE_APPLY_NAME) != 0) ? font.getName() : f.getName(),
((fontMode & FONT_MODE_APPLY_STYLE) != 0) ? font.getStyle() : f.getStyle(),
((fontMode & FONT_MODE_APPLY_SIZE) != 0) ? font.getSize() : f.getSize()
);
}
private Color modifyForeColor(Color underForeColor) {
int alpha = foreColor.getAlpha(); // alpha 0 - 255
int fcRGB = foreColor.getRGB();
int underRGB = underForeColor.getRGB();
int rgb = (((foreColor.getRed() * alpha
+ underForeColor.getRed() * (255 - alpha)) / 255) & 0x000000FF) << 16;
rgb += ((((fcRGB & 0x0000FF00) * alpha
+ (underRGB & 0x0000FF00) * (255 - alpha)) / 255) & 0x0000FF00) // green
+ ((((fcRGB & 0x000000FF) * alpha
+ (underRGB & 0x000000FF) * (255 - alpha)) / 255) & 0x000000FF);// blue
return new Color(rgb, false);
}
private Color modifyBackColor(Color underBackColor) {
int alpha = backColor.getAlpha(); // alpha 0 - 255
int bcRGB = backColor.getRGB();
int underRGB = underBackColor.getRGB();
int rgb = (((backColor.getRed() * alpha
+ underBackColor.getRed() * (255 - alpha)) / 255) & 0x000000FF) << 16;
rgb += ((((bcRGB & 0x0000FF00) * alpha
+ (underRGB & 0x0000FF00) * (255 - alpha)) / 255) & 0x0000FF00) // green
+ ((((bcRGB & 0x000000FF) * alpha
+ (underRGB & 0x000000FF) * (255 - alpha)) / 255) & 0x000000FF);// blue
return new Color(rgb, false);
}
/** Apply this coloring to draw context. */
public void apply(DrawContext ctx) {
// Possibly change font
if (font != null) {
if (fontMode == FONT_MODE_DEFAULT) {
ctx.setFont(font);
} else { // non-default font-mode
Font origFont = ctx.getFont();
if (origFont != null) {
synchronized (fontAndForeColorCache) {
Font f = (Font)fontAndForeColorCache.get(origFont);
if (f == null) {
f = modifyFont(origFont);
fontAndForeColorCache.put(origFont, f);
}
ctx.setFont(f);
}
}
}
}
// Possibly change fore-color
if (foreColor != null) {
if (!hasAlpha(foreColor)) { // doesn't have an alpha
ctx.setForeColor(foreColor);
} else { // has alpha
Color origForeColor = ctx.getForeColor();
if (origForeColor != null) {
synchronized (fontAndForeColorCache) {
Color fc = (Color)fontAndForeColorCache.get(origForeColor);
if (fc == null) {
fc = modifyForeColor(origForeColor);
fontAndForeColorCache.put(origForeColor, fc);
}
ctx.setForeColor(fc);
}
}
}
}
// Possibly change back-color
if (backColor != null) {
if (!hasAlpha(backColor)) {
ctx.setBackColor(backColor);
} else { // non-default back color-mode
Color origBackColor = ctx.getBackColor();
if (origBackColor != null) {
synchronized (backColorCache) {
Color bc = (Color)backColorCache.get(origBackColor);
if (bc == null) {
bc = modifyBackColor(origBackColor);
backColorCache.put(origBackColor, bc);
}
ctx.setBackColor(bc);
}
}
}
}
if (underlineColor != null) {
ctx.setUnderlineColor(underlineColor);
}
if (waveUnderlineColor != null) {
ctx.setWaveUnderlineColor(waveUnderlineColor);
}
if (strikeThroughColor != null) {
ctx.setStrikeThroughColor(strikeThroughColor);
}
}
/** Apply this coloring to component colors/font.
* The underline and strikeThrough line colors have no effect here.
*/
public void apply(JComponent c) {
// Possibly change font
if (font != null) {
if (fontMode == FONT_MODE_DEFAULT) {
c.setFont(font);
} else { // non-default font-mode
Font origFont = c.getFont();
if (origFont != null) {
synchronized (fontAndForeColorCache) {
Font f = (Font)fontAndForeColorCache.get(origFont);
if (f == null) {
f = modifyFont(origFont);
fontAndForeColorCache.put(origFont, f);
}
c.setFont(f);
}
}
}
}
// Possibly change fore-color
if (foreColor != null) {
if (!hasAlpha(foreColor)) {
c.setForeground(foreColor);
} else { // non-default fore color-mode
Color origForeColor = c.getForeground();
if (origForeColor != null) {
synchronized (fontAndForeColorCache) {
Color fc = (Color)fontAndForeColorCache.get(origForeColor);
if (fc == null) {
fc = modifyForeColor(origForeColor);
fontAndForeColorCache.put(origForeColor, fc);
}
c.setForeground(fc);
}
}
}
}
// Possibly change back-color
if (backColor != null) {
if (!hasAlpha(backColor)) {
c.setBackground(backColor);
} else { // non-default back color-mode
Color origBackColor = c.getBackground();
if (origBackColor != null) {
synchronized (backColorCache) {
Color bc = (Color)backColorCache.get(origBackColor);
if (bc == null) {
bc = modifyBackColor(origBackColor);
backColorCache.put(origBackColor, bc);
}
c.setBackground(bc);
}
}
}
}
}
/** Apply this coloring to some other coloring c and return
* the resulting coloring.
* @param c coloring to which this coloring will be applied.
* If it's null then this coloring will be returned as result.
*/
public Coloring apply(Coloring c) {
if (c == null) { // if c is null, return this coloring as result
return this;
}
Font newFont = c.font;
int newFontMode = FONT_MODE_DEFAULT;
Color newForeColor = c.foreColor;
Color newBackColor = c.backColor;
Color newUnderlineColor = c.underlineColor;
Color newWaveUnderlineColor = c.waveUnderlineColor;
Color newStrikeThroughColor = c.strikeThroughColor;
// Possibly change font
if (font != null) {
if (fontMode == FONT_MODE_DEFAULT) {
newFont = font;
} else { // non-default font-mode
if (newFont != null) {
synchronized (fontAndForeColorCache) {
Font f = (Font)fontAndForeColorCache.get(newFont);
if (f == null) {
f = modifyFont(newFont);
fontAndForeColorCache.put(newFont, f);
}
newFont = f;
}
}
}
} else { // fixing of #17669
// inherit font mode too
newFontMode = c.fontMode;
}
// Possibly change fore-color
if (foreColor != null) {
if (!hasAlpha(foreColor)) {
newForeColor = foreColor;
} else { // non-default fore color-mode
if (newForeColor != null) {
synchronized (fontAndForeColorCache) {
Color fc = (Color)fontAndForeColorCache.get(newForeColor);
if (fc == null) {
fc = modifyForeColor(newForeColor);
fontAndForeColorCache.put(newForeColor, fc);
}
newForeColor = fc;
}
}
}
}
// Possibly change back-color
if (backColor != null) {
if (!hasAlpha(backColor)) {
newBackColor = backColor;
} else { // non-default back color-mode
newBackColor = backColor;
if (newBackColor != null) {
synchronized (backColorCache) {
Color bc = (Color)backColorCache.get(newBackColor);
if (bc == null) {
bc = modifyBackColor(newBackColor);
backColorCache.put(newBackColor, bc);
}
newBackColor = bc;
}
}
}
}
if (underlineColor != null) {
newUnderlineColor = underlineColor;
}
if (waveUnderlineColor != null) {
newWaveUnderlineColor = waveUnderlineColor;
}
if (strikeThroughColor != null) {
newStrikeThroughColor = strikeThroughColor;
}
if (c.fontMode != FONT_MODE_DEFAULT
|| newFont != c.font // currently only equality
|| newForeColor != c.foreColor // currently only equality
|| newBackColor != c.backColor // currently only equality
|| newUnderlineColor != c.underlineColor // currently only equality
|| newWaveUnderlineColor != c.waveUnderlineColor // currently only equality
|| newStrikeThroughColor != c.strikeThroughColor // currently only equality
) {
return new Coloring(newFont, newFontMode,
newForeColor, newBackColor,
newUnderlineColor, newStrikeThroughColor, newWaveUnderlineColor
);
} else {
return c; // return original coloring
}
}
/** All font, foreColor and backColor are the same. */
public boolean equals(Object o) {
if (o instanceof Coloring) {
Coloring c = (Coloring)o;
return ((font == null && c.font == null) || (font != null && font.equals(c.font)))
&& (fontMode == c.fontMode)
&& ((foreColor == null && c.foreColor == null)
|| (foreColor != null && foreColor.equals(c.foreColor)))
&& ((backColor == null && c.backColor == null)
|| (backColor != null && backColor.equals(c.backColor)))
&& ((underlineColor == null && c.underlineColor == null)
|| (underlineColor != null && underlineColor.equals(c.underlineColor)))
&& ((waveUnderlineColor == null && c.waveUnderlineColor == null)
|| (waveUnderlineColor != null && waveUnderlineColor.equals(c.waveUnderlineColor)))
&& ((strikeThroughColor == null && c.strikeThroughColor == null)
|| (strikeThroughColor != null
&& strikeThroughColor.equals(c.strikeThroughColor)));
}
return false;
}
public int hashCode() {
return font.hashCode() ^ foreColor.hashCode() ^ backColor.hashCode();
}
/** Derive a new coloring by changing the font and leaving
* the rest of the coloring (including the font-mode) unchanged.
*/
public static Coloring changeFont(Coloring c, Font newFont) {
return changeFont(c, newFont, c.getFontMode());
}
/** Derive a new coloring by changing the font and font-mode and leaving
* the rest of the coloring unchanged.
*/
public static Coloring changeFont(Coloring c, Font newFont, int newFontMode) {
if ((newFont == null && c.font == null)
|| (newFont != null && newFont.equals(c.font)
&& c.fontMode == newFontMode)
) {
return c;
}
return new Coloring(newFont, c.foreColor, c.backColor);
}
/** Derive a new coloring by changing
* the foreground-color and its color-mode
* and leaving the rest of the coloring unchanged.
*/
public static Coloring changeForeColor(Coloring c, Color newForeColor) {
if ((newForeColor == null && c.foreColor == null)
|| (newForeColor != null && newForeColor.equals(c.foreColor))
) {
return c;
}
return new Coloring(c.font, newForeColor, c.backColor);
}
/** Derive a new coloring by changing
* the background-color and its color-mode
* and leaving the rest of the coloring unchanged.
*/
public static Coloring changeBackColor(Coloring c, Color newBackColor) {
if ((newBackColor == null && c.backColor == null)
|| (newBackColor != null && newBackColor.equals(c.backColor))
) {
return c;
}
return new Coloring(c.font, c.foreColor, newBackColor);
}
private void readObject(java.io.ObjectInputStream ois)
throws java.io.IOException, ClassNotFoundException {
ois.defaultReadObject();
if (fontMode == 0) {
fontMode = FONT_MODE_DEFAULT;
}
checkCaches();
}
public String toString() {
return "font=" + font + ", fontMode=" + fontMode // NOI18N
+ ", foreColor=" + foreColor // NOI18N
+ ", backColor=" + backColor // NOI18N
+ ", underlineColor=" + underlineColor // NOI18N
+ ", waveUnderlineColor=" + waveUnderlineColor // NOI18N
+ ", strikeThroughColor=" + strikeThroughColor; // NOI18N
}
}