1
2
3
4
5
6
7
8
9
10
11
12 package sk.uniba.euromath.foRenderer.figures;
13
14 import org.apache.fop.area.Trait;
15 import org.apache.fop.area.inline.TextArea;
16 import org.apache.fop.datatypes.ColorType;
17 import org.apache.fop.fonts.FontTriplet;
18 import org.eclipse.draw2d.FigureListener;
19 import org.eclipse.draw2d.FigureUtilities;
20 import org.eclipse.draw2d.Graphics;
21 import org.eclipse.draw2d.IFigure;
22 import org.eclipse.draw2d.geometry.Dimension;
23 import org.eclipse.draw2d.geometry.Point;
24 import org.eclipse.draw2d.geometry.Rectangle;
25 import org.eclipse.swt.graphics.Color;
26
27 import sk.uniba.euromath.editor.figures.IEMFigure;
28 import sk.uniba.euromath.editor.textEditor.ITextLocator;
29 import sk.uniba.euromath.foRenderer.Draw2dRenderer;
30 import sk.uniba.euromath.foTools.SWTFontMetrics;
31
32 /***
33 * Figure displaying piece of text of text node rendered by fop to TextArea.
34 *
35 * @author Tomáš Studva, Martin Kollar
36 */
37 public class TextAreaFigure extends BasicFOPFigure implements ITextLocator {
38
39 private String text;
40
41 private int textLength = 0;
42
43 /***
44 * signalize if figure is painted
45 */
46 private boolean painted = false;
47
48 /***
49 * absolute bounding of Figure
50 */
51 private Rectangle textRectangle = null;
52
53 /***
54 * Holds offsets of characters of text. Offset[i] holds offset(from
55 * start of figure) of char with index i. Offset[text length] holds
56 * coordinate of end of text. <b>!!! don't access directly, use getters.</b>
57 */
58 private int[] offsets;
59
60 /***
61 * Constructor computes and sets Text color,font, background and size of
62 * this figure
63 *
64 * @param textArea
65 * Area from FOP with its traits
66 */
67 public TextAreaFigure(TextArea textArea, Draw2dRenderer renderer) {
68 super(textArea);
69 setText(textArea.getText());
70
71
72 if (textArea.getTrait(Trait.COLOR) != null) {
73 setForegroundColor(FOFigureUtils
74 .getSWTColor((ColorType) textArea
75 .getTrait(Trait.COLOR)));
76 }
77
78 if (textArea.getTrait(Trait.BACKGROUND) != null) {
79 setOpaque(true);
80 setBackgroundColor(FOFigureUtils
81 .getSWTColor((ColorType) textArea
82 .getTrait(Trait.BACKGROUND)));
83 }
84
85
86 FontTriplet fontTriplet = (FontTriplet) textArea
87 .getTrait(Trait.FONT);
88
89
90 String internalFontKey = renderer.getFontInfo()
91 .getInternalFontKey(fontTriplet);
92 SWTFontMetrics metric = (SWTFontMetrics) renderer.getFontInfo()
93 .getMetricsFor(internalFontKey);
94 Integer size = (Integer) textArea.getTrait(Trait.FONT_SIZE);
95 setFont(metric.getFont((int) (size.intValue() / 1000.0f)));
96
97
98 setSize(Math.max(FOFigureUtils.mps2pxs(textArea.getIPD()),
99 FigureUtilities.getTextExtents(getText(),
100 getFont()).width),
101 FOFigureUtils.mps2pxs(textArea.getAllocBPD()));
102
103
104
105
106
107 addFigureListener(new FigureListener() {
108
109 public void figureMoved(IFigure source) {
110 setTextRectangle(null);
111 TextAreaFigure.this.offsets = null;
112 }
113
114 });
115 }
116
117
118
119
120
121
122 public final String getID() {
123 String id = super.getID();
124
125
126 if ((id == null) && (getParent() instanceof InlineParentFigure)) {
127 id = ((IEMFigure) getParent()).getID();
128 }
129 return id;
130 }
131
132 /***
133 * @return Text of TextArea
134 */
135 public String getText() {
136 return this.text;
137 }
138
139 /***
140 * Sets text,textlength and invalidates
141 *
142 * @param text
143 * new text
144 */
145 public void setText(String text) {
146 if (text == null) {
147 throw new IllegalArgumentException(
148 "Text can not be null");
149 }
150 this.text = text;
151 setTextLength(text.length());
152 setTextRectangle(null);
153 this.offsets = null;
154 invalidate();
155 }
156
157 /***
158 * Sets text and text length , computes size of figure
159 *
160 * @param text
161 * new text
162 */
163 public void updateText(String text) {
164 if (text == null) {
165 throw new IllegalArgumentException(
166 "Text can not be null");
167 }
168 this.text = text;
169 setTextLength(text.length());
170 setTextRectangle(null);
171 this.offsets = null;
172 setSize(Math.max(FOFigureUtils.mps2pxs(getArea().getIPD()),
173 FigureUtilities.getTextExtents(getText(),
174 getFont()).width),
175 FOFigureUtils.mps2pxs(getArea().getAllocBPD()));
176 }
177
178 /***
179 * Computes bounding rectangle of a text substring computed from given
180 * locations.
181 *
182 * @param topLeft
183 * top left point (if null, then from start)
184 * @param bottomRight
185 * bottom right point (if null, then to the end)
186 * @return Rectangle that exactly bounds text substring within given
187 * locations.
188 * @deprecated
189 */
190 public Rectangle getBounding(Point topLeft, Point bottomRight) {
191 Rectangle result = null;
192
193 int c1, c2;
194 if ((topLeft == null)
195 || (!(getTextRectangle().contains(topLeft))))
196 c1 = 0;
197 else
198 c1 = getNearestCharIndexAtPos(topLeft.x);
199
200 if ((bottomRight == null)
201 || (!(getTextRectangle().contains(bottomRight))))
202 c2 = getTextLength();
203 else
204 c2 = getNearestCharIndexAtPos(bottomRight.x);
205
206 if ((c1 >= 0) && (c2 >= 0)) {
207 if (c2 < c1) {
208 int temp = c1;
209 c1 = c2;
210 c2 = temp;
211 }
212 result = new Rectangle(this.textRectangle.x
213 + getOffset(c1), this.textRectangle.y,
214 getOffset(c2) - getOffset(c1),
215 this.textRectangle.height);
216 }
217 return result;
218 }
219
220
221
222
223
224
225 public int getNearestCharIndexAtPos(int x) {
226
227 if (!isPainted())
228 throw new IllegalStateException(
229 "Figure is not painted,can not compute caracter index");
230
231
232 if (x < this.textRectangle.x)
233 return 0;
234
235 if (x > this.textRectangle.x + this.textRectangle.width)
236 return getTextLength() - 1;
237
238 int offsetX = x - this.textRectangle.x;
239
240 int pos = 0;
241
242
243 while ((pos < getTextLength() - 1)
244
245 && (getOffset(pos + 1) <= offsetX)) {
246 pos++;
247 }
248 return pos;
249 }
250
251
252
253
254
255
256 public int getNearestCharGapIndexAtPos(int x) {
257
258 if (!isPainted())
259 throw new IllegalStateException(
260 "Figure is not painted,can not compute caracter index");
261
262
263 if (x < this.textRectangle.x)
264 return 0;
265
266 if (x > this.textRectangle.x + this.textRectangle.width)
267
268 return getTextLength();
269
270 int offsetX = x - this.textRectangle.x;
271
272 int pos = 0;
273
274
275 while ((pos <= getTextLength() - 1)
276
277 && ((getOffset(pos + 1) + getOffset(pos)) / 2 <= offsetX)) {
278 pos++;
279 }
280 return pos;
281 }
282
283 /***
284 * @return space between textRectangle and figure bounds from both sides
285 */
286 public Dimension getSpaceAvailable() {
287 prepareTextLayoutData();
288 return new Dimension(
289 getSize().width - this.textRectangle.width,
290 getSize().height - this.textRectangle.width);
291 }
292
293
294
295
296
297
298 protected void paintFigure(Graphics g) {
299 super.paintFigure(g);
300 paintText(g);
301 }
302
303 protected void paintText(Graphics g) {
304 Point loc = getLocation();
305 getTextBounds();
306 if (!isPainted()
307 || !loc
308 .equals(getTextRectangle()
309 .getLocation())) {
310 prepareTextLayoutData();
311 }
312 Color fg = g.getForegroundColor();
313 g.setForegroundColor(getForegroundColor());
314 g.drawString(getText(), loc);
315 g.setForegroundColor(fg);
316 setPainted(true);
317 firePropertyChange("painted", false, true);
318 }
319
320 private void prepareTextLayoutData() {
321 if (getFont() == null)
322 throw new IllegalStateException("Font can not by null");
323 setTextRectangle(new Rectangle(getLocation(), FigureUtilities
324 .getTextExtents(getText(), getFont())));
325 this.offsets = null;
326 }
327
328 /***
329 * Computes offsets for each character of text, stores it in array
330 * offsets 0, ..., text length.
331 */
332 private void computeOffsets() {
333 this.offsets = new int[getTextLength() + 1];
334
335 int offset = 0;
336 for (int i = 0; i < getTextLength(); i++) {
337 this.offsets[i] = offset;
338 offset += FigureUtilities.getTextWidth(getText()
339 .substring(i, i + 1), getFont());
340 }
341
342 this.offsets[getTextLength()] = offset;
343 }
344
345
346
347
348
349
350 public Point getStart(int charIndex) {
351 if (charIndex < 0 || charIndex >= this.textLength)
352 throw new IllegalArgumentException();
353
354 Rectangle tb = getTextBounds();
355 return new Point(tb.x + getOffset(charIndex), tb.y);
356 }
357
358
359
360
361
362
363 public Point getEnd(int charIndex) {
364 if (charIndex < 0 || charIndex >= this.textLength)
365 throw new IllegalArgumentException();
366
367 Rectangle tb = getTextBounds();
368 return new Point(tb.x + getOffset(charIndex + 1), tb.y);
369 }
370
371
372
373
374
375
376 public Rectangle getTextBounds() {
377 return getTextBounds(0, getTextLength());
378 }
379
380
381
382
383
384
385
386 public Rectangle getTextBounds(int start, int end) {
387 start = Math.max(0, start);
388 end = Math.min(end, getTextLength());
389 Rectangle textBox = getTextRectangle();
390
391 return new Rectangle(textBox.x + getOffset(start), textBox.y,
392 getOffset(end) - getOffset(start),
393 textBox.height);
394 }
395
396 /***
397 * @return textBounds
398 */
399 private Rectangle getTextRectangle() {
400 if (this.textRectangle == null) {
401 prepareTextLayoutData();
402 }
403 return this.textRectangle;
404 }
405
406 private void setTextRectangle(Rectangle rect) {
407 this.textRectangle = rect;
408 }
409
410 private void setPainted(boolean value) {
411 this.painted = value;
412 }
413
414 /***
415 * @return <code>true</code> if is painted, else return
416 * <code> false </code>
417 */
418 public boolean isPainted() {
419 return this.painted;
420 }
421
422 protected int getTextLength() {
423 return this.textLength;
424 }
425
426 protected void setTextLength(int textLength) {
427 this.textLength = textLength;
428 }
429
430 /***
431 * Offset(from start of figure) of char with index i. For i =text length
432 * returns coordinate of end of text.
433 *
434 * @param index
435 * of character of text or one behind
436 *
437 * @return offset(x coordinate of leftmost point) from start of figure
438 * of character with index i
439 */
440 protected int getOffset(int i) {
441 if (this.offsets == null)
442 computeOffsets();
443 return this.offsets[i];
444 }
445
446 protected int[] getOffsets() {
447 if (this.offsets == null)
448 computeOffsets();
449 return this.offsets;
450 }
451
452 @Override
453 public String toString() {
454 return "TextAreaFigure[id: " + getID() + ", text: " + this.text
455 + "]";
456 }
457
458 }