Source code: com/virtuosotechnologies/asaph/standardgui/LineTextView.java
1 /*
2 ================================================================================
3
4 FILE: LineTextView.java
5
6 PROJECT:
7
8 Virtuoso Utilities
9
10 CONTENTS:
11
12 LineMemberView representing a fragment of normal text
13
14 PROGRAMMERS:
15
16 Daniel Azuma (DA) <dazuma@kagi.com>
17
18 COPYRIGHT:
19
20 Copyright (C) 2003 Daniel Azuma (dazuma@kagi.com)
21
22 This program is free software; you can redistribute it and/or
23 modify it under the terms of the GNU General Public License as
24 published by the Free Software Foundation; either version 2
25 of the License, or (at your option) any later version.
26
27 This program is distributed in the hope that it will be useful,
28 but WITHOUT ANY WARRANTY; without even the implied warranty of
29 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 GNU General Public License for more details.
31
32 You should have received a copy of the GNU General Public
33 License along with this program; if not, write to
34 Free Software Foundation, Inc.
35 59 Temple Place, Suite 330
36 Boston, MA 02111-1307 USA
37
38 ================================================================================
39 */
40
41
42 package com.virtuosotechnologies.asaph.standardgui;
43
44
45 import java.awt.Font;
46 import java.awt.Graphics2D;
47 import java.awt.font.TextLayout;
48 import java.awt.font.FontRenderContext;
49 import java.awt.font.TextHitInfo;
50 import java.awt.geom.Line2D;
51 import java.awt.geom.Rectangle2D;
52
53 import com.virtuosotechnologies.asaph.model.SongLineMember;
54 import com.virtuosotechnologies.asaph.model.TextString;
55 import com.virtuosotechnologies.asaph.model.CommentString;
56
57
58 /**
59 * LineMemberView representing a fragment of normal text
60 */
61 /*package*/ class LineTextView
62 extends LineMemberView
63 {
64 private TextLayout textLayout_;
65 private float baseline_;
66 private float leftLimit_;
67 private float rightLimit_;
68 private boolean blank_;
69 private String str_;
70
71
72 /**
73 * Constructor
74 */
75 private LineTextView(
76 SongLineMember model,
77 String str,
78 Font font,
79 float xpos,
80 float width,
81 float baseline,
82 FontRenderContext frc)
83 {
84 super(model, xpos, width);
85 baseline_ = baseline;
86 blank_ = false;
87 str_ = str;
88 if (str.length() == 0)
89 {
90 str = " ";
91 blank_ = true;
92 }
93 textLayout_ = new TextLayout(str, font, frc);
94 leftLimit_ = Float.NEGATIVE_INFINITY;
95 rightLimit_ = Float.POSITIVE_INFINITY;
96 }
97
98
99 /**
100 * Constructor
101 */
102 /*package*/ LineTextView(
103 TextString model,
104 String str,
105 float xpos,
106 float width,
107 float baseline,
108 FontRenderContext frc)
109 {
110 this(model, str, EditorConstants.TEXT_FONT, xpos, width, baseline, frc);
111 }
112
113
114 /**
115 * Constructor
116 */
117 /*package*/ LineTextView(
118 CommentString model,
119 String str,
120 float xpos,
121 float width,
122 float baseline,
123 FontRenderContext frc)
124 {
125 this(model, str, EditorConstants.COMMENT_FONT, xpos, width, baseline, frc);
126 }
127
128
129 /**
130 * Get the string
131 */
132 /*package*/ String getText()
133 {
134 return str_;
135 }
136
137
138 /**
139 * Set left limit
140 */
141 /*package*/ void setLeftLimit(
142 float leftLimit)
143 {
144 leftLimit_ = leftLimit;
145 }
146
147
148 /**
149 * Set right limit
150 */
151 /*package*/ void setRightLimit(
152 float rightLimit)
153 {
154 rightLimit_ = rightLimit;
155 }
156
157
158 /**
159 * Accessor for number of characters
160 */
161 /*package*/ int getCharacterCount()
162 {
163 return str_.length();
164 }
165
166
167 /**
168 * Accessor for baseline
169 */
170 /*package*/ float getBaseline()
171 {
172 return baseline_;
173 }
174
175
176 /**
177 * Accessor for top
178 */
179 /*package*/ float getTop()
180 {
181 return baseline_-textLayout_.getAscent();
182 }
183
184
185 /**
186 * Accessor for height
187 */
188 /*package*/ float getHeight()
189 {
190 return textLayout_.getAscent()+textLayout_.getDescent();
191 }
192
193
194 /**
195 * Get offset given a position.
196 */
197 /*package*/ int getOffsetForPosition(
198 float x,
199 float y)
200 {
201 if (x < leftLimit_ || x > rightLimit_)
202 {
203 return -1;
204 }
205 if (blank_)
206 {
207 return 0;
208 }
209 TextHitInfo hitInfo = textLayout_.hitTestChar(x-getXPos(), y-baseline_);
210 int ret = hitInfo.getCharIndex();
211 if (!hitInfo.isLeadingEdge())
212 {
213 ++ret;
214 }
215 return ret;
216 }
217
218
219 /**
220 * Get offset given a position.
221 */
222 /*package*/ int[] getDoubleClickOffsetsForPosition(
223 float x,
224 float y)
225 {
226 if (x < leftLimit_ || x > rightLimit_)
227 {
228 return null;
229 }
230 if (blank_)
231 {
232 return new int[]{0, 0};
233 }
234 TextHitInfo hitInfo = textLayout_.hitTestChar(x-getXPos(), y-baseline_);
235 int[] ret = new int[2];
236 ret[0] = hitInfo.getCharIndex();
237 if (!hitInfo.isLeadingEdge())
238 {
239 ++ret[0];
240 }
241 if (ret[0] == str_.length())
242 {
243 --ret[0];
244 }
245 ret[1] = ret[0]+1;
246 if (isAlphanumeric(str_.charAt(ret[0])))
247 {
248 while (ret[0] > 0 && isAlphanumeric(str_.charAt(ret[0]-1)))
249 {
250 --ret[0];
251 }
252 int max = str_.length();
253 while (ret[1] < max && isAlphanumeric(str_.charAt(ret[1])))
254 {
255 ++ret[1];
256 }
257 }
258 return ret;
259 }
260
261
262 private boolean isAlphanumeric(
263 char ch)
264 {
265 return (ch != ' ');
266 }
267
268
269 /**
270 * Get the x position (in coordinates relative to the line, not the component)
271 * for the given offset.
272 */
273 /*package*/ float getXForOffset(
274 int offset)
275 {
276 assert offset >= 0;
277 TextHitInfo hitInfo;
278 if (offset == 0)
279 {
280 hitInfo = TextHitInfo.leading(0);
281 }
282 else
283 {
284 hitInfo = TextHitInfo.trailing(offset-1);
285 }
286 float[] info = textLayout_.getCaretInfo(hitInfo);
287 return info[0]+getXPos();
288 }
289
290
291 /**
292 * Accessor for the caret line segment given a character position and
293 * xy offsets.
294 */
295 /*package*/ Line2D getCaretSegment(
296 int offset,
297 float x,
298 float y)
299 {
300 assert offset >= 0;
301 TextHitInfo hitInfo;
302 if (offset == 0)
303 {
304 hitInfo = TextHitInfo.leading(0);
305 }
306 else
307 {
308 hitInfo = TextHitInfo.trailing(offset-1);
309 }
310 float[] info = textLayout_.getCaretInfo(hitInfo);
311
312 // Hack workaround for Mac OS X bug (radar 3193109)
313 if (info[1] > 0.3f) info[1] = 0.3f;
314
315 float baselineX = info[0]+getXPos()+x;
316 return new Line2D.Float(
317 baselineX+textLayout_.getAscent()*info[1], baseline_-textLayout_.getAscent()+y,
318 baselineX-textLayout_.getDescent()*info[1], baseline_+textLayout_.getDescent()+y);
319 }
320
321
322 /**
323 * Draw
324 */
325 /*package*/ void paint(
326 Graphics2D g2d,
327 float x,
328 float y)
329 {
330 Rectangle2D rect = new Rectangle2D.Float(
331 getXPos()+x-1, baseline_+y-textLayout_.getAscent(),
332 getWidth()+1, textLayout_.getAscent()+textLayout_.getDescent());
333 //g2d.setColor(EditorConstants.BACKGROUND_COLOR);
334 //g2d.fill(rect);
335 g2d.setColor(EditorConstants.LINE_TEXT_BORDER_COLOR);
336 g2d.draw(rect);
337 if (!blank_)
338 {
339 g2d.setColor(EditorConstants.TEXT_COLOR);
340 textLayout_.draw(g2d, getXPos()+x, baseline_+y);
341 }
342 }
343 }