View Javadoc

1   /*
2    * Copyright 1999-2006 Faculty of Mathematics, Physics
3    * and Informatics, Comenius University, Bratislava. This file is protected by
4    * the Mozilla Public License version 1.1 (the License); you may not use this
5    * file except in compliance with the License. You may obtain a copy of the
6    * License at http://euromath2.sourceforge.net/license.html Unless required by
7    * applicable law or agreed to in writing, software distributed under the
8    * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
9    * OF ANY KIND, either express or implied. See the License for the specific
10   * language governing permissions and limitations under the License.
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                  // SET COLOR
72                  if (textArea.getTrait(Trait.COLOR) != null) {
73                          setForegroundColor(FOFigureUtils
74                                          .getSWTColor((ColorType) textArea
75                                                          .getTrait(Trait.COLOR)));
76                  }
77                  // SET BACKGROUND
78                  if (textArea.getTrait(Trait.BACKGROUND) != null) {
79                          setOpaque(true);
80                          setBackgroundColor(FOFigureUtils
81                                          .getSWTColor((ColorType) textArea
82                                                          .getTrait(Trait.BACKGROUND)));
83                  }
84                  // SET FONT
85                  // get font triplet description of which font to use
86                  FontTriplet fontTriplet = (FontTriplet) textArea
87                                  .getTrait(Trait.FONT);
88                  // get key of such available font to get mapping triplet to
89                  // system font metric
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                  // SET SIZE AND LOCATION
98                  setSize(Math.max(FOFigureUtils.mps2pxs(textArea.getIPD()),
99                                  FigureUtilities.getTextExtents(getText(),
100                                                 getFont()).width),
101                                 FOFigureUtils.mps2pxs(textArea.getAllocBPD()));
102 
103                 // figure.setSize(FigureUtilities.getTextExtents(textArea.getTextArea(),
104                 // figure.getFont()));
105                 // return getFactory().buildFigure((TextArea)getModel());
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          * (non-Javadoc)
119          * 
120          * @see sk.uniba.euromath.fop.figures.FOPFigure#getID()
121          */
122         public final String getID() {
123                 String id = super.getID();
124                 // if text area is covered in InlineParent, than text area has
125                 // id null, and used is InlineParentFigure id
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          * (non-Javadoc)
222          * 
223          * @see sk.uniba.euromath.editor.textEditor.ITextLocator#getCharIndexAtPos(int)
224          */
225         public int getNearestCharIndexAtPos(int x) {
226                 // if figure is not painted, then it doesn't work
227                 if (!isPainted())
228                         throw new IllegalStateException(
229                                         "Figure is not painted,can not compute caracter index");
230 
231                 // if x is out of bounds
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                 // TODO Studva: binary search should be implemented
242                 // nearest char is find by offsets of chars
243                 while ((pos < getTextLength() - 1)
244                 // offset of char at pos + 1
245                                 && (getOffset(pos + 1) <= offsetX)) {
246                         pos++;
247                 }
248                 return pos;
249         }
250 
251         /*
252          * (non-Javadoc)
253          * 
254          * @see sk.uniba.euromath.editor.textEditor.ITextLocator#getNearestCharGapIndexAtPos(int)
255          */
256         public int getNearestCharGapIndexAtPos(int x) {
257                 // if figure is not painted, then it doesn't work
258                 if (!isPainted())
259                         throw new IllegalStateException(
260                                         "Figure is not painted,can not compute caracter index");
261 
262                 // if x is out of bounds
263                 if (x < this.textRectangle.x)
264                         return 0;
265 
266                 if (x > this.textRectangle.x + this.textRectangle.width)
267                         // return last gap index
268                         return getTextLength();
269 
270                 int offsetX = x - this.textRectangle.x;
271 
272                 int pos = 0;
273                 // TODO Studva: binary search should be implemented
274                 // nearest gap is find by offsets of half of chars
275                 while ((pos <= getTextLength() - 1)
276                 // offset of half of char at pos
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          * (non-Javadoc)
295          * 
296          * @see org.eclipse.draw2d.Figure#paintFigure(org.eclipse.draw2d.Graphics)
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                 // holds offset of character with index i
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                 // Offset[text length] holds coordinate of end of text.
342                 this.offsets[getTextLength()] = offset;
343         }
344 
345         /*
346          * (non-Javadoc)
347          * 
348          * @see sk.uniba.euromath.editor.textEditor.ITextLocator#getStart(int)
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          * (non-Javadoc)
360          * 
361          * @see sk.uniba.euromath.editor.textEditor.ITextLocator#getEnd(int)
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          * (non-Javadoc)
373          * 
374          * @see sk.uniba.euromath.editor.textEditor.ITextLocator#getTextBounds()
375          */
376         public Rectangle getTextBounds() {
377                 return getTextBounds(0, getTextLength());
378         }
379 
380         /*
381          * (non-Javadoc)
382          * 
383          * @see sk.uniba.euromath.editor.textEditor.ITextLocator#getTextBounds(int,
384          *      int)
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 }