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

Last change on this file since 1522 was 1522, checked in by bnemhaus, 4 years ago

Fixed issue where final Point ORIGIN was being changed via Point::setPosition. This was being caused when apollo LinkedTrack's where being redrawn.

File size: 8.4 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) {
34 _instance = new SwingTextLayoutManager();
35 }
36
37 return _instance;
38 }
39
40 /** Mapping from Expeditee text-layout objects to swing equivalents. */
41 private HashMap<Long, java.awt.font.TextLayout> _layoutMap;
42
43 private SwingTextLayoutManager()
44 {
45 _layoutMap = new HashMap<Long, java.awt.font.TextLayout>();
46 }
47
48 private synchronized void register(TextLayout layout, java.awt.font.TextLayout swingLayout)
49 {
50 if (layout == null || swingLayout == null) {
51 return;
52 }
53
54 _layoutMap.put(layout.getHandle(), swingLayout);
55 }
56
57 public synchronized java.awt.font.TextLayout getInternalLayout(TextLayout layout)
58 {
59 if (!isTextLayoutValid(layout)) {
60 return null;
61 }
62
63 return _layoutMap.get(layout.getHandle());
64 }
65
66 public synchronized boolean isTextLayoutValid(TextLayout layout)
67 {
68 if (layout == null) {
69 return false;
70 }
71
72 return _layoutMap.containsKey(layout.getHandle());
73 }
74
75 @Override
76 public float getAdvance(TextLayout layout)
77 {
78 if (!isTextLayoutValid(layout)) {
79 return Float.NaN;
80 }
81
82 return getInternalLayout(layout).getAdvance();
83 }
84
85 @Override
86 public int getCharacterCount(TextLayout layout)
87 {
88 if (!isTextLayoutValid(layout)) {
89 return 0;
90 }
91
92 return getInternalLayout(layout).getCharacterCount();
93 }
94
95 @Override
96 public TextHitInfo getNextLeftHit(TextLayout layout, int offset)
97 {
98 if (!isTextLayoutValid(layout)) {
99 return null;
100 }
101
102 return SwingConversions.fromSwingTextHitInfo(getInternalLayout(layout).getNextLeftHit(offset));
103 }
104
105 @Override
106 public TextHitInfo getNextLeftHit(TextLayout layout, TextHitInfo hit)
107 {
108 if (!isTextLayoutValid(layout)) {
109 return null;
110 }
111
112 return SwingConversions.fromSwingTextHitInfo(getInternalLayout(layout).getNextLeftHit(SwingConversions.toSwingTextHitInfo(hit)));
113 }
114
115 @Override
116 public TextHitInfo getNextRightHit(TextLayout layout, int offset)
117 {
118 if (!isTextLayoutValid(layout)) {
119 return null;
120 }
121
122 return SwingConversions.fromSwingTextHitInfo(getInternalLayout(layout).getNextRightHit(offset));
123 }
124
125 @Override
126 public TextHitInfo getNextRightHit(TextLayout layout, TextHitInfo hit)
127 {
128 if (!isTextLayoutValid(layout)) {
129 return null;
130 }
131
132 return SwingConversions.fromSwingTextHitInfo(getInternalLayout(layout).getNextRightHit(SwingConversions.toSwingTextHitInfo(hit)));
133 }
134
135 @Override
136 public float[] getCaretInfo(TextLayout layout, TextHitInfo hit) {
137 if (!isTextLayoutValid(layout)) {
138 return null;
139 }
140
141 return getInternalLayout(layout).getCaretInfo(SwingConversions.toSwingTextHitInfo(hit));
142 }
143
144 @Override
145 public TextHitInfo hitTestChar(TextLayout layout, float x, float y)
146 {
147 if (!isTextLayoutValid(layout)) {
148 return null;
149 }
150
151 return SwingConversions.fromSwingTextHitInfo(getInternalLayout(layout).hitTestChar(x, y));
152 }
153
154 @Override
155 public AxisAlignedBoxBounds getPixelBounds(TextLayout layout, float x, float y)
156 {
157 if (!isTextLayoutValid(layout)) {
158 return null;
159 }
160
161 return SwingConversions.fromSwingRectangle(getInternalLayout(layout).getPixelBounds(null, x, y));
162 }
163
164 @Override
165 public AxisAlignedBoxBounds getLogicalHighlightShape(TextLayout layout, int firstEndpoint, int secondEndpoint)
166 {
167 if (!isTextLayoutValid(layout)) {
168 return null;
169 }
170
171 Rectangle rect = getInternalLayout(layout).getLogicalHighlightShape(firstEndpoint, secondEndpoint).getBounds();
172
173 return SwingConversions.fromSwingRectangle(rect);
174 }
175
176 @Override
177 public float getAscent(TextLayout layout)
178 {
179 if (!isTextLayoutValid(layout)) {
180 return Float.NaN;
181 }
182
183 return getInternalLayout(layout).getAscent();
184 }
185
186 @Override
187 public float getDescent(TextLayout layout)
188 {
189 if (!isTextLayoutValid(layout)) {
190 return Float.NaN;
191 }
192
193 return getInternalLayout(layout).getDescent();
194 }
195
196 @Override
197 public float getLeading(TextLayout layout)
198 {
199 if (!isTextLayoutValid(layout)) {
200 return Float.NaN;
201 }
202
203 return getInternalLayout(layout).getLeading();
204 }
205
206 @Override
207 public synchronized void releaseLayout(TextLayout layout)
208 {
209 if (!isTextLayoutValid(layout)) {
210 return;
211 }
212
213 _layoutMap.remove(layout.getHandle());
214 }
215
216 @Override
217 public List<TextLayout> layoutString(String string, Font font, Point start, Line[] lineBreakers, int widthLimit, int lineSpacing, boolean dontBreakWords, boolean fullJustify)
218 {
219 if (string == null || font == null || start == null) {
220 return null;
221 }
222
223 // Make sure we have a swing font to use
224 java.awt.Font swingFont;
225 SwingFontManager fontManager = SwingMiscManager.getIfUsingSwingFontManager();
226 if (fontManager == null) {
227 return null;
228 }
229 swingFont = fontManager.getInternalFont(font);
230 if (swingFont == null) {
231 return null;
232 }
233
234 // Temporary list to accumulate the TextLayouts in to
235 List<java.awt.font.TextLayout> layouts = new LinkedList<java.awt.font.TextLayout>();
236 List<Integer> offsets = new LinkedList<Integer>();
237 offsets.add(0);
238 List<Point> positions = new LinkedList<Point>();
239 positions.add(new Point(start));
240
241 // Set up a line-break measurer to layout the text
242 AttributedString paragraphText = new AttributedString(string);
243 paragraphText.addAttribute(TextAttribute.FONT, swingFont);
244 FontRenderContext frc = new FontRenderContext(null, true, true);
245 LineBreakMeasurer lineBreaker = new LineBreakMeasurer(paragraphText.getIterator(), frc);
246 lineBreaker.setPosition(0);
247
248 // Keep laying out text until the input string is completely laid out
249 while (lineBreaker.getPosition() < string.length()) {
250
251 int width = widthLimit;
252 if (lineBreakers != null) {
253 width = Math.min(width, (int) getLineWidth(start, lineBreakers));
254 }
255
256 int end = string.length();
257 for (int i = lineBreaker.getPosition() + 1; i < string.length(); i++) {
258 if (string.charAt(i) == '\n') {
259 end = i;
260 break;
261 }
262 }
263
264 java.awt.font.TextLayout layout = null;
265
266 try {
267 layout = lineBreaker.nextLayout(width, end, dontBreakWords);
268 } catch (ArrayIndexOutOfBoundsException e) {
269 e.printStackTrace();
270 }
271
272 // If it's impossible to layout any more text without breaking a word, just do it
273 if (layout == null && width == widthLimit) {
274 layout = lineBreaker.nextLayout(width, end, false);
275 // If still impossible, give up
276
277 if (layout == null) {
278 break;
279 }
280 }
281
282 if (fullJustify && lineBreaker.getPosition() < string.length()) {
283 layout = layout.getJustifiedLayout(width);
284 }
285
286 layouts.add(layout);
287 offsets.add(lineBreaker.getPosition());
288 int lineDrop = (int) (layout.getAscent() + layout.getDescent());
289 if (lineSpacing >= 0) {
290 lineDrop += lineSpacing;
291 } else {
292 lineDrop += layout.getLeading();
293 }
294
295 Point startLineDrop = new Point(start.getX(), start.getY() + lineDrop);
296 positions.add(startLineDrop);
297 }
298
299 // Convert the accumulated list into an internal-type array
300 List<TextLayout> ret = new LinkedList<TextLayout>();
301 for (int i = 0; i < layouts.size(); i++) {
302 java.awt.font.TextLayout swingLayout = layouts.get(i);
303 int startCharIndex = offsets.get(i);
304 int endCharIndex = offsets.get(i + 1) - 1;
305 String line = string.substring(startCharIndex, endCharIndex + 1);
306 TextLayout layout = TextLayout.get(line, font, startCharIndex, endCharIndex);
307 register(layout, swingLayout);
308 ret.add(layout);
309 }
310
311 return ret;
312
313 }
314
315 @Override
316 public int getStringWidth(Font font, String string)
317 {
318 SwingGraphicsManager g = SwingMiscManager.getIfUsingSwingGraphicsManager();
319 SwingFontManager f = SwingMiscManager.getIfUsingSwingFontManager();
320
321 if (g == null || f == null) {
322 return -1;
323 }
324
325 if (font != null) {
326 return g.getFontMetrics(f.getInternalFont(font)).stringWidth(string);
327 } else {
328 return g.getFontMetrics(f.getInternalFont(f.getDefaultFont())).stringWidth(string);
329 }
330
331 }
332
333}
Note: See TracBrowser for help on using the repository browser.