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