KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > lib > terminalemulator > MyFontMetrics


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is Terminal Emulator.
16  * The Initial Developer of the Original Software is Sun Microsystems, Inc..
17  * Portions created by Sun Microsystems, Inc. are Copyright (C) 2001.
18  * All Rights Reserved.
19  *
20  * Contributor(s): Ivan Soleimanipour.
21  */

22
23 /*
24  * "MyFontMetrics"
25  * MyFontMetrics.java 1.5 01/07/10
26  */

27
28 package org.netbeans.lib.terminalemulator;
29
30 import java.awt.*;
31 import java.util.AbstractMap JavaDoc;
32 import java.util.HashMap JavaDoc;
33
34 class MyFontMetrics {
35
36     /**
37      * WidthCache contains a byte array that maps a character to it's cell width.
38      * It also keeps track of whether we've discovered that we're dealing with a
39      * font that has wide characters. This information is kept with WidthCache
40      * because the caches are shared between MyFontMetrics and we test for
41      * multi-cellnes only on a cache miss. So we got into a situation where one
42      * Term got a wide character, missed the cache, figured that it's multi-cell,
43      * then another Term got the same character didn't miss the cache and didn't
44      * set it's own multi-cell bit.
45      *
46      * The reference counting stuff is explained with CacheFactory.
47      */

48
49     static class WidthCache {
50     byte [] cache = new byte[Character.MAX_VALUE+1];
51     int reference_count = 1;
52
53     public void up() {
54         reference_count ++;
55         if (reference_count == 1)
56         cache = new byte[Character.MAX_VALUE+1];
57     }
58
59     public void down() {
60         if (reference_count == 0)
61         return;
62         reference_count --;
63         if (reference_count == 0)
64         cache = null;
65     }
66
67     public boolean isMultiCell() {
68         return multiCell;
69     }
70     public void setMultiCell(boolean multiCell) {
71         this.multiCell = multiCell;
72     }
73     private boolean multiCell = false;
74     }
75
76     /**
77      *
78      * CacheFactory doles out WidthCaches.
79      *
80      * These caches are 64Kb (Character.MAX_VALUE+1) and we don't really want
81      * Each interp to have it's own. So we share them in a map using FontMetrics
82      * as a key. Unfortunately stuff will accumulate in the map. A WeakHashMap
83      * is no good because the keys (FontMetrics) are usually alive. For all I
84      * know Jave might be cacheing them in turn. I actually tried using a
85      * WeakHashMap and wouldn't see things going away, even after System.gc().
86      * <p>
87      * So we get this slightly more involved manager.
88      * <br>
89      * A WidthCache holds on to the actual WidthCache and reference counts it.
90      * When the count goes to 0 the actual cache array is "free"d be nulling
91      * it's reference. To make the reference count go down CacheFactory.disposeBy()
92      * is used. And that is called from MyFontMetrics.finalize().
93      *
94      * NOTE: The actual WidthCache's instances _will_ accumulate, but they are small and
95      * there are only so many font variations an app can go through. As I
96      * mentioned above using a WeakHashMap doesn't help much because WidthCache's
97      * are keyed by relatively persistent FontMetrics.
98      */

99
100     private static class CacheFactory {
101     static synchronized WidthCache cacheForFontMetrics(FontMetrics fm) {
102         WidthCache entry = (WidthCache) map.get(fm);
103         if (entry == null) {
104         entry = new WidthCache();
105         map.put(fm, entry);
106         } else {
107         entry.up();
108         }
109         return entry;
110     }
111
112     static synchronized void disposeBy(FontMetrics fm) {
113         WidthCache entry = (WidthCache) map.get(fm);
114         if (entry != null)
115         entry.down();
116     }
117
118     private static AbstractMap JavaDoc map = new HashMap JavaDoc();
119     }
120
121
122     public MyFontMetrics(Component component, Font font) {
123     fm = component.getFontMetrics(font);
124     width = fm.charWidth('a');
125     height = fm.getHeight();
126     ascent = fm.getAscent();
127     leading = fm.getLeading();
128
129     // HACK
130
// From all I can tell both xterm and DtTerm ignore the leading.
131
// Maybe X font's don't have a leading and Java sets it to one
132
// artificially? Because unless I do the below I get one extra pixel
133
// between my lines.
134
//
135
// Some code takes the leading into account and some code doesn't.
136
// the following makes things match up, but if we ever undo this
137
// we'll have to go and adjust how everything is drawn (cursor,
138
// reverse-video attribute, underscore, bg stripe, selection etc.
139

140     height -= leading;
141     leading = 0;
142
143     cwidth_cache = CacheFactory.cacheForFontMetrics(fm);
144     }
145
146     protected void finalize() {
147     CacheFactory.disposeBy(fm);
148     }
149
150
151     public int width;
152     public int height;
153     public int ascent;
154     public int leading;
155     public FontMetrics fm;
156
157     private WidthCache cwidth_cache;
158
159     public boolean isMultiCell() {
160     return cwidth_cache.isMultiCell();
161     }
162
163     /*
164      * Called 'wcwidth' for historical reasons. (see wcwidth(3) on unix.)
165      * Return how many cells this character occupies.
166      */

167
168     public int wcwidth(char c) {
169     int cell_width = cwidth_cache.cache[c]; // how many cells wide
170

171     if (cell_width == 0) {
172         // width not cached yet so figure it out
173
int pixel_width = fm.charWidth(c);
174
175         if (pixel_width == width) {
176         cell_width = 1;
177
178         } else if (pixel_width == 0) {
179         cell_width = 1;
180
181         } else {
182         // round up pixel width to multiple of cell size
183
// then distill into a width in terms of cells.
184
int mod = pixel_width % width;
185         int rounded_width = pixel_width;
186         if (mod != 0)
187             rounded_width = pixel_width + (width - mod);
188         cell_width = rounded_width/width;
189         if (cell_width == 0)
190             cell_width = 1;
191
192         cwidth_cache.setMultiCell(true);
193         }
194
195         cwidth_cache.cache[c] = (byte) cell_width;
196     }
197     return cell_width;
198     }
199
200     /*
201      * Shift to the multi-cell character regime as soon as we spot one.
202      * The actual work is done in wcwidth() itself.
203      */

204     void checkForMultiCell(char c) {
205     wcwidth(c);
206     }
207 }
208
Popular Tags