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.Component;
017: import java.awt.Font;
018: import java.awt.FontMetrics;
019: import java.awt.Graphics;
020: import java.awt.Graphics2D;
021: import java.awt.font.LineMetrics;
022: import java.util.HashMap;
023:
024: /**
025: * Static cache that holds the font metrics for the fonts. This can generally
026: * speed up drawing if the metrics are not cached directly by the system.
027: *
028: * @author Miloslav Metelka
029: * @version 1.00
030: */
031:
032: public class FontMetricsCache {
033:
034: private static HashMap font2FM = new HashMap();
035:
036: private static HashMap font2Info = new HashMap();
037:
038: /**
039: * Get the font-metrics for the given font.
040: *
041: * @param font
042: * font for which the metrics is being retrieved.
043: * @param c
044: * component that is used to retrieve the metrics in case it's
045: * not yet in the cache.
046: */
047: public static synchronized FontMetrics getFontMetrics(Font f,
048: Component c) {
049: Object fm = font2FM.get(f);
050: if (fm == null) {
051: fm = c.getFontMetrics(f);
052: font2FM.put(f, fm);
053: }
054: return (FontMetrics) fm;
055: }
056:
057: /**
058: * Get the font-metrics for the given font.
059: *
060: * @param font
061: * font for which the metrics is being retrieved.
062: * @param g
063: * graphics that is used to retrieve the metrics in case it's not
064: * yet in the cache.
065: */
066: public static synchronized FontMetrics getFontMetrics(Font f,
067: Graphics g) {
068: Object fm = font2FM.get(f);
069: if (fm == null) {
070: fm = g.getFontMetrics(f);
071: font2FM.put(f, fm);
072: }
073: return (FontMetrics) fm;
074: }
075:
076: /**
077: * Get the info about the space-width and strike-through and underline
078: * constants.
079: *
080: * @param font
081: * font for which the info is being retrieved.
082: */
083: public static synchronized Info getInfo(Font f) {
084: Info info = (Info) font2Info.get(f);
085: if (info == null) {
086: info = new InfoImpl(f);
087: font2Info.put(f, info);
088: }
089: return info;
090: }
091:
092: /**
093: * Clear all the metrics from the cache. It's usually done when any of the
094: * editor ui is being garbage collected to ensure there will be no more
095: * unused metrics.
096: */
097: public static synchronized void clear() {
098: font2FM.clear();
099: font2Info.clear();
100: }
101:
102: public interface Info {
103:
104: /** Returns the width of the space character */
105: public int getSpaceWidth(Graphics g);
106:
107: /** Returns the width of the space character */
108: public int getSpaceWidth(Component c);
109:
110: /**
111: * Returns the position of the strike-through line relative to the
112: * baseline.
113: */
114: public float getStrikethroughOffset(Graphics g);
115:
116: /**
117: * Returns the position of the strike-through line relative to the
118: * baseline.
119: */
120: public float getStrikethroughOffset(Component c);
121:
122: /** Returns the thickness of the strike-through line. */
123: public float getStrikethroughThickness(Graphics g);
124:
125: /** Returns the thickness of the strike-through line. */
126: public float getStrikethroughThickness(Component c);
127:
128: /** Returns the position of the underline relative to the baseline. */
129: public float getUnderlineOffset(Graphics g);
130:
131: /** Returns the position of the underline relative to the baseline. */
132: public float getUnderlineOffset(Component c);
133:
134: /** Returns the thickness of the underline. */
135: public float getUnderlineThickness(Graphics g);
136:
137: /** Returns the thickness of the underline. */
138: public float getUnderlineThickness(Component c);
139:
140: }
141:
142: private static class InfoImpl implements Info {
143:
144: private static final int SW_INITED = 1;
145: private static final int ST_INITED = 2;
146: private static final int UL_INITED = 4;
147:
148: private Font font;
149:
150: private int inited;
151:
152: private int spaceWidth;
153:
154: private float strikethroughOffset;
155:
156: private float strikethroughThickness;
157:
158: private float underlineOffset;
159:
160: private float underlineThickness;
161:
162: InfoImpl(Font font) {
163: this .font = font;
164: }
165:
166: private synchronized void initSpaceWidth(Graphics g, Component c) {
167: FontMetrics fm = (g != null) ? getFontMetrics(font, g)
168: : getFontMetrics(font, c);
169: spaceWidth = fm.stringWidth(" ");
170: if (spaceWidth <= 0) {
171: spaceWidth = fm.stringWidth("A") / 3;
172: }
173: inited |= SW_INITED;
174: }
175:
176: private synchronized void initStrikethrough(Graphics g) {
177: LineMetrics lm = font.getLineMetrics("aAyY",
178: ((Graphics2D) g).getFontRenderContext());
179: strikethroughOffset = lm.getStrikethroughOffset();
180: strikethroughThickness = lm.getStrikethroughThickness();
181: inited |= ST_INITED;
182: }
183:
184: private synchronized void initUnderline(Graphics g) {
185: LineMetrics lm = font.getLineMetrics("aAyY",
186: ((Graphics2D) g).getFontRenderContext());
187: underlineOffset = lm.getUnderlineOffset();
188: underlineThickness = lm.getUnderlineThickness();
189: inited |= UL_INITED;
190: }
191:
192: /** Returns the width of the space character */
193: public int getSpaceWidth(Graphics g) {
194: if ((inited & SW_INITED) == 0) {
195: initSpaceWidth(g, null);
196: }
197:
198: return spaceWidth;
199: }
200:
201: /** Returns the width of the space character */
202: public int getSpaceWidth(Component c) {
203: if ((inited & SW_INITED) == 0) {
204: initSpaceWidth(null, c);
205: }
206:
207: return spaceWidth;
208: }
209:
210: /**
211: * Returns the position of the strike-through line relative to the
212: * baseline.
213: */
214: public float getStrikethroughOffset(Graphics g) {
215: if ((inited & ST_INITED) == 0) {
216: initStrikethrough(g);
217: }
218:
219: return strikethroughOffset;
220: }
221:
222: /**
223: * Returns the position of the strike-through line relative to the
224: * baseline.
225: */
226: public float getStrikethroughOffset(Component c) {
227: if ((inited & ST_INITED) == 0) {
228: initStrikethrough(c.getGraphics());
229: }
230:
231: return strikethroughOffset;
232: }
233:
234: /** Returns the thickness of the strike-through line. */
235: public float getStrikethroughThickness(Graphics g) {
236: if ((inited & ST_INITED) == 0) {
237: initStrikethrough(g);
238: }
239:
240: return strikethroughThickness;
241: }
242:
243: /** Returns the thickness of the strike-through line. */
244: public float getStrikethroughThickness(Component c) {
245: if ((inited & ST_INITED) == 0) {
246: initStrikethrough(c.getGraphics());
247: }
248:
249: return strikethroughThickness;
250: }
251:
252: /** Returns the position of the underline relative to the baseline. */
253: public float getUnderlineOffset(Graphics g) {
254: if ((inited & UL_INITED) == 0) {
255: initUnderline(g);
256: }
257:
258: return underlineOffset;
259: }
260:
261: /** Returns the position of the underline relative to the baseline. */
262: public float getUnderlineOffset(Component c) {
263: if ((inited & UL_INITED) == 0) {
264: initUnderline(c.getGraphics());
265: }
266:
267: return underlineOffset;
268: }
269:
270: /** Returns the thickness of the underline. */
271: public float getUnderlineThickness(Graphics g) {
272: if ((inited & UL_INITED) == 0) {
273: initUnderline(g);
274: }
275:
276: return underlineThickness;
277: }
278:
279: /** Returns the thickness of the underline. */
280: public float getUnderlineThickness(Component c) {
281: if ((inited & UL_INITED) == 0) {
282: initUnderline(c.getGraphics());
283: }
284:
285: return underlineThickness;
286: }
287:
288: }
289:
290: }
|