001: /*
002: * Sun Public License Notice
003: *
004: * The contents of this file are subject to the Sun Public License
005: * Version 1.0 (the "License"). You may not use this file except in
006: * compliance with the License. A copy of the License is available at
007: * http://www.sun.com/
008: *
009: * The Original Code is NetBeans. The Initial Developer of the Original
010: * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
011: * Microsystems, Inc. All Rights Reserved.
012: */
013:
014: package org.netbeans.editor;
015:
016: import java.awt.Color;
017: import java.awt.Font;
018: import java.util.HashMap;
019:
020: import javax.swing.JComponent;
021:
022: /**
023: * Immutable class that stores font and foreground and background colors. The
024: * coloring can be applied to either the drawing context, component or some
025: * other coloring. Applying first checks whether each of the font and the colors
026: * is non-null. If it's null the particular thing is left unchanged. For example
027: * to change the background color only the Coloring must be created with the
028: * font and foreground color being null. It's also possible to use a more
029: * advanced way to apply the coloring by using the modes. There are two modes -
030: * font-mode and color-mode. The deafult font-mode simply overwrites the whole
031: * font. The font-mode constants allow to change the underlying font by only
032: * changing some of the font-name, style or size. The
033: * <tt>FONT_MODE_APPLY_STYLE</tt> for example will only apply the style of the
034: * coloring's font. The "rest" of the coloring's font (name and size in this
035: * case) is not used so there can be any valid values ("Monospaced" and 12 for
036: * example). The colors can use the alpha value. It's also possible to set the
037: * color of the underline line or the color of the strikethrough line.
038: *
039: * Generally the editor uses two sets of the colorings to colorize the text.
040: * They are component-set and printing-set. The component-set is used for the
041: * editor component while the printing-set is used solely for colorizing the
042: * printed text.
043: *
044: *
045: * @author Miloslav Metelka
046: * @version 1.00
047: */
048:
049: public final class Coloring implements java.io.Serializable {
050:
051: /**
052: * Apply only the name from the font. This flag can be combined with other
053: * FONT_MODE flags. When using solely this constant the "rest" of the
054: * coloring's font (style and size in this case) is unused so there can be
055: * any valid values used to create the font (Font.PLAIN and 12 for example).
056: */
057: public static final int FONT_MODE_APPLY_NAME = 1;
058:
059: /**
060: * Apply only the style from the font. This flag can be combined with other
061: * FONT_MODE flags. When using solely this constant the "rest" of the
062: * coloring's font (name and size in this case) is unused so there can be
063: * any valid values used ("Monospaced" and 12 for example).
064: */
065: public static final int FONT_MODE_APPLY_STYLE = 2;
066:
067: /**
068: * Apply only the size from the font. This flag can be combined with other
069: * FONT_MODE flags. When using solely this constant the "rest" of the
070: * coloring's font (name and style in this case) is unused so there can be
071: * any valid values used to create the font ("Monospaced" and Font.PLAIN for
072: * example).
073: */
074: public static final int FONT_MODE_APPLY_SIZE = 4;
075:
076: /**
077: * Replace the underlying font by the coloring's font. This value is a
078: * binary combination of FONT_MODE_APPLY_NAME, FONT_MODE_APPLY_STYLE,
079: * FONT_MODE_APPLY_SIZE.
080: */
081: public static final int FONT_MODE_DEFAULT = FONT_MODE_APPLY_NAME
082: | FONT_MODE_APPLY_STYLE | FONT_MODE_APPLY_SIZE;
083:
084: /** Font */
085: private Font font;
086:
087: /** Font mode */
088: private int fontMode;
089:
090: /** Foreground color */
091: private Color foreColor;
092:
093: /** Background color */
094: private Color backColor;
095:
096: /** Underline line color */
097: private Color underlineColor;
098:
099: /** Strikethrough line color */
100: private Color strikeThroughColor;
101:
102: /**
103: * Cache holding the [original-font, derived-font] pairs and also original
104: * [fore-color, derived-fore-color] pairs. This helps to avoid the
105: * repetitive computations of the derived font and foreground-color (the
106: * backround-color is handled through different cache) and it also avoids
107: * the repetitive creations of the derived font and color objects.
108: */
109: private transient HashMap fontAndForeColorCache;
110:
111: /** Cache holding the [back-color, derived-back-color] pairs */
112: private transient HashMap backColorCache;
113:
114: static final long serialVersionUID = -1382649127124476675L;
115:
116: /** Construct empty coloring */
117: public Coloring() {
118: this (null, null, null);
119: }
120:
121: /** Construct new coloring */
122: public Coloring(Font font, Color foreColor, Color backColor) {
123: this (font, FONT_MODE_DEFAULT, foreColor, backColor, null, null);
124: }
125:
126: /** Construct new coloring */
127: public Coloring(Font font, int fontMode, Color foreColor,
128: Color backColor) {
129: this (font, fontMode, foreColor, backColor, null, null);
130: }
131:
132: /** Construct new coloring */
133: public Coloring(Font font, int fontMode, Color foreColor,
134: Color backColor, Color underlineColor,
135: Color strikeThroughColor) {
136: font = (fontMode != 0) ? font : null;
137: fontMode = (font != null) ? fontMode : FONT_MODE_DEFAULT;
138:
139: this .font = font;
140: this .fontMode = fontMode;
141:
142: this .foreColor = foreColor;
143: this .backColor = backColor;
144:
145: this .underlineColor = underlineColor;
146: this .strikeThroughColor = strikeThroughColor;
147:
148: checkCaches();
149: }
150:
151: private void checkCaches() {
152: // Possibly create the caches
153: boolean createFontCache = (font != null && fontMode != 0 && fontMode != FONT_MODE_DEFAULT);
154: boolean createForeColorCache = (foreColor != null && hasAlpha(foreColor));
155: if (createFontCache || createForeColorCache) {
156: fontAndForeColorCache = new HashMap(
157: (createFontCache && createForeColorCache) ? 47 : 23);
158: }
159:
160: if (backColor != null && hasAlpha(backColor)) {
161: backColorCache = new HashMap(23);
162: }
163: }
164:
165: /** Whether the color has non-default alpha. */
166: private boolean hasAlpha(Color c) {
167: return ((c.getRGB() & 0xFF000000) != 0xFF000000);
168: }
169:
170: /** Getter for font */
171: public Font getFont() {
172: return font;
173: }
174:
175: /** Getter for font-mode */
176: public int getFontMode() {
177: return fontMode;
178: }
179:
180: /** Getter for foreground color */
181: public Color getForeColor() {
182: return foreColor;
183: }
184:
185: /** Getter for background color */
186: public Color getBackColor() {
187: return backColor;
188: }
189:
190: /** Getter for underline line color */
191: public Color getUnderlineColor() {
192: return underlineColor;
193: }
194:
195: /** Getter for strikethrough line color */
196: public Color getStrikeThroughColor() {
197: return strikeThroughColor;
198: }
199:
200: /** Modify the given font according to the font-mode */
201: private Font modifyFont(Font f) {
202: return new Font(((fontMode & FONT_MODE_APPLY_NAME) != 0) ? font
203: .getName() : f.getName(),
204: ((fontMode & FONT_MODE_APPLY_STYLE) != 0) ? font
205: .getStyle() : f.getStyle(),
206: ((fontMode & FONT_MODE_APPLY_SIZE) != 0) ? font
207: .getSize() : f.getSize());
208: }
209:
210: private Color modifyForeColor(Color underForeColor) {
211: int alpha = foreColor.getAlpha(); // alpha 0 - 255
212: int fcRGB = foreColor.getRGB();
213: int underRGB = underForeColor.getRGB();
214:
215: int rgb = (((foreColor.getRed() * alpha + underForeColor
216: .getRed()
217: * (255 - alpha)) / 255) & 0x000000FF) << 16;
218:
219: rgb += ((((fcRGB & 0x0000FF00) * alpha + (underRGB & 0x0000FF00)
220: * (255 - alpha)) / 255) & 0x0000FF00) // green
221: + ((((fcRGB & 0x000000FF) * alpha + (underRGB & 0x000000FF)
222: * (255 - alpha)) / 255) & 0x000000FF);// blue
223:
224: return new Color(rgb, false);
225: }
226:
227: private Color modifyBackColor(Color underBackColor) {
228: int alpha = backColor.getAlpha(); // alpha 0 - 255
229: int bcRGB = backColor.getRGB();
230: int underRGB = underBackColor.getRGB();
231: int rgb = (((backColor.getRed() * alpha + underBackColor
232: .getRed()
233: * (255 - alpha)) / 255) & 0x000000FF) << 16;
234:
235: rgb += ((((bcRGB & 0x0000FF00) * alpha + (underRGB & 0x0000FF00)
236: * (255 - alpha)) / 255) & 0x0000FF00) // green
237: + ((((bcRGB & 0x000000FF) * alpha + (underRGB & 0x000000FF)
238: * (255 - alpha)) / 255) & 0x000000FF);// blue
239:
240: return new Color(rgb, false);
241: }
242:
243: /** Apply this coloring to draw context. */
244: public void apply(DrawContext ctx) {
245: // Possibly change font
246: if (font != null) {
247: if (fontMode == FONT_MODE_DEFAULT) {
248: ctx.setFont(font);
249:
250: } else { // non-default font-mode
251: Font origFont = ctx.getFont();
252: if (origFont != null) {
253: synchronized (fontAndForeColorCache) {
254: Font f = (Font) fontAndForeColorCache
255: .get(origFont);
256: if (f == null) {
257: f = modifyFont(origFont);
258: fontAndForeColorCache.put(origFont, f);
259: }
260: ctx.setFont(f);
261: }
262: }
263: }
264: }
265:
266: // Possibly change fore-color
267: if (foreColor != null) {
268: if (!hasAlpha(foreColor)) { // doesn't have an alpha
269: ctx.setForeColor(foreColor);
270:
271: } else { // has alpha
272: Color origForeColor = ctx.getForeColor();
273: if (origForeColor != null) {
274: synchronized (fontAndForeColorCache) {
275: Color fc = (Color) fontAndForeColorCache
276: .get(origForeColor);
277: if (fc == null) {
278: fc = modifyForeColor(origForeColor);
279: fontAndForeColorCache
280: .put(origForeColor, fc);
281: }
282: ctx.setForeColor(fc);
283: }
284: }
285: }
286: }
287:
288: // Possibly change back-color
289: if (backColor != null) {
290: if (!hasAlpha(backColor)) {
291: ctx.setBackColor(backColor);
292:
293: } else { // non-default back color-mode
294: Color origBackColor = ctx.getBackColor();
295: if (origBackColor != null) {
296: synchronized (backColorCache) {
297: Color bc = (Color) backColorCache
298: .get(origBackColor);
299: if (bc == null) {
300: bc = modifyBackColor(origBackColor);
301: backColorCache.put(origBackColor, bc);
302: }
303: ctx.setBackColor(bc);
304: }
305: }
306: }
307: }
308:
309: if (underlineColor != null) {
310: ctx.setUnderlineColor(underlineColor);
311: }
312:
313: if (strikeThroughColor != null) {
314: ctx.setStrikeThroughColor(strikeThroughColor);
315: }
316: }
317:
318: /**
319: * Apply this coloring to component colors/font. The underline and
320: * strikeThrough line colors have no effect here.
321: */
322: public void apply(JComponent c) {
323: // Possibly change font
324: if (font != null) {
325: if (fontMode == FONT_MODE_DEFAULT) {
326: c.setFont(font);
327:
328: } else { // non-default font-mode
329: Font origFont = c.getFont();
330: if (origFont != null) {
331: synchronized (fontAndForeColorCache) {
332: Font f = (Font) fontAndForeColorCache
333: .get(origFont);
334: if (f == null) {
335: f = modifyFont(origFont);
336: fontAndForeColorCache.put(origFont, f);
337: }
338: c.setFont(f);
339: }
340: }
341: }
342: }
343:
344: // Possibly change fore-color
345: if (foreColor != null) {
346: if (!hasAlpha(foreColor)) {
347: c.setForeground(foreColor);
348:
349: } else { // non-default fore color-mode
350: Color origForeColor = c.getForeground();
351: if (origForeColor != null) {
352: synchronized (fontAndForeColorCache) {
353: Color fc = (Color) fontAndForeColorCache
354: .get(origForeColor);
355: if (fc == null) {
356: fc = modifyForeColor(origForeColor);
357: fontAndForeColorCache
358: .put(origForeColor, fc);
359: }
360: c.setForeground(fc);
361: }
362: }
363: }
364: }
365:
366: // Possibly change back-color
367: if (backColor != null) {
368: if (!hasAlpha(backColor)) {
369: c.setBackground(backColor);
370:
371: } else { // non-default back color-mode
372: Color origBackColor = c.getBackground();
373: if (origBackColor != null) {
374: synchronized (backColorCache) {
375: Color bc = (Color) backColorCache
376: .get(origBackColor);
377: if (bc == null) {
378: bc = modifyBackColor(origBackColor);
379: backColorCache.put(origBackColor, bc);
380: }
381: c.setBackground(bc);
382: }
383: }
384: }
385: }
386: }
387:
388: /**
389: * Apply this coloring to some other coloring c and return the resulting
390: * coloring.
391: *
392: * @param c
393: * coloring to which this coloring will be applied. If it's null
394: * then this coloring will be returned as result.
395: */
396: public Coloring apply(Coloring c) {
397: if (c == null) { // if c is null, return this coloring as result
398: return this ;
399: }
400:
401: Font newFont = c.font;
402: Color newForeColor = c.foreColor;
403: Color newBackColor = c.backColor;
404: Color newUnderlineColor = c.underlineColor;
405: Color newStrikeThroughColor = c.strikeThroughColor;
406:
407: // Possibly change font
408: if (font != null) {
409: if (fontMode == FONT_MODE_DEFAULT) {
410: newFont = font;
411:
412: } else { // non-default font-mode
413: if (newFont != null) {
414: synchronized (fontAndForeColorCache) {
415: Font f = (Font) fontAndForeColorCache
416: .get(newFont);
417: if (f == null) {
418: f = modifyFont(newFont);
419: fontAndForeColorCache.put(newFont, f);
420: }
421: newFont = f;
422: }
423: }
424: }
425: }
426:
427: // Possibly change fore-color
428: if (foreColor != null) {
429: if (!hasAlpha(foreColor)) {
430: newForeColor = foreColor;
431:
432: } else { // non-default fore color-mode
433: if (newForeColor != null) {
434: synchronized (fontAndForeColorCache) {
435: Color fc = (Color) fontAndForeColorCache
436: .get(newForeColor);
437: if (fc == null) {
438: fc = modifyForeColor(newForeColor);
439: fontAndForeColorCache.put(newForeColor, fc);
440: }
441: newForeColor = fc;
442: }
443: }
444: }
445: }
446:
447: // Possibly change back-color
448: if (backColor != null) {
449: if (!hasAlpha(backColor)) {
450: newBackColor = backColor;
451:
452: } else { // non-default back color-mode
453: newBackColor = backColor;
454: if (newBackColor != null) {
455: synchronized (backColorCache) {
456: Color bc = (Color) backColorCache
457: .get(newBackColor);
458: if (bc == null) {
459: bc = modifyBackColor(newBackColor);
460: backColorCache.put(newBackColor, bc);
461: }
462: newBackColor = bc;
463: }
464: }
465: }
466: }
467:
468: if (underlineColor != null) {
469: newUnderlineColor = underlineColor;
470: }
471:
472: if (strikeThroughColor != null) {
473: newStrikeThroughColor = strikeThroughColor;
474: }
475:
476: if (c.fontMode != FONT_MODE_DEFAULT || newFont != c.font // currently
477: // only
478: // equality
479: || newForeColor != c.foreColor // currently only equality
480: || newBackColor != c.backColor // currently only equality
481: || newUnderlineColor != c.underlineColor // currently only
482: // equality
483: || newStrikeThroughColor != c.strikeThroughColor // currently
484: // only
485: // equality
486: ) {
487: return new Coloring(newFont, FONT_MODE_DEFAULT,
488: newForeColor, newBackColor, newUnderlineColor,
489: newStrikeThroughColor);
490: } else {
491: return c; // return original coloring
492: }
493:
494: }
495:
496: /** All font, foreColor and backColor are the same. */
497: public boolean equals(Object o) {
498: if (o instanceof Coloring) {
499: Coloring c = (Coloring) o;
500: return ((font == null && c.font == null) || (font != null && font
501: .equals(c.font)))
502: && (fontMode == c.fontMode)
503: && ((foreColor == null && c.foreColor == null) || (foreColor != null && foreColor
504: .equals(c.foreColor)))
505: && ((backColor == null && c.backColor == null) || (backColor != null && backColor
506: .equals(c.backColor)))
507: && ((underlineColor == null && c.underlineColor == null) || (underlineColor != null && underlineColor
508: .equals(c.underlineColor)))
509: && ((strikeThroughColor == null && c.strikeThroughColor == null) || (strikeThroughColor != null && strikeThroughColor
510: .equals(c.strikeThroughColor)));
511: }
512: return false;
513: }
514:
515: public int hashCode() {
516: return font.hashCode() ^ foreColor.hashCode()
517: ^ backColor.hashCode();
518: }
519:
520: /**
521: * Derive a new coloring by changing the font and leaving the rest of the
522: * coloring (including the font-mode) unchanged.
523: */
524: public static Coloring changeFont(Coloring c, Font newFont) {
525: return changeFont(c, newFont, c.getFontMode());
526: }
527:
528: /**
529: * Derive a new coloring by changing the font and font-mode and leaving the
530: * rest of the coloring unchanged.
531: */
532: public static Coloring changeFont(Coloring c, Font newFont,
533: int newFontMode) {
534: if ((newFont == null && c.font == null)
535: || (newFont != null && newFont.equals(c.font) && c.fontMode == newFontMode)) {
536: return c;
537: }
538:
539: return new Coloring(newFont, c.foreColor, c.backColor);
540: }
541:
542: /**
543: * Derive a new coloring by changing the foreground-color and its color-mode
544: * and leaving the rest of the coloring unchanged.
545: */
546: public static Coloring changeForeColor(Coloring c,
547: Color newForeColor) {
548: if ((newForeColor == null && c.foreColor == null)
549: || (newForeColor != null && newForeColor
550: .equals(c.foreColor))) {
551: return c;
552: }
553:
554: return new Coloring(c.font, newForeColor, c.backColor);
555: }
556:
557: /**
558: * Derive a new coloring by changing the background-color and its color-mode
559: * and leaving the rest of the coloring unchanged.
560: */
561: public static Coloring changeBackColor(Coloring c,
562: Color newBackColor) {
563: if ((newBackColor == null && c.backColor == null)
564: || (newBackColor != null && newBackColor
565: .equals(c.backColor))) {
566: return c;
567: }
568:
569: return new Coloring(c.font, c.foreColor, newBackColor);
570: }
571:
572: private void readObject(java.io.ObjectInputStream ois)
573: throws java.io.IOException, ClassNotFoundException {
574: ois.defaultReadObject();
575:
576: if (fontMode == 0) {
577: fontMode = FONT_MODE_DEFAULT;
578: }
579:
580: checkCaches();
581: }
582:
583: public String toString() {
584: return "font=" + font + ", fontMode=" + fontMode // NOI18N
585: + ", foreColor=" + foreColor // NOI18N
586: + ", backColor=" + backColor // NOI18N
587: + ", underlineColor=" + underlineColor // NOI18N
588: + ", strikeThroughColor=" + strikeThroughColor; // NOI18N
589: }
590:
591: }
|