KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > batik > gvt > TextNode


1 /*
2
3    Copyright 2000-2003 The Apache Software Foundation
4
5    Licensed under the Apache License, Version 2.0 (the "License");
6    you may not use this file except in compliance with the License.
7    You may obtain a copy of the License at
8
9        http://www.apache.org/licenses/LICENSE-2.0
10
11    Unless required by applicable law or agreed to in writing, software
12    distributed under the License is distributed on an "AS IS" BASIS,
13    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14    See the License for the specific language governing permissions and
15    limitations under the License.
16
17  */

18 package org.apache.batik.gvt;
19
20 import java.awt.Graphics2D JavaDoc;
21 import java.awt.Shape JavaDoc;
22 import java.awt.geom.AffineTransform JavaDoc;
23 import java.awt.geom.GeneralPath JavaDoc;
24 import java.awt.geom.Point2D JavaDoc;
25 import java.awt.geom.Rectangle2D JavaDoc;
26 import java.text.AttributedCharacterIterator JavaDoc;
27 import java.text.CharacterIterator JavaDoc;
28 import java.util.List JavaDoc;
29
30 import org.apache.batik.gvt.renderer.StrokingTextPainter;
31 import org.apache.batik.gvt.text.AttributedCharacterSpanIterator;
32 import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
33 import org.apache.batik.gvt.text.Mark;
34 import org.apache.batik.gvt.text.TextHit;
35 import org.apache.batik.gvt.text.TextPaintInfo;
36 import org.apache.batik.gvt.text.TextSpanLayout;
37
38 /**
39  * A graphics node that represents text.
40  *
41  * @author <a HREF="mailto:Thierry.Kormann@sophia.inria.fr">Thierry Kormann</a>
42  * @version $Id: TextNode.java,v 1.34 2005/03/27 08:58:34 cam Exp $
43  */

44 public class TextNode extends AbstractGraphicsNode implements Selectable {
45
46     public static final
47         AttributedCharacterIterator.Attribute JavaDoc PAINT_INFO =
48         GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO;
49
50     /**
51      * Location of this text node (inherited, independent of explicit
52      * X and Y attributes applied to children).
53      */

54     protected Point2D JavaDoc location = new Point2D.Float JavaDoc(0, 0);
55
56     /**
57      * Attributed Character Iterator describing the text
58      */

59     protected AttributedCharacterIterator JavaDoc aci;
60
61     /**
62      * The text of this <tt>TextNode</tt>.
63      */

64     protected String JavaDoc text;
65
66     /**
67      * The begin mark.
68      */

69     protected Mark beginMark = null;
70
71     /**
72      * The end mark.
73      */

74     protected Mark endMark = null;
75
76     /**
77      * The list of text runs.
78      */

79     protected List JavaDoc textRuns;
80
81     /**
82      * The text painter used to display the text of this text node.
83      */

84     protected TextPainter textPainter = StrokingTextPainter.getInstance();
85
86     /**
87      * Internal Cache: Bounds for this text node, without taking any of the
88      * rendering attributes (e.g., stroke) into account
89      */

90     private Rectangle2D JavaDoc geometryBounds;
91
92     /**
93      * Internal Cache: Primitive Bounds.
94      */

95     private Rectangle2D JavaDoc primitiveBounds;
96
97     /**
98      * Internal Cache: the outline.
99      */

100     private Shape JavaDoc outline;
101
102     /**
103      * Constructs a new empty <tt>TextNode</tt>.
104      */

105     public TextNode() {
106     }
107
108     /**
109      * Sets the text painter of this text node. If the specified text
110      * painter is null, this text node will use its default text
111      * painter (StrokingTextPainter.getInstance()).
112      *
113      * @param textPainter the text painter to use
114      */

115     public void setTextPainter(TextPainter textPainter) {
116         if (textPainter == null) {
117             this.textPainter = StrokingTextPainter.getInstance();
118         } else {
119             this.textPainter = textPainter;
120         }
121     }
122
123     /**
124      * Returns the text painter of this text node.
125      */

126     public TextPainter getTextPainter() {
127         return textPainter;
128     }
129
130     /**
131      * Returns a list of text runs.
132      */

133     public List JavaDoc getTextRuns() {
134         return textRuns;
135     }
136
137     /**
138      * Sets the list of text runs of this text node.
139      *
140      * @param textRuns the new list of text runs
141      */

142     public void setTextRuns(List JavaDoc textRuns) {
143         this.textRuns = textRuns;
144     }
145
146     /**
147      * Returns the text of this <tt>TextNode</tt> as a string.
148      */

149     public String JavaDoc getText() {
150         if (text == null) {
151             StringBuffer JavaDoc buf = new StringBuffer JavaDoc(aci.getEndIndex());
152             for (char c = aci.first();
153                  c != CharacterIterator.DONE;
154                  c = aci.next()) {
155                 buf.append(c);
156             }
157             text = buf.toString();
158         }
159         return text;
160     }
161
162     /**
163      * Sets the location of this text node.
164      *
165      * @param newLocation the new location of this text node
166      */

167     public void setLocation(Point2D JavaDoc newLocation){
168         fireGraphicsNodeChangeStarted();
169         invalidateGeometryCache();
170         this.location = newLocation;
171         fireGraphicsNodeChangeCompleted();
172     }
173
174     /**
175      * Returns the location of this text node.
176      *
177      * @return the location of this text node
178      */

179     public Point2D JavaDoc getLocation(){
180         return location;
181     }
182
183     public void swapTextPaintInfo(TextPaintInfo newInfo,
184                                   TextPaintInfo oldInfo) {
185         fireGraphicsNodeChangeStarted();
186         invalidateGeometryCache();
187         oldInfo.set(newInfo);
188         fireGraphicsNodeChangeCompleted();
189     }
190                                   
191
192     /**
193      * Sets the attributed character iterator of this text node.
194      *
195      * @param newAci the new attributed character iterator
196      */

197     public void setAttributedCharacterIterator
198         (AttributedCharacterIterator JavaDoc newAci) {
199         fireGraphicsNodeChangeStarted();
200         invalidateGeometryCache();
201         this.aci = newAci;
202         text = null;
203         textRuns = null;
204         fireGraphicsNodeChangeCompleted();
205     }
206
207     /**
208      * Returns the attributed character iterator of this text node.
209      *
210      * @return the attributed character iterator
211      */

212     public AttributedCharacterIterator JavaDoc getAttributedCharacterIterator(){
213         return aci;
214     }
215
216     //
217
// Geometric methods
218
//
219

220     /**
221      * Invalidates this <tt>TextNode</tt>. This node and all its ancestors have
222      * been informed that all its cached values related to its bounds must be
223      * recomputed.
224      */

225     protected void invalidateGeometryCache() {
226         super.invalidateGeometryCache();
227         primitiveBounds = null;
228         geometryBounds = null;
229         outline = null;
230     }
231
232     /**
233      * Returns the bounds of the area covered by this node's primitive paint.
234      */

235     public Rectangle2D JavaDoc getPrimitiveBounds(){
236         if (primitiveBounds == null) {
237             if (aci != null) {
238                 primitiveBounds = textPainter.getBounds2D(this);
239             }
240         }
241         return primitiveBounds;
242     }
243
244     /**
245      * Returns the bounds of the area covered by this node, without
246      * taking any of its rendering attribute into account. That is,
247      * exclusive of any clipping, masking, filtering or stroking, for
248      * example.
249      */

250     public Rectangle2D JavaDoc getGeometryBounds(){
251         if (geometryBounds == null){
252             if (aci != null) {
253                 geometryBounds = textPainter.getGeometryBounds(this);
254             }
255         }
256         return geometryBounds;
257     }
258
259     /**
260      * Returns the bounds of the sensitive area covered by this node,
261      * This includes the stroked area but does not include the effects
262      * of clipping, masking or filtering.
263      */

264     public Rectangle2D JavaDoc getSensitiveBounds() {
265         return getGeometryBounds();
266     }
267
268     /**
269      * Returns the outline of this node.
270      */

271     public Shape JavaDoc getOutline() {
272         if (outline == null) {
273             if (aci != null) {
274                 outline = textPainter.getOutline(this);
275             }
276         }
277         return outline;
278     }
279
280     /**
281      * Return the marker for the character at index in this nodes
282      * AttributedCharacterIterator. Before Char indicates if the
283      * Marker should be considered before or after char.
284      */

285     public Mark getMarkerForChar(int index, boolean beforeChar) {
286         return textPainter.getMark(this, index, beforeChar);
287     }
288
289     //
290
// Selection methods
291
//
292
public void setSelection(Mark begin, Mark end) {
293         if ((begin.getTextNode() != this) ||
294             (end.getTextNode() != this))
295             throw new Error JavaDoc("Markers not from this TextNode");
296
297         beginMark = begin;
298         endMark = end;
299     }
300
301     /**
302      * Initializes the current selection to begin with the character at (x, y).
303      * @param x the x coordinate of the start of the selection
304      * @param y the y coordinate of the start of the selection
305      */

306     public boolean selectAt(double x, double y) {
307         beginMark = textPainter.selectAt(x, y, this);
308         return true; // assume this always changes selection, for now.
309
}
310
311     /**
312      * Extends the current selection to the character at (x, y).
313      * @param x the x coordinate of the end of the selection
314      * @param y the y coordinate of the end of the selection
315      */

316     public boolean selectTo(double x, double y) {
317         Mark tmpMark = textPainter.selectTo(x, y, beginMark);
318         if (tmpMark == null)
319             return false;
320         if (tmpMark != endMark) {
321             endMark = tmpMark;
322             return true;
323         }
324         return false;
325     }
326
327     /**
328      * Selects all the text in this TextNode. The coordinates are ignored.
329      * @param x the x coordinate of the point the selection was made
330      * @param y the y coordinate of the point the selection was made
331      */

332     public boolean selectAll(double x, double y) {
333         beginMark = textPainter.selectFirst(this);
334         endMark = textPainter.selectLast(this);
335         return true;
336     }
337
338     /**
339      * Gets the current text selection.
340      *
341      * @return an object containing the selected content.
342      */

343     public Object JavaDoc getSelection() {
344
345         int[] ranges = textPainter.getSelected(beginMark, endMark);
346         Object JavaDoc o = null;
347
348         // TODO: later we can return more complex things like
349
// noncontiguous selections
350
if ((ranges != null) && (ranges.length > 1)) {
351             // make sure that they are in order
352
if (ranges[0] > ranges[1]) {
353                 int temp = ranges[1];
354                 ranges[1] = ranges[0];
355                 ranges[0] = temp;
356             }
357             o = new AttributedCharacterSpanIterator
358                 (aci, ranges[0], ranges[1]+1);
359         }
360         return o;
361     }
362
363     /**
364      * Returns the shape used to outline this text node.
365      *
366      * @return a Shape which encloses the current text selection.
367      */

368     public Shape JavaDoc getHighlightShape() {
369         Shape JavaDoc highlightShape =
370             textPainter.getHighlightShape(beginMark, endMark);
371         AffineTransform JavaDoc t = getGlobalTransform();
372         highlightShape = t.createTransformedShape(highlightShape);
373         return highlightShape;
374     }
375
376     //
377
// Drawing methods
378
//
379

380     /**
381      * Paints this node without applying Filter, Mask, Composite, and clip.
382      *
383      * @param g2d the Graphics2D to use
384      */

385     public void primitivePaint(Graphics2D JavaDoc g2d) {
386         //
387
// DO NOT REMOVE: THE FOLLOWING IS A WORK AROUND
388
// A BUG IN THE JDK 1.2 RENDERING PIPELINE WHEN
389
// THE CLIP IS A RECTANGLE
390
//
391
Shape JavaDoc clip = g2d.getClip();
392         if (clip != null && !(clip instanceof GeneralPath JavaDoc)) {
393             g2d.setClip(new GeneralPath JavaDoc(clip));
394         }
395         // Paint the text
396
textPainter.paint(this, g2d);
397     }
398
399     //
400
// Geometric methods
401
//
402

403     /**
404      * Returns true if the specified Point2D is inside the boundary of this
405      * node, false otherwise.
406      *
407      * @param p the specified Point2D in the user space
408      */

409     public boolean contains(Point2D JavaDoc p) {
410         // <!> FIXME: should put this code in TextPaint somewhere,
411
// as pointer-events support - same problem with pointer-events
412
// and ShapeNode
413
if (!super.contains(p)) {
414             return false;
415         }
416         List JavaDoc list = getTextRuns();
417         // place coords in text node coordinate system
418
for (int i = 0 ; i < list.size(); i++) {
419             StrokingTextPainter.TextRun run =
420                 (StrokingTextPainter.TextRun)list.get(i);
421             TextSpanLayout layout = run.getLayout();
422             float x = (float)p.getX();
423             float y = (float)p.getY();
424             TextHit textHit = layout.hitTestChar(x, y);
425             if (textHit != null && contains(p, layout.getBounds2D())) {
426                 return true;
427             }
428         }
429         return false;
430     }
431
432     protected boolean contains(Point2D JavaDoc p, Rectangle2D JavaDoc b) {
433         if (b == null || !b.contains(p)) {
434             return false;
435         }
436         switch(pointerEventType) {
437         case VISIBLE_PAINTED:
438         case VISIBLE_FILL:
439         case VISIBLE_STROKE:
440         case VISIBLE:
441             return isVisible;
442         case PAINTED:
443         case FILL:
444         case STROKE:
445         case ALL:
446             return true;
447         case NONE:
448             return false;
449         default:
450             return false;
451         }
452     }
453
454     /**
455      * Defines where the text of a <tt>TextNode</tt> can be anchored
456      * relative to its location.
457      */

458     public static final class Anchor implements java.io.Serializable JavaDoc {
459
460         /**
461          * The type of the START anchor.
462          */

463         public static final int ANCHOR_START = 0;
464
465         /**
466          * The type of the MIDDLE anchor.
467          */

468         public static final int ANCHOR_MIDDLE = 1;
469
470         /**
471          * The type of the END anchor.
472          */

473         public static final int ANCHOR_END = 2;
474
475         /**
476          * The anchor which enables the rendered characters to be aligned such
477          * that the start of the text string is at the initial current text
478          * location.
479          */

480         public static final Anchor START = new Anchor(ANCHOR_START);
481
482         /**
483          * The anchor which enables the rendered characters to be aligned such
484          * that the middle of the text string is at the initial current text
485          * location.
486          */

487         public static final Anchor MIDDLE = new Anchor(ANCHOR_MIDDLE);
488
489         /**
490          * The anchor which enables the rendered characters to be aligned such
491          * that the end of the text string is at the initial current text
492          * location.
493          */

494         public static final Anchor END = new Anchor(ANCHOR_END);
495
496         /**
497          * The anchor type.
498          */

499         private int type;
500
501         /**
502          * No instance of this class.
503          */

504         private Anchor(int type) {
505             this.type = type;
506         }
507
508         /**
509          * Returns the type of this anchor.
510          */

511         public int getType() {
512             return type;
513         }
514
515         /**
516          * This is called by the serialization code before it returns
517          * an unserialized object. To provide for unicity of
518          * instances, the instance that was read is replaced by its
519          * static equivalent. See the serialiazation specification for
520          * further details on this method's logic.
521          */

522         private Object JavaDoc readResolve() throws java.io.ObjectStreamException JavaDoc {
523             switch(type){
524             case ANCHOR_START:
525                 return START;
526             case ANCHOR_MIDDLE:
527                 return MIDDLE;
528             case ANCHOR_END:
529                 return END;
530             default:
531                 throw new Error JavaDoc("Unknown Anchor type");
532             }
533         }
534     }
535 }
536
537
538
Popular Tags