1 /*
2 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25 package javax.swing.text;
26
27 import java.awt;
28 import javax.swing.SwingConstants;
29 import javax.swing.event;
30
31 /**
32 * <p>
33 * A very important part of the text package is the <code>View</code> class.
34 * As the name suggests it represents a view of the text model,
35 * or a piece of the text model.
36 * It is this class that is responsible for the look of the text component.
37 * The view is not intended to be some completely new thing that one must
38 * learn, but rather is much like a lightweight component.
39 * <p>
40 By default, a view is very light. It contains a reference to the parent
41 view from which it can fetch many things without holding state, and it
42 contains a reference to a portion of the model (<code>Element</code>).
43 A view does not
44 have to exactly represent an element in the model, that is simply a typical
45 and therefore convenient mapping. A view can alternatively maintain a couple
46 of Position objects to maintain its location in the model (i.e. represent
47 a fragment of an element). This is typically the result of formatting where
48 views have been broken down into pieces. The convenience of a substantial
49 relationship to the element makes it easier to build factories to produce the
50 views, and makes it easier to keep track of the view pieces as the model is
51 changed and the view must be changed to reflect the model. Simple views
52 therefore represent an Element directly and complex views do not.
53 <p>
54 A view has the following responsibilities:
55 <dl>
56
57 <dt><b>Participate in layout.</b>
58 <dd>
59 <p>The view has a <code>setSize</code> method which is like
60 <code>doLayout</code> and <code>setSize</code> in <code>Component</code> combined.
61 The view has a <code>preferenceChanged</code> method which is
62 like <code>invalidate</code> in <code>Component</code> except that one can
63 invalidate just one axis
64 and the child requesting the change is identified.
65 <p>A View expresses the size that it would like to be in terms of three
66 values, a minimum, a preferred, and a maximum span. Layout in a view is
67 can be done independently upon each axis. For a properly functioning View
68 implementation, the minimum span will be <= the preferred span which in turn
69 will be <= the maximum span.
70 </p>
71 <p align=center><img src="doc-files/View-flexibility.jpg"
72 alt="The above text describes this graphic.">
73 <p>The minimum set of methods for layout are:
74 <ul>
75 <li><a href="#getMinimumSpan(int)">getMinimumSpan</a>
76 <li><a href="#getPreferredSpan(int)">getPreferredSpan</a>
77 <li><a href="#getMaximumSpan(int)">getMaximumSpan</a>
78 <li><a href="#getAlignment(int)">getAlignment</a>
79 <li><a href="#preferenceChanged(javax.swing.text.View, boolean, boolean)">preferenceChanged</a>
80 <li><a href="#setSize(float, float)">setSize</a>
81 </ul>
82
83 <p>The <code>setSize</code> method should be prepared to be called a number of times
84 (i.e. It may be called even if the size didn't change).
85 The <code>setSize</code> method
86 is generally called to make sure the View layout is complete prior to trying
87 to perform an operation on it that requires an up-to-date layout. A view's
88 size should <em>always</em> be set to a value within the minimum and maximum
89 span specified by that view. Additionally, the view must always call the
90 <code>preferenceChanged</code> method on the parent if it has changed the
91 values for the
92 layout it would like, and expects the parent to honor. The parent View is
93 not required to recognize a change until the <code>preferenceChanged</code>
94 has been sent.
95 This allows parent View implementations to cache the child requirements if
96 desired. The calling sequence looks something like the following:
97 </p>
98 <p align=center>
99 <img src="doc-files/View-layout.jpg"
100 alt="Sample calling sequence between parent view and child view:
101 setSize, getMinimum, getPreferred, getMaximum, getAlignment, setSize">
102 <p>The exact calling sequence is up to the layout functionality of
103 the parent view (if the view has any children). The view may collect
104 the preferences of the children prior to determining what it will give
105 each child, or it might iteratively update the children one at a time.
106 </p>
107
108 <dt><b>Render a portion of the model.</b>
109 <dd>
110 <p>This is done in the paint method, which is pretty much like a component
111 paint method. Views are expected to potentially populate a fairly large
112 tree. A <code>View</code> has the following semantics for rendering:
113 </p>
114 <ul>
115 <li>The view gets its allocation from the parent at paint time, so it
116 must be prepared to redo layout if the allocated area is different from
117 what it is prepared to deal with.
118 <li>The coordinate system is the same as the hosting <code>Component</code>
119 (i.e. the <code>Component</code> returned by the
120 <a href="#getContainer">getContainer</a> method).
121 This means a child view lives in the same coordinate system as the parent
122 view unless the parent has explicitly changed the coordinate system.
123 To schedule itself to be repainted a view can call repaint on the hosting
124 <code>Component</code>.
125 <li>The default is to <em>not clip</em> the children. It is more efficient
126 to allow a view to clip only if it really feels it needs clipping.
127 <li>The <code>Graphics</code> object given is not initialized in any way.
128 A view should set any settings needed.
129 <li>A <code>View</code> is inherently transparent. While a view may render into its
130 entire allocation, typically a view does not. Rendering is performed by
131 tranversing down the tree of <code>View</code> implementations.
132 Each <code>View</code> is responsible
133 for rendering its children. This behavior is depended upon for thread
134 safety. While view implementations do not necessarily have to be implemented
135 with thread safety in mind, other view implementations that do make use of
136 concurrency can depend upon a tree traversal to guarantee thread safety.
137 <li>The order of views relative to the model is up to the implementation.
138 Although child views will typically be arranged in the same order that they
139 occur in the model, they may be visually arranged in an entirely different
140 order. View implementations may have Z-Order associated with them if the
141 children are overlapping.
142 </ul>
143 <p>The methods for rendering are:
144 <ul>
145 <li><a href="#paint(java.awt.Graphics, java.awt.Shape)">paint</a>
146 </ul>
147 <p>
148
149 <dt><b>Translate between the model and view coordinate systems.</b>
150 <dd>
151 <p>Because the view objects are produced from a factory and therefore cannot
152 necessarily be counted upon to be in a particular pattern, one must be able
153 to perform translation to properly locate spatial representation of the model.
154 The methods for doing this are:
155 <ul>
156 <li><a href="#modelToView(int, javax.swing.text.Position.Bias, int, javax.swing.text.Position.Bias, java.awt.Shape)">modelToView</a>
157 <li><a href="#viewToModel(float, float, java.awt.Shape, javax.swing.text.Position.Bias[])">viewToModel</a>
158 <li><a href="#getDocument()">getDocument</a>
159 <li><a href="#getElement()">getElement</a>
160 <li><a href="#getStartOffset()">getStartOffset</a>
161 <li><a href="#getEndOffset()">getEndOffset</a>
162 </ul>
163 <p>The layout must be valid prior to attempting to make the translation.
164 The translation is not valid, and must not be attempted while changes
165 are being broadcasted from the model via a <code>DocumentEvent</code>.
166 </p>
167
168 <dt><b>Respond to changes from the model.</b>
169 <dd>
170 <p>If the overall view is represented by many pieces (which is the best situation
171 if one want to be able to change the view and write the least amount of new code),
172 it would be impractical to have a huge number of <code>DocumentListener</code>s.
173 If each
174 view listened to the model, only a few would actually be interested in the
175 changes broadcasted at any given time. Since the model has no knowledge of
176 views, it has no way to filter the broadcast of change information. The view
177 hierarchy itself is instead responsible for propagating the change information.
178 At any level in the view hierarchy, that view knows enough about its children to
179 best distribute the change information further. Changes are therefore broadcasted
180 starting from the root of the view hierarchy.
181 The methods for doing this are:
182 <ul>
183 <li><a href="#insertUpdate">insertUpdate</a>
184 <li><a href="#removeUpdate">removeUpdate</a>
185 <li><a href="#changedUpdate">changedUpdate</a>
186 </ul>
187 <p>
188 </dl>
189 *
190 * @author Timothy Prinzing
191 */
192 public abstract class View implements SwingConstants {
193
194 /**
195 * Creates a new <code>View</code> object.
196 *
197 * @param elem the <code>Element</code> to represent
198 */
199 public View(Element elem) {
200 this.elem = elem;
201 }
202
203 /**
204 * Returns the parent of the view.
205 *
206 * @return the parent, or <code>null</code> if none exists
207 */
208 public View getParent() {
209 return parent;
210 }
211
212 /**
213 * Returns a boolean that indicates whether
214 * the view is visible or not. By default
215 * all views are visible.
216 *
217 * @return always returns true
218 */
219 public boolean isVisible() {
220 return true;
221 }
222
223
224 /**
225 * Determines the preferred span for this view along an
226 * axis.
227 *
228 * @param axis may be either <code>View.X_AXIS</code> or
229 * <code>View.Y_AXIS</code>
230 * @return the span the view would like to be rendered into.
231 * Typically the view is told to render into the span
232 * that is returned, although there is no guarantee.
233 * The parent may choose to resize or break the view
234 * @see View#getPreferredSpan
235 */
236 public abstract float getPreferredSpan(int axis);
237
238 /**
239 * Determines the minimum span for this view along an
240 * axis.
241 *
242 * @param axis may be either <code>View.X_AXIS</code> or
243 * <code>View.Y_AXIS</code>
244 * @return the minimum span the view can be rendered into
245 * @see View#getPreferredSpan
246 */
247 public float getMinimumSpan(int axis) {
248 int w = getResizeWeight(axis);
249 if (w == 0) {
250 // can't resize
251 return getPreferredSpan(axis);
252 }
253 return 0;
254 }
255
256 /**
257 * Determines the maximum span for this view along an
258 * axis.
259 *
260 * @param axis may be either <code>View.X_AXIS</code> or
261 * <code>View.Y_AXIS</code>
262 * @return the maximum span the view can be rendered into
263 * @see View#getPreferredSpan
264 */
265 public float getMaximumSpan(int axis) {
266 int w = getResizeWeight(axis);
267 if (w == 0) {
268 // can't resize
269 return getPreferredSpan(axis);
270 }
271 return Integer.MAX_VALUE;
272 }
273
274 /**
275 * Child views can call this on the parent to indicate that
276 * the preference has changed and should be reconsidered
277 * for layout. By default this just propagates upward to
278 * the next parent. The root view will call
279 * <code>revalidate</code> on the associated text component.
280 *
281 * @param child the child view
282 * @param width true if the width preference has changed
283 * @param height true if the height preference has changed
284 * @see javax.swing.JComponent#revalidate
285 */
286 public void preferenceChanged(View child, boolean width, boolean height) {
287 View parent = getParent();
288 if (parent != null) {
289 parent.preferenceChanged(this, width, height);
290 }
291 }
292
293 /**
294 * Determines the desired alignment for this view along an
295 * axis. The desired alignment is returned. This should be
296 * a value >= 0.0 and <= 1.0, where 0 indicates alignment at
297 * the origin and 1.0 indicates alignment to the full span
298 * away from the origin. An alignment of 0.5 would be the
299 * center of the view.
300 *
301 * @param axis may be either <code>View.X_AXIS</code> or
302 * <code>View.Y_AXIS</code>
303 * @return the value 0.5
304 */
305 public float getAlignment(int axis) {
306 return 0.5f;
307 }
308
309 /**
310 * Renders using the given rendering surface and area on that
311 * surface. The view may need to do layout and create child
312 * views to enable itself to render into the given allocation.
313 *
314 * @param g the rendering surface to use
315 * @param allocation the allocated region to render into
316 */
317 public abstract void paint(Graphics g, Shape allocation);
318
319 /**
320 * Establishes the parent view for this view. This is
321 * guaranteed to be called before any other methods if the
322 * parent view is functioning properly. This is also
323 * the last method called, since it is called to indicate
324 * the view has been removed from the hierarchy as
325 * well. When this method is called to set the parent to
326 * null, this method does the same for each of its children,
327 * propogating the notification that they have been
328 * disconnected from the view tree. If this is
329 * reimplemented, <code>super.setParent()</code> should
330 * be called.
331 *
332 * @param parent the new parent, or <code>null</code> if the view is
333 * being removed from a parent
334 */
335 public void setParent(View parent) {
336 // if the parent is null then propogate down the view tree
337 if (parent == null) {
338 for (int i = 0; i < getViewCount(); i++) {
339 if (getView(i).getParent() == this) {
340 // in FlowView.java view might be referenced
341 // from two super-views as a child. see logicalView
342 getView(i).setParent(null);
343 }
344 }
345 }
346 this.parent = parent;
347 }
348
349 /**
350 * Returns the number of views in this view. Since
351 * the default is to not be a composite view this
352 * returns 0.
353 *
354 * @return the number of views >= 0
355 * @see View#getViewCount
356 */
357 public int getViewCount() {
358 return 0;
359 }
360
361 /**
362 * Gets the <i>n</i>th child view. Since there are no
363 * children by default, this returns <code>null</code>.
364 *
365 * @param n the number of the view to get, >= 0 && < getViewCount()
366 * @return the view
367 */
368 public View getView(int n) {
369 return null;
370 }
371
372
373 /**
374 * Removes all of the children. This is a convenience
375 * call to <code>replace</code>.
376 *
377 * @since 1.3
378 */
379 public void removeAll() {
380 replace(0, getViewCount(), null);
381 }
382
383 /**
384 * Removes one of the children at the given position.
385 * This is a convenience call to <code>replace</code>.
386 * @since 1.3
387 */
388 public void remove(int i) {
389 replace(i, 1, null);
390 }
391
392 /**
393 * Inserts a single child view. This is a convenience
394 * call to <code>replace</code>.
395 *
396 * @param offs the offset of the view to insert before >= 0
397 * @param v the view
398 * @see #replace
399 * @since 1.3
400 */
401 public void insert(int offs, View v) {
402 View[] one = new View[1];
403 one[0] = v;
404 replace(offs, 0, one);
405 }
406
407 /**
408 * Appends a single child view. This is a convenience
409 * call to <code>replace</code>.
410 *
411 * @param v the view
412 * @see #replace
413 * @since 1.3
414 */
415 public void append(View v) {
416 View[] one = new View[1];
417 one[0] = v;
418 replace(getViewCount(), 0, one);
419 }
420
421 /**
422 * Replaces child views. If there are no views to remove
423 * this acts as an insert. If there are no views to
424 * add this acts as a remove. Views being removed will
425 * have the parent set to <code>null</code>, and the internal reference
426 * to them removed so that they can be garbage collected.
427 * This is implemented to do nothing, because by default
428 * a view has no children.
429 *
430 * @param offset the starting index into the child views to insert
431 * the new views. This should be a value >= 0 and <= getViewCount
432 * @param length the number of existing child views to remove
433 * This should be a value >= 0 and <= (getViewCount() - offset).
434 * @param views the child views to add. This value can be
435 * <code>null</code> to indicate no children are being added
436 * (useful to remove).
437 * @since 1.3
438 */
439 public void replace(int offset, int length, View[] views) {
440 }
441
442 /**
443 * Returns the child view index representing the given position in
444 * the model. By default a view has no children so this is implemented
445 * to return -1 to indicate there is no valid child index for any
446 * position.
447 *
448 * @param pos the position >= 0
449 * @return index of the view representing the given position, or
450 * -1 if no view represents that position
451 * @since 1.3
452 */
453 public int getViewIndex(int pos, Position.Bias b) {
454 return -1;
455 }
456
457 /**
458 * Fetches the allocation for the given child view.
459 * This enables finding out where various views
460 * are located, without assuming how the views store
461 * their location. This returns <code>null</code> since the
462 * default is to not have any child views.
463 *
464 * @param index the index of the child, >= 0 && <
465 * <code>getViewCount()</code>
466 * @param a the allocation to this view
467 * @return the allocation to the child
468 */
469 public Shape getChildAllocation(int index, Shape a) {
470 return null;
471 }
472
473 /**
474 * Provides a way to determine the next visually represented model
475 * location at which one might place a caret.
476 * Some views may not be visible,
477 * they might not be in the same order found in the model, or they just
478 * might not allow access to some of the locations in the model.
479 *
480 * @param pos the position to convert >= 0
481 * @param a the allocated region in which to render
482 * @param direction the direction from the current position that can
483 * be thought of as the arrow keys typically found on a keyboard.
484 * This will be one of the following values:
485 * <ul>
486 * <li>SwingConstants.WEST
487 * <li>SwingConstants.EAST
488 * <li>SwingConstants.NORTH
489 * <li>SwingConstants.SOUTH
490 * </ul>
491 * @return the location within the model that best represents the next
492 * location visual position
493 * @exception BadLocationException
494 * @exception IllegalArgumentException if <code>direction</code>
495 * doesn't have one of the legal values above
496 */
497 public int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a,
498 int direction, Position.Bias[] biasRet)
499 throws BadLocationException {
500
501 biasRet[0] = Position.Bias.Forward;
502 switch (direction) {
503 case NORTH:
504 case SOUTH:
505 {
506 if (pos == -1) {
507 pos = (direction == NORTH) ? Math.max(0, getEndOffset() - 1) :
508 getStartOffset();
509 break;
510 }
511 JTextComponent target = (JTextComponent) getContainer();
512 Caret c = (target != null) ? target.getCaret() : null;
513 // YECK! Ideally, the x location from the magic caret position
514 // would be passed in.
515 Point mcp;
516 if (c != null) {
517 mcp = c.getMagicCaretPosition();
518 }
519 else {
520 mcp = null;
521 }
522 int x;
523 if (mcp == null) {
524 Rectangle loc = target.modelToView(pos);
525 x = (loc == null) ? 0 : loc.x;
526 }
527 else {
528 x = mcp.x;
529 }
530 if (direction == NORTH) {
531 pos = Utilities.getPositionAbove(target, pos, x);
532 }
533 else {
534 pos = Utilities.getPositionBelow(target, pos, x);
535 }
536 }
537 break;
538 case WEST:
539 if(pos == -1) {
540 pos = Math.max(0, getEndOffset() - 1);
541 }
542 else {
543 pos = Math.max(0, pos - 1);
544 }
545 break;
546 case EAST:
547 if(pos == -1) {
548 pos = getStartOffset();
549 }
550 else {
551 pos = Math.min(pos + 1, getDocument().getLength());
552 }
553 break;
554 default:
555 throw new IllegalArgumentException("Bad direction: " + direction);
556 }
557 return pos;
558 }
559
560 /**
561 * Provides a mapping, for a given character,
562 * from the document model coordinate space
563 * to the view coordinate space.
564 *
565 * @param pos the position of the desired character (>=0)
566 * @param a the area of the view, which encompasses the requested character
567 * @param b the bias toward the previous character or the
568 * next character represented by the offset, in case the
569 * position is a boundary of two views; <code>b</code> will have one
570 * of these values:
571 * <ul>
572 * <li> <code>Position.Bias.Forward</code>
573 * <li> <code>Position.Bias.Backward</code>
574 * </ul>
575 * @return the bounding box, in view coordinate space,
576 * of the character at the specified position
577 * @exception BadLocationException if the specified position does
578 * not represent a valid location in the associated document
579 * @exception IllegalArgumentException if <code>b</code> is not one of the
580 * legal <code>Position.Bias</code> values listed above
581 * @see View#viewToModel
582 */
583 public abstract Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException;
584
585 /**
586 * Provides a mapping, for a given region,
587 * from the document model coordinate space
588 * to the view coordinate space. The specified region is
589 * created as a union of the first and last character positions.
590 *
591 * @param p0 the position of the first character (>=0)
592 * @param b0 the bias of the first character position,
593 * toward the previous character or the
594 * next character represented by the offset, in case the
595 * position is a boundary of two views; <code>b0</code> will have one
596 * of these values:
597 * <ul>
598 * <li> <code>Position.Bias.Forward</code>
599 * <li> <code>Position.Bias.Backward</code>
600 * </ul>
601 * @param p1 the position of the last character (>=0)
602 * @param b1 the bias for the second character position, defined
603 * one of the legal values shown above
604 * @param a the area of the view, which encompasses the requested region
605 * @return the bounding box which is a union of the region specified
606 * by the first and last character positions
607 * @exception BadLocationException if the given position does
608 * not represent a valid location in the associated document
609 * @exception IllegalArgumentException if <code>b0</code> or
610 * <code>b1</code> are not one of the
611 * legal <code>Position.Bias</code> values listed above
612 * @see View#viewToModel
613 */
614 public Shape modelToView(int p0, Position.Bias b0, int p1, Position.Bias b1, Shape a) throws BadLocationException {
615 Shape s0 = modelToView(p0, a, b0);
616 Shape s1;
617 if (p1 == getEndOffset()) {
618 try {
619 s1 = modelToView(p1, a, b1);
620 } catch (BadLocationException ble) {
621 s1 = null;
622 }
623 if (s1 == null) {
624 // Assume extends left to right.
625 Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a :
626 a.getBounds();
627 s1 = new Rectangle(alloc.x + alloc.width - 1, alloc.y,
628 1, alloc.height);
629 }
630 }
631 else {
632 s1 = modelToView(p1, a, b1);
633 }
634 Rectangle r0 = s0.getBounds();
635 Rectangle r1 = (s1 instanceof Rectangle) ? (Rectangle) s1 :
636 s1.getBounds();
637 if (r0.y != r1.y) {
638 // If it spans lines, force it to be the width of the view.
639 Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a :
640 a.getBounds();
641 r0.x = alloc.x;
642 r0.width = alloc.width;
643 }
644 r0.add(r1);
645 return r0;
646 }
647
648 /**
649 * Provides a mapping from the view coordinate space to the logical
650 * coordinate space of the model. The <code>biasReturn</code>
651 * argument will be filled in to indicate that the point given is
652 * closer to the next character in the model or the previous
653 * character in the model.
654 *
655 * @param x the X coordinate >= 0
656 * @param y the Y coordinate >= 0
657 * @param a the allocated region in which to render
658 * @return the location within the model that best represents the
659 * given point in the view >= 0. The <code>biasReturn</code>
660 * argument will be
661 * filled in to indicate that the point given is closer to the next
662 * character in the model or the previous character in the model.
663 */
664 public abstract int viewToModel(float x, float y, Shape a, Position.Bias[] biasReturn);
665
666 /**
667 * Gives notification that something was inserted into
668 * the document in a location that this view is responsible for.
669 * To reduce the burden to subclasses, this functionality is
670 * spread out into the following calls that subclasses can
671 * reimplement:
672 * <ol>
673 * <li><a href="#updateChildren">updateChildren</a> is called
674 * if there were any changes to the element this view is
675 * responsible for. If this view has child views that are
676 * represent the child elements, then this method should do
677 * whatever is necessary to make sure the child views correctly
678 * represent the model.
679 * <li><a href="#forwardUpdate">forwardUpdate</a> is called
680 * to forward the DocumentEvent to the appropriate child views.
681 * <li><a href="#updateLayout">updateLayout</a> is called to
682 * give the view a chance to either repair its layout, to reschedule
683 * layout, or do nothing.
684 * </ol>
685 *
686 * @param e the change information from the associated document
687 * @param a the current allocation of the view
688 * @param f the factory to use to rebuild if the view has children
689 * @see View#insertUpdate
690 */
691 public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) {
692 if (getViewCount() > 0) {
693 Element elem = getElement();
694 DocumentEvent.ElementChange ec = e.getChange(elem);
695 if (ec != null) {
696 if (! updateChildren(ec, e, f)) {
697 // don't consider the element changes they
698 // are for a view further down.
699 ec = null;
700 }
701 }
702 forwardUpdate(ec, e, a, f);
703 updateLayout(ec, e, a);
704 }
705 }
706
707 /**
708 * Gives notification that something was removed from the document
709 * in a location that this view is responsible for.
710 * To reduce the burden to subclasses, this functionality is
711 * spread out into the following calls that subclasses can
712 * reimplement:
713 * <ol>
714 * <li><a href="#updateChildren">updateChildren</a> is called
715 * if there were any changes to the element this view is
716 * responsible for. If this view has child views that are
717 * represent the child elements, then this method should do
718 * whatever is necessary to make sure the child views correctly
719 * represent the model.
720 * <li><a href="#forwardUpdate">forwardUpdate</a> is called
721 * to forward the DocumentEvent to the appropriate child views.
722 * <li><a href="#updateLayout">updateLayout</a> is called to
723 * give the view a chance to either repair its layout, to reschedule
724 * layout, or do nothing.
725 * </ol>
726 *
727 * @param e the change information from the associated document
728 * @param a the current allocation of the view
729 * @param f the factory to use to rebuild if the view has children
730 * @see View#removeUpdate
731 */
732 public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) {
733 if (getViewCount() > 0) {
734 Element elem = getElement();
735 DocumentEvent.ElementChange ec = e.getChange(elem);
736 if (ec != null) {
737 if (! updateChildren(ec, e, f)) {
738 // don't consider the element changes they
739 // are for a view further down.
740 ec = null;
741 }
742 }
743 forwardUpdate(ec, e, a, f);
744 updateLayout(ec, e, a);
745 }
746 }
747
748 /**
749 * Gives notification from the document that attributes were changed
750 * in a location that this view is responsible for.
751 * To reduce the burden to subclasses, this functionality is
752 * spread out into the following calls that subclasses can
753 * reimplement:
754 * <ol>
755 * <li><a href="#updateChildren">updateChildren</a> is called
756 * if there were any changes to the element this view is
757 * responsible for. If this view has child views that are
758 * represent the child elements, then this method should do
759 * whatever is necessary to make sure the child views correctly
760 * represent the model.
761 * <li><a href="#forwardUpdate">forwardUpdate</a> is called
762 * to forward the DocumentEvent to the appropriate child views.
763 * <li><a href="#updateLayout">updateLayout</a> is called to
764 * give the view a chance to either repair its layout, to reschedule
765 * layout, or do nothing.
766 * </ol>
767 *
768 * @param e the change information from the associated document
769 * @param a the current allocation of the view
770 * @param f the factory to use to rebuild if the view has children
771 * @see View#changedUpdate
772 */
773 public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
774 if (getViewCount() > 0) {
775 Element elem = getElement();
776 DocumentEvent.ElementChange ec = e.getChange(elem);
777 if (ec != null) {
778 if (! updateChildren(ec, e, f)) {
779 // don't consider the element changes they
780 // are for a view further down.
781 ec = null;
782 }
783 }
784 forwardUpdate(ec, e, a, f);
785 updateLayout(ec, e, a);
786 }
787 }
788
789 /**
790 * Fetches the model associated with the view.
791 *
792 * @return the view model, <code>null</code> if none
793 * @see View#getDocument
794 */
795 public Document getDocument() {
796 return elem.getDocument();
797 }
798
799 /**
800 * Fetches the portion of the model for which this view is
801 * responsible.
802 *
803 * @return the starting offset into the model >= 0
804 * @see View#getStartOffset
805 */
806 public int getStartOffset() {
807 return elem.getStartOffset();
808 }
809
810 /**
811 * Fetches the portion of the model for which this view is
812 * responsible.
813 *
814 * @return the ending offset into the model >= 0
815 * @see View#getEndOffset
816 */
817 public int getEndOffset() {
818 return elem.getEndOffset();
819 }
820
821 /**
822 * Fetches the structural portion of the subject that this
823 * view is mapped to. The view may not be responsible for the
824 * entire portion of the element.
825 *
826 * @return the subject
827 * @see View#getElement
828 */
829 public Element getElement() {
830 return elem;
831 }
832
833 /**
834 * Fetch a <code>Graphics</code> for rendering.
835 * This can be used to determine
836 * font characteristics, and will be different for a print view
837 * than a component view.
838 *
839 * @return a <code>Graphics</code> object for rendering
840 * @since 1.3
841 */
842 public Graphics getGraphics() {
843 // PENDING(prinz) this is a temporary implementation
844 Component c = getContainer();
845 return c.getGraphics();
846 }
847
848 /**
849 * Fetches the attributes to use when rendering. By default
850 * this simply returns the attributes of the associated element.
851 * This method should be used rather than using the element
852 * directly to obtain access to the attributes to allow
853 * view-specific attributes to be mixed in or to allow the
854 * view to have view-specific conversion of attributes by
855 * subclasses.
856 * Each view should document what attributes it recognizes
857 * for the purpose of rendering or layout, and should always
858 * access them through the <code>AttributeSet</code> returned
859 * by this method.
860 */
861 public AttributeSet getAttributes() {
862 return elem.getAttributes();
863 }
864
865 /**
866 * Tries to break this view on the given axis. This is
867 * called by views that try to do formatting of their
868 * children. For example, a view of a paragraph will
869 * typically try to place its children into row and
870 * views representing chunks of text can sometimes be
871 * broken down into smaller pieces.
872 * <p>
873 * This is implemented to return the view itself, which
874 * represents the default behavior on not being
875 * breakable. If the view does support breaking, the
876 * starting offset of the view returned should be the
877 * given offset, and the end offset should be less than
878 * or equal to the end offset of the view being broken.
879 *
880 * @param axis may be either <code>View.X_AXIS</code> or
881 * <code>View.Y_AXIS</code>
882 * @param offset the location in the document model
883 * that a broken fragment would occupy >= 0. This
884 * would be the starting offset of the fragment
885 * returned
886 * @param pos the position along the axis that the
887 * broken view would occupy >= 0. This may be useful for
888 * things like tab calculations
889 * @param len specifies the distance along the axis
890 * where a potential break is desired >= 0
891 * @return the fragment of the view that represents the
892 * given span, if the view can be broken. If the view
893 * doesn't support breaking behavior, the view itself is
894 * returned.
895 * @see ParagraphView
896 */
897 public View breakView(int axis, int offset, float pos, float len) {
898 return this;
899 }
900
901 /**
902 * Creates a view that represents a portion of the element.
903 * This is potentially useful during formatting operations
904 * for taking measurements of fragments of the view. If
905 * the view doesn't support fragmenting (the default), it
906 * should return itself.
907 *
908 * @param p0 the starting offset >= 0. This should be a value
909 * greater or equal to the element starting offset and
910 * less than the element ending offset.
911 * @param p1 the ending offset > p0. This should be a value
912 * less than or equal to the elements end offset and
913 * greater than the elements starting offset.
914 * @return the view fragment, or itself if the view doesn't
915 * support breaking into fragments
916 * @see LabelView
917 */
918 public View createFragment(int p0, int p1) {
919 return this;
920 }
921
922 /**
923 * Determines how attractive a break opportunity in
924 * this view is. This can be used for determining which
925 * view is the most attractive to call <code>breakView</code>
926 * on in the process of formatting. A view that represents
927 * text that has whitespace in it might be more attractive
928 * than a view that has no whitespace, for example. The
929 * higher the weight, the more attractive the break. A
930 * value equal to or lower than <code>BadBreakWeight</code>
931 * should not be considered for a break. A value greater
932 * than or equal to <code>ForcedBreakWeight</code> should
933 * be broken.
934 * <p>
935 * This is implemented to provide the default behavior
936 * of returning <code>BadBreakWeight</code> unless the length
937 * is greater than the length of the view in which case the
938 * entire view represents the fragment. Unless a view has
939 * been written to support breaking behavior, it is not
940 * attractive to try and break the view. An example of
941 * a view that does support breaking is <code>LabelView</code>.
942 * An example of a view that uses break weight is
943 * <code>ParagraphView</code>.
944 *
945 * @param axis may be either <code>View.X_AXIS</code> or
946 * <code>View.Y_AXIS</code>
947 * @param pos the potential location of the start of the
948 * broken view >= 0. This may be useful for calculating tab
949 * positions
950 * @param len specifies the relative length from <em>pos</em>
951 * where a potential break is desired >= 0
952 * @return the weight, which should be a value between
953 * ForcedBreakWeight and BadBreakWeight
954 * @see LabelView
955 * @see ParagraphView
956 * @see #BadBreakWeight
957 * @see #GoodBreakWeight
958 * @see #ExcellentBreakWeight
959 * @see #ForcedBreakWeight
960 */
961 public int getBreakWeight(int axis, float pos, float len) {
962 if (len > getPreferredSpan(axis)) {
963 return GoodBreakWeight;
964 }
965 return BadBreakWeight;
966 }
967
968 /**
969 * Determines the resizability of the view along the
970 * given axis. A value of 0 or less is not resizable.
971 *
972 * @param axis may be either <code>View.X_AXIS</code> or
973 * <code>View.Y_AXIS</code>
974 * @return the weight
975 */
976 public int getResizeWeight(int axis) {
977 return 0;
978 }
979
980 /**
981 * Sets the size of the view. This should cause
982 * layout of the view along the given axis, if it
983 * has any layout duties.
984 *
985 * @param width the width >= 0
986 * @param height the height >= 0
987 */
988 public void setSize(float width, float height) {
989 }
990
991 /**
992 * Fetches the container hosting the view. This is useful for
993 * things like scheduling a repaint, finding out the host
994 * components font, etc. The default implementation
995 * of this is to forward the query to the parent view.
996 *
997 * @return the container, <code>null</code> if none
998 */
999 public Container getContainer() {
1000 View v = getParent();
1001 return (v != null) ? v.getContainer() : null;
1002 }
1003
1004 /**
1005 * Fetches the <code>ViewFactory</code> implementation that is feeding
1006 * the view hierarchy. Normally the views are given this
1007 * as an argument to updates from the model when they
1008 * are most likely to need the factory, but this
1009 * method serves to provide it at other times.
1010 *
1011 * @return the factory, <code>null</code> if none
1012 */
1013 public ViewFactory getViewFactory() {
1014 View v = getParent();
1015 return (v != null) ? v.getViewFactory() : null;
1016 }
1017
1018 /**
1019 * Returns the tooltip text at the specified location. The default
1020 * implementation returns the value from the child View identified by
1021 * the passed in location.
1022 *
1023 * @since 1.4
1024 * @see JTextComponent#getToolTipText
1025 */
1026 public String getToolTipText(float x, float y, Shape allocation) {
1027 int viewIndex = getViewIndex(x, y, allocation);
1028 if (viewIndex >= 0) {
1029 allocation = getChildAllocation(viewIndex, allocation);
1030 Rectangle rect = (allocation instanceof Rectangle) ?
1031 (Rectangle)allocation : allocation.getBounds();
1032 if (rect.contains(x, y)) {
1033 return getView(viewIndex).getToolTipText(x, y, allocation);
1034 }
1035 }
1036 return null;
1037 }
1038
1039 /**
1040 * Returns the child view index representing the given position in
1041 * the view. This iterates over all the children returning the
1042 * first with a bounds that contains <code>x</code>, <code>y</code>.
1043 *
1044 * @param x the x coordinate
1045 * @param y the y coordinate
1046 * @param allocation current allocation of the View.
1047 * @return index of the view representing the given location, or
1048 * -1 if no view represents that position
1049 * @since 1.4
1050 */
1051 public int getViewIndex(float x, float y, Shape allocation) {
1052 for (int counter = getViewCount() - 1; counter >= 0; counter--) {
1053 Shape childAllocation = getChildAllocation(counter, allocation);
1054
1055 if (childAllocation != null) {
1056 Rectangle rect = (childAllocation instanceof Rectangle) ?
1057 (Rectangle)childAllocation : childAllocation.getBounds();
1058
1059 if (rect.contains(x, y)) {
1060 return counter;
1061 }
1062 }
1063 }
1064 return -1;
1065 }
1066
1067 /**
1068 * Updates the child views in response to receiving notification
1069 * that the model changed, and there is change record for the
1070 * element this view is responsible for. This is implemented
1071 * to assume the child views are directly responsible for the
1072 * child elements of the element this view represents. The
1073 * <code>ViewFactory</code> is used to create child views for each element
1074 * specified as added in the <code>ElementChange</code>, starting at the
1075 * index specified in the given <code>ElementChange</code>. The number of
1076 * child views representing the removed elements specified are
1077 * removed.
1078 *
1079 * @param ec the change information for the element this view
1080 * is responsible for. This should not be <code>null</code> if
1081 * this method gets called
1082 * @param e the change information from the associated document
1083 * @param f the factory to use to build child views
1084 * @return whether or not the child views represent the
1085 * child elements of the element this view is responsible
1086 * for. Some views create children that represent a portion
1087 * of the element they are responsible for, and should return
1088 * false. This information is used to determine if views
1089 * in the range of the added elements should be forwarded to
1090 * or not
1091 * @see #insertUpdate
1092 * @see #removeUpdate
1093 * @see #changedUpdate
1094 * @since 1.3
1095 */
1096 protected boolean updateChildren(DocumentEvent.ElementChange ec,
1097 DocumentEvent e, ViewFactory f) {
1098 Element[] removedElems = ec.getChildrenRemoved();
1099 Element[] addedElems = ec.getChildrenAdded();
1100 View[] added = null;
1101 if (addedElems != null) {
1102 added = new View[addedElems.length];
1103 for (int i = 0; i < addedElems.length; i++) {
1104 added[i] = f.create(addedElems[i]);
1105 }
1106 }
1107 int nremoved = 0;
1108 int index = ec.getIndex();
1109 if (removedElems != null) {
1110 nremoved = removedElems.length;
1111 }
1112 replace(index, nremoved, added);
1113 return true;
1114 }
1115
1116 /**
1117 * Forwards the given <code>DocumentEvent</code> to the child views
1118 * that need to be notified of the change to the model.
1119 * If there were changes to the element this view is
1120 * responsible for, that should be considered when
1121 * forwarding (i.e. new child views should not get
1122 * notified).
1123 *
1124 * @param ec changes to the element this view is responsible
1125 * for (may be <code>null</code> if there were no changes).
1126 * @param e the change information from the associated document
1127 * @param a the current allocation of the view
1128 * @param f the factory to use to rebuild if the view has children
1129 * @see #insertUpdate
1130 * @see #removeUpdate
1131 * @see #changedUpdate
1132 * @since 1.3
1133 */
1134 protected void forwardUpdate(DocumentEvent.ElementChange ec,
1135 DocumentEvent e, Shape a, ViewFactory f) {
1136 Element elem = getElement();
1137 int pos = e.getOffset();
1138 int index0 = getViewIndex(pos, Position.Bias.Forward);
1139 if (index0 == -1 && e.getType() == DocumentEvent.EventType.REMOVE &&
1140 pos >= getEndOffset()) {
1141 // Event beyond our offsets. We may have represented this, that is
1142 // the remove may have removed one of our child Elements that
1143 // represented this, so, we should foward to last element.
1144 index0 = getViewCount() - 1;
1145 }
1146 int index1 = index0;
1147 View v = (index0 >= 0) ? getView(index0) : null;
1148 if (v != null) {
1149 if ((v.getStartOffset() == pos) && (pos > 0)) {
1150 // If v is at a boundary, forward the event to the previous
1151 // view too.
1152 index0 = Math.max(index0 - 1, 0);
1153 }
1154 }
1155 if (e.getType() != DocumentEvent.EventType.REMOVE) {
1156 index1 = getViewIndex(pos + e.getLength(), Position.Bias.Forward);
1157 if (index1 < 0) {
1158 index1 = getViewCount() - 1;
1159 }
1160 }
1161 int hole0 = index1 + 1;
1162 int hole1 = hole0;
1163 Element[] addedElems = (ec != null) ? ec.getChildrenAdded() : null;
1164 if ((addedElems != null) && (addedElems.length > 0)) {
1165 hole0 = ec.getIndex();
1166 hole1 = hole0 + addedElems.length - 1;
1167 }
1168
1169 // forward to any view not in the forwarding hole
1170 // formed by added elements (i.e. they will be updated
1171 // by initialization.
1172 index0 = Math.max(index0, 0);
1173 for (int i = index0; i <= index1; i++) {
1174 if (! ((i >= hole0) && (i <= hole1))) {
1175 v = getView(i);
1176 if (v != null) {
1177 Shape childAlloc = getChildAllocation(i, a);
1178 forwardUpdateToView(v, e, childAlloc, f);
1179 }
1180 }
1181 }
1182 }
1183
1184 /**
1185 * Forwards the <code>DocumentEvent</code> to the give child view. This
1186 * simply messages the view with a call to <code>insertUpdate</code>,
1187 * <code>removeUpdate</code>, or <code>changedUpdate</code> depending
1188 * upon the type of the event. This is called by
1189 * <a href="#forwardUpdate">forwardUpdate</a> to forward
1190 * the event to children that need it.
1191 *
1192 * @param v the child view to forward the event to
1193 * @param e the change information from the associated document
1194 * @param a the current allocation of the view
1195 * @param f the factory to use to rebuild if the view has children
1196 * @see #forwardUpdate
1197 * @since 1.3
1198 */
1199 protected void forwardUpdateToView(View v, DocumentEvent e,
1200 Shape a, ViewFactory f) {
1201 DocumentEvent.EventType type = e.getType();
1202 if (type == DocumentEvent.EventType.INSERT) {
1203 v.insertUpdate(e, a, f);
1204 } else if (type == DocumentEvent.EventType.REMOVE) {
1205 v.removeUpdate(e, a, f);
1206 } else {
1207 v.changedUpdate(e, a, f);
1208 }
1209 }
1210
1211 /**
1212 * Updates the layout in response to receiving notification of
1213 * change from the model. This is implemented to call
1214 * <code>preferenceChanged</code> to reschedule a new layout
1215 * if the <code>ElementChange</code> record is not <code>null</code>.
1216 *
1217 * @param ec changes to the element this view is responsible
1218 * for (may be <code>null</code> if there were no changes)
1219 * @param e the change information from the associated document
1220 * @param a the current allocation of the view
1221 * @see #insertUpdate
1222 * @see #removeUpdate
1223 * @see #changedUpdate
1224 * @since 1.3
1225 */
1226 protected void updateLayout(DocumentEvent.ElementChange ec,
1227 DocumentEvent e, Shape a) {
1228 if ((ec != null) && (a != null)) {
1229 // should damage more intelligently
1230 preferenceChanged(null, true, true);
1231 Container host = getContainer();
1232 if (host != null) {
1233 host.repaint();
1234 }
1235 }
1236 }
1237
1238 /**
1239 * The weight to indicate a view is a bad break
1240 * opportunity for the purpose of formatting. This
1241 * value indicates that no attempt should be made to
1242 * break the view into fragments as the view has
1243 * not been written to support fragmenting.
1244 *
1245 * @see #getBreakWeight
1246 * @see #GoodBreakWeight
1247 * @see #ExcellentBreakWeight
1248 * @see #ForcedBreakWeight
1249 */
1250 public static final int BadBreakWeight = 0;
1251
1252 /**
1253 * The weight to indicate a view supports breaking,
1254 * but better opportunities probably exist.
1255 *
1256 * @see #getBreakWeight
1257 * @see #BadBreakWeight
1258 * @see #ExcellentBreakWeight
1259 * @see #ForcedBreakWeight
1260 */
1261 public static final int GoodBreakWeight = 1000;
1262
1263 /**
1264 * The weight to indicate a view supports breaking,
1265 * and this represents a very attractive place to
1266 * break.
1267 *
1268 * @see #getBreakWeight
1269 * @see #BadBreakWeight
1270 * @see #GoodBreakWeight
1271 * @see #ForcedBreakWeight
1272 */
1273 public static final int ExcellentBreakWeight = 2000;
1274
1275 /**
1276 * The weight to indicate a view supports breaking,
1277 * and must be broken to be represented properly
1278 * when placed in a view that formats its children
1279 * by breaking them.
1280 *
1281 * @see #getBreakWeight
1282 * @see #BadBreakWeight
1283 * @see #GoodBreakWeight
1284 * @see #ExcellentBreakWeight
1285 */
1286 public static final int ForcedBreakWeight = 3000;
1287
1288 /**
1289 * Axis for format/break operations.
1290 */
1291 public static final int X_AXIS = HORIZONTAL;
1292
1293 /**
1294 * Axis for format/break operations.
1295 */
1296 public static final int Y_AXIS = VERTICAL;
1297
1298 /**
1299 * Provides a mapping from the document model coordinate space
1300 * to the coordinate space of the view mapped to it. This is
1301 * implemented to default the bias to <code>Position.Bias.Forward</code>
1302 * which was previously implied.
1303 *
1304 * @param pos the position to convert >= 0
1305 * @param a the allocated region in which to render
1306 * @return the bounding box of the given position is returned
1307 * @exception BadLocationException if the given position does
1308 * not represent a valid location in the associated document
1309 * @see View#modelToView
1310 * @deprecated
1311 */
1312 @Deprecated
1313 public Shape modelToView(int pos, Shape a) throws BadLocationException {
1314 return modelToView(pos, a, Position.Bias.Forward);
1315 }
1316
1317
1318 /**
1319 * Provides a mapping from the view coordinate space to the logical
1320 * coordinate space of the model.
1321 *
1322 * @param x the X coordinate >= 0
1323 * @param y the Y coordinate >= 0
1324 * @param a the allocated region in which to render
1325 * @return the location within the model that best represents the
1326 * given point in the view >= 0
1327 * @see View#viewToModel
1328 * @deprecated
1329 */
1330 @Deprecated
1331 public int viewToModel(float x, float y, Shape a) {
1332 sharedBiasReturn[0] = Position.Bias.Forward;
1333 return viewToModel(x, y, a, sharedBiasReturn);
1334 }
1335
1336 // static argument available for viewToModel calls since only
1337 // one thread at a time may call this method.
1338 static final Position.Bias[] sharedBiasReturn = new Position.Bias[1];
1339
1340 private View parent;
1341 private Element elem;
1342
1343 };