source: trunk/src/org/expeditee/gio/swing/SwingTextLayoutManager.java@ 1097

Last change on this file since 1097 was 1097, checked in by davidb, 6 years ago

Newly structured files from Corey's work on logic/graphics separation

File size: 8.1 KB
Line 
1package org.expeditee.gio.swing;
2
3import java.awt.Rectangle;
4import java.awt.font.FontRenderContext;
5import java.awt.font.LineBreakMeasurer;
6import java.awt.font.TextAttribute;
7import java.text.AttributedString;
8import java.util.HashMap;
9import java.util.LinkedList;
10import java.util.List;
11
12import org.expeditee.core.Font;
13import org.expeditee.core.Line;
14import org.expeditee.core.Point;
15import org.expeditee.core.TextHitInfo;
16import org.expeditee.core.TextLayout;
17import org.expeditee.core.bounds.AxisAlignedBoxBounds;
18import org.expeditee.gio.TextLayoutManager;
19
20/**
21 * TODO: Comment. cts16
22 *
23 * @author cts16
24 */
25public class SwingTextLayoutManager extends TextLayoutManager
26{
27 /** Singleton instance. */
28 private static SwingTextLayoutManager _instance = null;
29
30 /** Singleton instantiator. */
31 public static SwingTextLayoutManager getInstance()
32 {
33 if (_instance == null) _instance = new SwingTextLayoutManager();
34
35 return _instance;
36 }
37
38 /** Mapping from Expeditee text-layout objects to swing equivalents. */
39 private HashMap<Long, java.awt.font.TextLayout> _layoutMap;
40
41 private SwingTextLayoutManager()
42 {
43 _layoutMap = new HashMap<Long, java.awt.font.TextLayout>();
44 }
45
46 private synchronized void register(TextLayout layout, java.awt.font.TextLayout swingLayout)
47 {
48 if (layout == null || swingLayout == null) return;
49
50 _layoutMap.put(layout.getHandle(), swingLayout);
51 }
52
53 public synchronized java.awt.font.TextLayout getInternalLayout(TextLayout layout)
54 {
55 if (!isTextLayoutValid(layout)) return null;
56
57 return _layoutMap.get(layout.getHandle());
58 }
59
60 public synchronized boolean isTextLayoutValid(TextLayout layout)
61 {
62 if (layout == null) return false;
63
64 return _layoutMap.containsKey(layout.getHandle());
65 }
66
67 @Override
68 public float getAdvance(TextLayout layout)
69 {
70 if (!isTextLayoutValid(layout)) return Float.NaN;
71
72 return getInternalLayout(layout).getAdvance();
73 }
74
75 @Override
76 public int getCharacterCount(TextLayout layout)
77 {
78 if (!isTextLayoutValid(layout)) return 0;
79
80 return getInternalLayout(layout).getCharacterCount();
81 }
82
83 @Override
84 public TextHitInfo getNextLeftHit(TextLayout layout, int offset)
85 {
86 if (!isTextLayoutValid(layout)) return null;
87
88 return SwingConversions.fromSwingTextHitInfo(getInternalLayout(layout).getNextLeftHit(offset));
89 }
90
91 @Override
92 public TextHitInfo getNextLeftHit(TextLayout layout, TextHitInfo hit)
93 {
94 if (!isTextLayoutValid(layout)) return null;
95
96 return SwingConversions.fromSwingTextHitInfo(getInternalLayout(layout).getNextLeftHit(SwingConversions.toSwingTextHitInfo(hit)));
97 }
98
99 @Override
100 public TextHitInfo getNextRightHit(TextLayout layout, int offset)
101 {
102 if (!isTextLayoutValid(layout)) return null;
103
104 return SwingConversions.fromSwingTextHitInfo(getInternalLayout(layout).getNextRightHit(offset));
105 }
106
107 @Override
108 public TextHitInfo getNextRightHit(TextLayout layout, TextHitInfo hit)
109 {
110 if (!isTextLayoutValid(layout)) return null;
111
112 return SwingConversions.fromSwingTextHitInfo(getInternalLayout(layout).getNextRightHit(SwingConversions.toSwingTextHitInfo(hit)));
113 }
114
115 @Override
116 public float[] getCaretInfo(TextLayout layout, TextHitInfo hit)
117 {
118 if (!isTextLayoutValid(layout)) return null;
119
120 return getInternalLayout(layout).getCaretInfo(SwingConversions.toSwingTextHitInfo(hit));
121 }
122
123 @Override
124 public TextHitInfo hitTestChar(TextLayout layout, float x, float y)
125 {
126 if (!isTextLayoutValid(layout)) return null;
127
128 return SwingConversions.fromSwingTextHitInfo(getInternalLayout(layout).hitTestChar(x, y));
129 }
130
131 @Override
132 public AxisAlignedBoxBounds getPixelBounds(TextLayout layout, float x, float y)
133 {
134 if (!isTextLayoutValid(layout)) return null;
135
136 return SwingConversions.fromSwingRectangle(getInternalLayout(layout).getPixelBounds(null, x, y));
137 }
138
139 @Override
140 public AxisAlignedBoxBounds getLogicalHighlightShape(TextLayout layout, int firstEndpoint, int secondEndpoint)
141 {
142 if (!isTextLayoutValid(layout)) return null;
143
144 Rectangle rect = getInternalLayout(layout).getLogicalHighlightShape(firstEndpoint, secondEndpoint).getBounds();
145
146 return SwingConversions.fromSwingRectangle(rect);
147 }
148
149 @Override
150 public float getAscent(TextLayout layout)
151 {
152 if (!isTextLayoutValid(layout)) return Float.NaN;
153
154 return getInternalLayout(layout).getAscent();
155 }
156
157 @Override
158 public float getDescent(TextLayout layout)
159 {
160 if (!isTextLayoutValid(layout)) return Float.NaN;
161
162 return getInternalLayout(layout).getDescent();
163 }
164
165 @Override
166 public float getLeading(TextLayout layout)
167 {
168 if (!isTextLayoutValid(layout)) return Float.NaN;
169
170 return getInternalLayout(layout).getLeading();
171 }
172
173 @Override
174 public synchronized void releaseLayout(TextLayout layout)
175 {
176 if (!isTextLayoutValid(layout)) return;
177
178 _layoutMap.remove(layout.getHandle());
179 }
180
181 @Override
182 public List<TextLayout> layoutString(String string, Font font, Point start, Line[] lineBreakers, int widthLimit, int lineSpacing, boolean dontBreakWords, boolean fullJustify)
183 {
184 if (string == null || font == null || start == null) return null;
185
186 // Make sure we have a swing font to use
187 java.awt.Font swingFont;
188 SwingFontManager fontManager = SwingMiscManager.getIfUsingSwingFontManager();
189 if (fontManager == null) return null;
190 swingFont = fontManager.getInternalFont(font);
191 if (swingFont == null) return null;
192
193 // Temporary list to accumulate the TextLayouts in to
194 List<java.awt.font.TextLayout> layouts = new LinkedList<java.awt.font.TextLayout>();
195 List<Integer> offsets = new LinkedList<Integer>();
196 offsets.add(0);
197 List<Point> positions = new LinkedList<Point>();
198 positions.add(new Point(start));
199
200 // Set up a line-break measurer to layout the text
201 AttributedString paragraphText = new AttributedString(string);
202 paragraphText.addAttribute(TextAttribute.FONT, swingFont);
203 FontRenderContext frc = new FontRenderContext(null, true, true);
204 LineBreakMeasurer lineBreaker = new LineBreakMeasurer(paragraphText.getIterator(), frc);
205 lineBreaker.setPosition(0);
206
207 // Keep laying out text until the input string is completely laid out
208 while (lineBreaker.getPosition() < string.length()) {
209
210 int width = widthLimit;
211 if (lineBreakers != null) width = Math.min(width, (int) getLineWidth(start, lineBreakers));
212
213 int end = string.length();
214 for (int i = lineBreaker.getPosition() + 1; i < string.length(); i++) {
215 if (string.charAt(i) == '\n') {
216 end = i;
217 break;
218 }
219 }
220
221 java.awt.font.TextLayout layout = lineBreaker.nextLayout(width, end, dontBreakWords);
222
223 // If it's impossible to layout any more text without breaking a word, just do it
224 if (layout == null && width == widthLimit) {
225 layout = lineBreaker.nextLayout(width, end, false);
226 // If still impossible, give up
227 if (layout == null) break;
228 }
229
230 if (fullJustify && lineBreaker.getPosition() < string.length()) {
231 layout = layout.getJustifiedLayout(width);
232 }
233
234 layouts.add(layout);
235 offsets.add(lineBreaker.getPosition());
236 int lineDrop = (int) (layout.getAscent() + layout.getDescent());
237 if (lineSpacing >= 0) {
238 lineDrop += lineSpacing;
239 } else {
240 lineDrop += layout.getLeading();
241 }
242 start.y += lineDrop;
243 positions.add(new Point(start));
244 }
245
246 // Convert the accumulated list into an internal-type array
247 List<TextLayout> ret = new LinkedList<TextLayout>();
248 for (int i = 0; i < layouts.size(); i++) {
249 java.awt.font.TextLayout swingLayout = layouts.get(i);
250 int startCharIndex = offsets.get(i);
251 int endCharIndex = offsets.get(i + 1) - 1;
252 String line = string.substring(startCharIndex, endCharIndex + 1);
253 TextLayout layout = TextLayout.get(line, font, startCharIndex, endCharIndex);
254 register(layout, swingLayout);
255 ret.add(layout);
256 }
257
258 return ret;
259
260 }
261
262 @Override
263 public int getStringWidth(Font font, String string)
264 {
265 SwingGraphicsManager g = SwingMiscManager.getIfUsingSwingGraphicsManager();
266 SwingFontManager f = SwingMiscManager.getIfUsingSwingFontManager();
267
268 if (g == null || f == null) return -1;
269
270 if (font != null) {
271 return g.getFontMetrics(f.getInternalFont(font)).stringWidth(string);
272 } else {
273 return g.getFontMetrics(f.getInternalFont(f.getDefaultFont())).stringWidth(string);
274 }
275
276 }
277
278}
Note: See TracBrowser for help on using the repository browser.