1 /* View.java -- 2 Copyright (C) 2002, 2004, 2005, 2006 Free Software Foundation, Inc. 3 4 This file is part of GNU Classpath. 5 6 GNU Classpath is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2, or (at your option) 9 any later version. 10 11 GNU Classpath is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with GNU Classpath; see the file COPYING. If not, write to the 18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19 02110-1301 USA. 20 21 Linking this library statically or dynamically with other modules is 22 making a combined work based on this library. Thus, the terms and 23 conditions of the GNU General Public License cover the whole 24 combination. 25 26 As a special exception, the copyright holders of this library give you 27 permission to link this library with independent modules to produce an 28 executable, regardless of the license terms of these independent 29 modules, and to copy and distribute the resulting executable under 30 terms of your choice, provided that you also meet, for each linked 31 independent module, the terms and conditions of the license of that 32 module. An independent module is a module which is not derived from 33 or based on this library. If you modify this library, you may extend 34 this exception to your version of the library, but you are not 35 obligated to do so. If you do not wish to do so, delete this 36 exception statement from your version. */ 37 38 39 package javax.swing.text; 40 41 import java.awt.Container; 42 import java.awt.Graphics; 43 import java.awt.Rectangle; 44 import java.awt.Shape; 45 46 import javax.swing.SwingConstants; 47 import javax.swing.SwingUtilities; 48 import javax.swing.event.DocumentEvent; 49 50 public abstract class View implements SwingConstants 51 { 52 public static final int BadBreakWeight = 0; 53 public static final int ExcellentBreakWeight = 2000; 54 public static final int ForcedBreakWeight = 3000; 55 public static final int GoodBreakWeight = 1000; 56 57 public static final int X_AXIS = 0; 58 public static final int Y_AXIS = 1; 59 60 private Element elt; 61 private View parent; 62 63 /** 64 * Creates a new <code>View</code> instance. 65 * 66 * @param elem an <code>Element</code> value 67 */ View(Element elem)68 public View(Element elem) 69 { 70 elt = elem; 71 } 72 paint(Graphics g, Shape s)73 public abstract void paint(Graphics g, Shape s); 74 75 /** 76 * Sets the parent for this view. This is the first method that is beeing 77 * called on a view to setup the view hierarchy. This is also the last method 78 * beeing called when the view is disconnected from the view hierarchy, in 79 * this case <code>parent</code> is null. 80 * 81 * If <code>parent</code> is <code>null</code>, a call to this method also 82 * calls <code>setParent</code> on the children, thus disconnecting them from 83 * the view hierarchy. That means that super must be called when this method 84 * is overridden. 85 * 86 * @param parent the parent to set, <code>null</code> when this view is 87 * beeing disconnected from the view hierarchy 88 */ setParent(View parent)89 public void setParent(View parent) 90 { 91 if (parent == null) 92 { 93 int numChildren = getViewCount(); 94 for (int i = 0; i < numChildren; i++) 95 { 96 View child = getView(i); 97 // It is important that we only reset the parent on views that 98 // actually belong to us. In FlowView the child may already be 99 // reparented. 100 if (child.getParent() == this) 101 child.setParent(null); 102 } 103 } 104 105 this.parent = parent; 106 } 107 getParent()108 public View getParent() 109 { 110 return parent; 111 } 112 getContainer()113 public Container getContainer() 114 { 115 View parent = getParent(); 116 if (parent == null) 117 return null; 118 else 119 return parent.getContainer(); 120 } 121 getDocument()122 public Document getDocument() 123 { 124 return getElement().getDocument(); 125 } 126 getElement()127 public Element getElement() 128 { 129 return elt; 130 } 131 132 /** 133 * Returns the preferred span along the specified axis. Normally the view is 134 * rendered with the span returned here if that is possible. 135 * 136 * @param axis the axis 137 * 138 * @return the preferred span along the specified axis 139 */ getPreferredSpan(int axis)140 public abstract float getPreferredSpan(int axis); 141 142 /** 143 * Returns the resize weight of this view. A value of <code>0</code> or less 144 * means this view is not resizeable. Positive values make the view 145 * resizeable. The default implementation returns <code>0</code> 146 * unconditionally. 147 * 148 * @param axis the axis 149 * 150 * @return the resizability of this view along the specified axis 151 */ getResizeWeight(int axis)152 public int getResizeWeight(int axis) 153 { 154 return 0; 155 } 156 157 /** 158 * Returns the maximum span along the specified axis. The default 159 * implementation will forward to 160 * {@link #getPreferredSpan(int)} unless {@link #getResizeWeight(int)} 161 * returns a value > 0, in which case this returns {@link Integer#MIN_VALUE}. 162 * 163 * @param axis the axis 164 * 165 * @return the maximum span along the specified axis 166 */ getMaximumSpan(int axis)167 public float getMaximumSpan(int axis) 168 { 169 float max = Integer.MAX_VALUE; 170 if (getResizeWeight(axis) <= 0) 171 max = getPreferredSpan(axis); 172 return max; 173 } 174 175 /** 176 * Returns the minimum span along the specified axis. The default 177 * implementation will forward to 178 * {@link #getPreferredSpan(int)} unless {@link #getResizeWeight(int)} 179 * returns a value > 0, in which case this returns <code>0</code>. 180 * 181 * @param axis the axis 182 * 183 * @return the minimum span along the specified axis 184 */ getMinimumSpan(int axis)185 public float getMinimumSpan(int axis) 186 { 187 float min = 0; 188 if (getResizeWeight(axis) <= 0) 189 min = getPreferredSpan(axis); 190 return min; 191 } 192 setSize(float width, float height)193 public void setSize(float width, float height) 194 { 195 // The default implementation does nothing. 196 } 197 198 /** 199 * Returns the alignment of this view along the baseline of the parent view. 200 * An alignment of <code>0.0</code> will align this view with the left edge 201 * along the baseline, an alignment of <code>0.5</code> will align it 202 * centered to the baseline, an alignment of <code>1.0</code> will align 203 * the right edge along the baseline. 204 * 205 * The default implementation returns 0.5 unconditionally. 206 * 207 * @param axis the axis 208 * 209 * @return the alignment of this view along the parents baseline for the 210 * specified axis 211 */ getAlignment(int axis)212 public float getAlignment(int axis) 213 { 214 return 0.5f; 215 } 216 getAttributes()217 public AttributeSet getAttributes() 218 { 219 return getElement().getAttributes(); 220 } 221 isVisible()222 public boolean isVisible() 223 { 224 return true; 225 } 226 getViewCount()227 public int getViewCount() 228 { 229 return 0; 230 } 231 getView(int index)232 public View getView(int index) 233 { 234 return null; 235 } 236 getViewFactory()237 public ViewFactory getViewFactory() 238 { 239 View parent = getParent(); 240 return parent != null ? parent.getViewFactory() : null; 241 } 242 243 /** 244 * Replaces a couple of child views with new child views. If 245 * <code>length == 0</code> then this is a simple insertion, if 246 * <code>views == null</code> this only removes some child views. 247 * 248 * @param offset the offset at which to replace 249 * @param length the number of child views to be removed 250 * @param views the new views to be inserted, may be <code>null</code> 251 */ replace(int offset, int length, View[] views)252 public void replace(int offset, int length, View[] views) 253 { 254 // Default implementation does nothing. 255 } 256 insert(int offset, View view)257 public void insert(int offset, View view) 258 { 259 View[] array = { view }; 260 replace(offset, 1, array); 261 } 262 append(View view)263 public void append(View view) 264 { 265 View[] array = { view }; 266 int offset = getViewCount(); 267 replace(offset, 0, array); 268 } 269 removeAll()270 public void removeAll() 271 { 272 replace(0, getViewCount(), null); 273 } 274 remove(int index)275 public void remove(int index) 276 { 277 replace(index, 1, null); 278 } 279 createFragment(int p0, int p1)280 public View createFragment(int p0, int p1) 281 { 282 // The default implementation doesn't support fragmentation. 283 return this; 284 } 285 getStartOffset()286 public int getStartOffset() 287 { 288 return getElement().getStartOffset(); 289 } 290 getEndOffset()291 public int getEndOffset() 292 { 293 return getElement().getEndOffset(); 294 } 295 getChildAllocation(int index, Shape a)296 public Shape getChildAllocation(int index, Shape a) 297 { 298 return null; 299 } 300 301 /** 302 * @since 1.4 303 */ getViewIndex(float x, float y, Shape allocation)304 public int getViewIndex(float x, float y, Shape allocation) 305 { 306 return -1; 307 } 308 309 /** 310 * @since 1.4 311 */ getToolTipText(float x, float y, Shape allocation)312 public String getToolTipText(float x, float y, Shape allocation) 313 { 314 int index = getViewIndex(x, y, allocation); 315 316 String text = null; 317 if (index >= 0) 318 { 319 allocation = getChildAllocation(index, allocation); 320 Rectangle r = allocation instanceof Rectangle ? (Rectangle) allocation 321 : allocation.getBounds(); 322 if (r.contains(x, y)) 323 text = getView(index).getToolTipText(x, y, allocation); 324 } 325 return text; 326 } 327 328 /** 329 * @since 1.3 330 */ getGraphics()331 public Graphics getGraphics() 332 { 333 return getContainer().getGraphics(); 334 } 335 preferenceChanged(View child, boolean width, boolean height)336 public void preferenceChanged(View child, boolean width, boolean height) 337 { 338 View p = getParent(); 339 if (p != null) 340 p.preferenceChanged(this, width, height); 341 } 342 getBreakWeight(int axis, float pos, float len)343 public int getBreakWeight(int axis, float pos, float len) 344 { 345 int weight = BadBreakWeight; 346 if (len > getPreferredSpan(axis)) 347 weight = GoodBreakWeight; 348 return weight; 349 } 350 breakView(int axis, int offset, float pos, float len)351 public View breakView(int axis, int offset, float pos, float len) 352 { 353 return this; 354 } 355 356 /** 357 * @since 1.3 358 */ getViewIndex(int pos, Position.Bias b)359 public int getViewIndex(int pos, Position.Bias b) 360 { 361 return -1; 362 } 363 364 /** 365 * Receive notification about an insert update to the text model. 366 * 367 * The default implementation of this method does the following: 368 * <ul> 369 * <li>Call {@link #updateChildren} if the element that this view is 370 * responsible for has changed. This makes sure that the children can 371 * correctly represent the model.<li> 372 * <li>Call {@link #forwardUpdate}. This forwards the DocumentEvent to 373 * the child views.<li> 374 * <li>Call {@link #updateLayout}. Gives the view a chance to either 375 * repair its layout, reschedule layout or do nothing at all.</li> 376 * </ul> 377 * 378 * @param ev the DocumentEvent that describes the change 379 * @param shape the shape of the view 380 * @param vf the ViewFactory for creating child views 381 */ insertUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)382 public void insertUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) 383 { 384 if (getViewCount() > 0) 385 { 386 Element el = getElement(); 387 DocumentEvent.ElementChange ec = ev.getChange(el); 388 if (ec != null) 389 { 390 if (! updateChildren(ec, ev, vf)) 391 ec = null; 392 } 393 forwardUpdate(ec, ev, shape, vf); 394 updateLayout(ec, ev, shape); 395 } 396 } 397 398 /** 399 * Receive notification about a remove update to the text model. 400 * 401 * The default implementation of this method does the following: 402 * <ul> 403 * <li>Call {@link #updateChildren} if the element that this view is 404 * responsible for has changed. This makes sure that the children can 405 * correctly represent the model.<li> 406 * <li>Call {@link #forwardUpdate}. This forwards the DocumentEvent to 407 * the child views.<li> 408 * <li>Call {@link #updateLayout}. Gives the view a chance to either 409 * repair its layout, reschedule layout or do nothing at all.</li> 410 * </ul> 411 * 412 * @param ev the DocumentEvent that describes the change 413 * @param shape the shape of the view 414 * @param vf the ViewFactory for creating child views 415 */ removeUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)416 public void removeUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) 417 { 418 Element el = getElement(); 419 DocumentEvent.ElementChange ec = ev.getChange(el); 420 if (ec != null) 421 { 422 if (! updateChildren(ec, ev, vf)) 423 ec = null; 424 } 425 forwardUpdate(ec, ev, shape, vf); 426 updateLayout(ec, ev, shape); 427 } 428 429 /** 430 * Receive notification about a change update to the text model. 431 * 432 * The default implementation of this method does the following: 433 * <ul> 434 * <li>Call {@link #updateChildren} if the element that this view is 435 * responsible for has changed. This makes sure that the children can 436 * correctly represent the model.<li> 437 * <li>Call {@link #forwardUpdate}. This forwards the DocumentEvent to 438 * the child views.<li> 439 * <li>Call {@link #updateLayout}. Gives the view a chance to either 440 * repair its layout, reschedule layout or do nothing at all.</li> 441 * </ul> 442 * 443 * @param ev the DocumentEvent that describes the change 444 * @param shape the shape of the view 445 * @param vf the ViewFactory for creating child views 446 */ changedUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)447 public void changedUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) 448 { 449 if (getViewCount() > 0) 450 { 451 Element el = getElement(); 452 DocumentEvent.ElementChange ec = ev.getChange(el); 453 if (ec != null) 454 { 455 if (! updateChildren(ec, ev, vf)) 456 ec = null; 457 } 458 forwardUpdate(ec, ev, shape, vf); 459 updateLayout(ec, ev, shape); 460 } 461 } 462 463 /** 464 * Updates the list of children that is returned by {@link #getView} 465 * and {@link #getViewCount}. 466 * 467 * Element that are specified as beeing added in the ElementChange record are 468 * assigned a view for using the ViewFactory. Views of Elements that 469 * are specified as beeing removed are removed from the list. 470 * 471 * @param ec the ElementChange record that describes the change of the 472 * element 473 * @param ev the DocumentEvent describing the change of the document model 474 * @param vf the ViewFactory to use for creating new views 475 * 476 * @return whether or not the child views represent the child elements of 477 * the element that this view is responsible for. Some views may 478 * create views that are responsible only for parts of the element 479 * that they are responsible for and should then return false. 480 * 481 * @since 1.3 482 */ updateChildren(DocumentEvent.ElementChange ec, DocumentEvent ev, ViewFactory vf)483 protected boolean updateChildren(DocumentEvent.ElementChange ec, 484 DocumentEvent ev, 485 ViewFactory vf) 486 { 487 Element[] added = ec.getChildrenAdded(); 488 Element[] removed = ec.getChildrenRemoved(); 489 int index = ec.getIndex(); 490 491 View[] newChildren = null; 492 if (added != null) 493 { 494 newChildren = new View[added.length]; 495 for (int i = 0; i < added.length; ++i) 496 newChildren[i] = vf.create(added[i]); 497 } 498 int numRemoved = removed != null ? removed.length : 0; 499 replace(index, numRemoved, newChildren); 500 501 return true; 502 } 503 504 /** 505 * Forwards the DocumentEvent to child views that need to get notified 506 * of the change to the model. This calles {@link #forwardUpdateToView} 507 * for each View that must be forwarded to. 508 * 509 * If <code>ec</code> is not <code>null</code> (this means there have been 510 * structural changes to the element that this view is responsible for) this 511 * method should recognize this and don't notify newly added child views. 512 * 513 * @param ec the ElementChange describing the element changes (may be 514 * <code>null</code> if there were no changes) 515 * @param ev the DocumentEvent describing the changes to the model 516 * @param shape the current allocation of the view 517 * @param vf the ViewFactory used to create new Views 518 * 519 * @since 1.3 520 */ forwardUpdate(DocumentEvent.ElementChange ec, DocumentEvent ev, Shape shape, ViewFactory vf)521 protected void forwardUpdate(DocumentEvent.ElementChange ec, 522 DocumentEvent ev, Shape shape, ViewFactory vf) 523 { 524 int count = getViewCount(); 525 if (count > 0) 526 { 527 // Determine start index. 528 int startOffset = ev.getOffset(); 529 int startIndex = getViewIndex(startOffset, Position.Bias.Backward); 530 531 // For REMOVE events we have to forward the event to the last element, 532 // for the case that an Element has been removed that represente 533 // the offset. 534 if (startIndex == -1 && ev.getType() == DocumentEvent.EventType.REMOVE 535 && startOffset >= getEndOffset()) 536 { 537 startIndex = getViewCount() - 1; 538 } 539 540 // When startIndex is on a view boundary, forward event to the 541 // previous view too. 542 if (startIndex >= 0) 543 { 544 View v = getView(startIndex); 545 if (v != null) 546 { 547 if (v.getStartOffset() == startOffset && startOffset > 0) 548 startIndex = Math.max(0, startIndex - 1); 549 } 550 } 551 startIndex = Math.max(0, startIndex); 552 553 // Determine end index. 554 int endIndex = startIndex; 555 if (ev.getType() != DocumentEvent.EventType.REMOVE) 556 { 557 endIndex = getViewIndex(startOffset + ev.getLength(), 558 Position.Bias.Forward); 559 if (endIndex < 0) 560 endIndex = getViewCount() - 1; 561 } 562 563 // Determine hole that comes from added elements (we don't forward 564 // the event to newly added views. 565 int startAdded = endIndex + 1; 566 int endAdded = startAdded; 567 Element[] added = (ec != null) ? ec.getChildrenAdded() : null; 568 if (added != null && added.length > 0) 569 { 570 startAdded = ec.getIndex(); 571 endAdded = startAdded + added.length - 1; 572 } 573 574 // Forward event to all views between startIndex and endIndex, 575 // and leave out all views in the hole. 576 for (int i = startIndex; i <= endIndex; i++) 577 { 578 // Skip newly added child views. 579 if (! (i >= startAdded && i <= endAdded)) 580 { 581 View child = getView(i); 582 if (child != null) 583 { 584 Shape childAlloc = getChildAllocation(i, shape); 585 forwardUpdateToView(child, ev, childAlloc, vf); 586 } 587 } 588 } 589 } 590 } 591 592 /** 593 * Forwards an update event to the given child view. This calls 594 * {@link #insertUpdate}, {@link #removeUpdate} or {@link #changedUpdate}, 595 * depending on the type of document event. 596 * 597 * @param view the View to forward the event to 598 * @param ev the DocumentEvent to forward 599 * @param shape the current allocation of the View 600 * @param vf the ViewFactory used to create new Views 601 * 602 * @since 1.3 603 */ forwardUpdateToView(View view, DocumentEvent ev, Shape shape, ViewFactory vf)604 protected void forwardUpdateToView(View view, DocumentEvent ev, Shape shape, 605 ViewFactory vf) 606 { 607 DocumentEvent.EventType type = ev.getType(); 608 if (type == DocumentEvent.EventType.INSERT) 609 view.insertUpdate(ev, shape, vf); 610 else if (type == DocumentEvent.EventType.REMOVE) 611 view.removeUpdate(ev, shape, vf); 612 else if (type == DocumentEvent.EventType.CHANGE) 613 view.changedUpdate(ev, shape, vf); 614 } 615 616 /** 617 * Updates the layout. 618 * 619 * @param ec the ElementChange that describes the changes to the element 620 * @param ev the DocumentEvent that describes the changes to the model 621 * @param shape the current allocation for this view 622 * 623 * @since 1.3 624 */ updateLayout(DocumentEvent.ElementChange ec, DocumentEvent ev, Shape shape)625 protected void updateLayout(DocumentEvent.ElementChange ec, 626 DocumentEvent ev, Shape shape) 627 { 628 if (ec != null && shape != null) 629 { 630 preferenceChanged(null, true, true); 631 Container c = getContainer(); 632 if (c != null) 633 c.repaint(); 634 } 635 } 636 637 /** 638 * Maps a position in the document into the coordinate space of the View. 639 * The output rectangle usually reflects the font height but has a width 640 * of zero. 641 * 642 * @param pos the position of the character in the model 643 * @param a the area that is occupied by the view 644 * @param b either {@link Position.Bias#Forward} or 645 * {@link Position.Bias#Backward} depending on the preferred 646 * direction bias. If <code>null</code> this defaults to 647 * <code>Position.Bias.Forward</code> 648 * 649 * @return a rectangle that gives the location of the document position 650 * inside the view coordinate space 651 * 652 * @throws BadLocationException if <code>pos</code> is invalid 653 * @throws IllegalArgumentException if b is not one of the above listed 654 * valid values 655 */ modelToView(int pos, Shape a, Position.Bias b)656 public abstract Shape modelToView(int pos, Shape a, Position.Bias b) 657 throws BadLocationException; 658 659 /** 660 * Maps a region in the document into the coordinate space of the View. 661 * 662 * @param p1 the beginning position inside the document 663 * @param b1 the direction bias for the beginning position 664 * @param p2 the end position inside the document 665 * @param b2 the direction bias for the end position 666 * @param a the area that is occupied by the view 667 * 668 * @return a rectangle that gives the span of the document region 669 * inside the view coordinate space 670 * 671 * @throws BadLocationException if <code>p1</code> or <code>p2</code> are 672 * invalid 673 * @throws IllegalArgumentException if b1 or b2 is not one of the above 674 * listed valid values 675 */ modelToView(int p1, Position.Bias b1, int p2, Position.Bias b2, Shape a)676 public Shape modelToView(int p1, Position.Bias b1, 677 int p2, Position.Bias b2, Shape a) 678 throws BadLocationException 679 { 680 if (b1 != Position.Bias.Forward && b1 != Position.Bias.Backward) 681 throw new IllegalArgumentException 682 ("b1 must be either Position.Bias.Forward or Position.Bias.Backward"); 683 if (b2 != Position.Bias.Forward && b2 != Position.Bias.Backward) 684 throw new IllegalArgumentException 685 ("b2 must be either Position.Bias.Forward or Position.Bias.Backward"); 686 687 Shape s1 = modelToView(p1, a, b1); 688 // Special case for p2 == end index. 689 Shape s2; 690 if (p2 != getEndOffset()) 691 { 692 s2 = modelToView(p2, a, b2); 693 } 694 else 695 { 696 try 697 { 698 s2 = modelToView(p2, a, b2); 699 } 700 catch (BadLocationException ex) 701 { 702 // Assume the end rectangle to be at the right edge of the 703 // view. 704 Rectangle aRect = a instanceof Rectangle ? (Rectangle) a 705 : a.getBounds(); 706 s2 = new Rectangle(aRect.x + aRect.width - 1, aRect.y, 1, 707 aRect.height); 708 } 709 } 710 711 // Need to modify the rectangle, so we create a copy in all cases. 712 Rectangle r1 = s1.getBounds(); 713 Rectangle r2 = s2 instanceof Rectangle ? (Rectangle) s2 714 : s2.getBounds(); 715 716 // For multiline view, let the resulting rectangle span the whole view. 717 if (r1.y != r2.y) 718 { 719 Rectangle aRect = a instanceof Rectangle ? (Rectangle) a 720 : a.getBounds(); 721 r1.x = aRect.x; 722 r1.width = aRect.width; 723 } 724 725 return SwingUtilities.computeUnion(r2.x, r2.y, r2.width, r2.height, r1); 726 } 727 728 /** 729 * Maps a position in the document into the coordinate space of the View. 730 * The output rectangle usually reflects the font height but has a width 731 * of zero. 732 * 733 * This method is deprecated and calls 734 * {@link #modelToView(int, Position.Bias, int, Position.Bias, Shape)} with 735 * a bias of {@link Position.Bias#Forward}. 736 * 737 * @param pos the position of the character in the model 738 * @param a the area that is occupied by the view 739 * 740 * @return a rectangle that gives the location of the document position 741 * inside the view coordinate space 742 * 743 * @throws BadLocationException if <code>pos</code> is invalid 744 * 745 * @deprecated Use {@link #modelToView(int, Shape, Position.Bias)} instead. 746 */ modelToView(int pos, Shape a)747 public Shape modelToView(int pos, Shape a) throws BadLocationException 748 { 749 return modelToView(pos, a, Position.Bias.Forward); 750 } 751 752 /** 753 * Maps coordinates from the <code>View</code>'s space into a position 754 * in the document model. 755 * 756 * @param x the x coordinate in the view space 757 * @param y the y coordinate in the view space 758 * @param a the allocation of this <code>View</code> 759 * @param b the bias to use 760 * 761 * @return the position in the document that corresponds to the screen 762 * coordinates <code>x, y</code> 763 */ viewToModel(float x, float y, Shape a, Position.Bias[] b)764 public abstract int viewToModel(float x, float y, Shape a, Position.Bias[] b); 765 766 /** 767 * Maps coordinates from the <code>View</code>'s space into a position 768 * in the document model. This method is deprecated and only there for 769 * compatibility. 770 * 771 * @param x the x coordinate in the view space 772 * @param y the y coordinate in the view space 773 * @param a the allocation of this <code>View</code> 774 * 775 * @return the position in the document that corresponds to the screen 776 * coordinates <code>x, y</code> 777 * 778 * @deprecated Use {@link #viewToModel(float, float, Shape, Position.Bias[])} 779 * instead. 780 */ viewToModel(float x, float y, Shape a)781 public int viewToModel(float x, float y, Shape a) 782 { 783 Position.Bias[] biasRet = new Position.Bias[1]; 784 biasRet[0] = Position.Bias.Forward; 785 return viewToModel(x, y, a, biasRet); 786 } 787 788 /** 789 * Dumps the complete View hierarchy. This method can be used for debugging 790 * purposes. 791 */ dump()792 protected void dump() 793 { 794 // Climb up the hierarchy to the parent. 795 View parent = getParent(); 796 if (parent != null) 797 parent.dump(); 798 else 799 dump(0); 800 } 801 802 /** 803 * Dumps the view hierarchy below this View with the specified indentation 804 * level. 805 * 806 * @param indent the indentation level to be used for this view 807 */ dump(int indent)808 void dump(int indent) 809 { 810 for (int i = 0; i < indent; ++i) 811 System.out.print('.'); 812 System.out.println(this + "(" + getStartOffset() + "," + getEndOffset() + ": " + getElement()); 813 814 int count = getViewCount(); 815 for (int i = 0; i < count; ++i) 816 getView(i).dump(indent + 1); 817 } 818 819 /** 820 * Returns the document position that is (visually) nearest to the given 821 * document position <code>pos</code> in the given direction <code>d</code>. 822 * 823 * @param pos the document position 824 * @param b the bias for <code>pos</code> 825 * @param a the allocation for this view 826 * @param d the direction, must be either {@link SwingConstants#NORTH}, 827 * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or 828 * {@link SwingConstants#EAST} 829 * @param biasRet an array of {@link Position.Bias} that can hold at least 830 * one element, which is filled with the bias of the return position 831 * on method exit 832 * 833 * @return the document position that is (visually) nearest to the given 834 * document position <code>pos</code> in the given direction 835 * <code>d</code> 836 * 837 * @throws BadLocationException if <code>pos</code> is not a valid offset in 838 * the document model 839 * @throws IllegalArgumentException if <code>d</code> is not a valid direction 840 */ getNextVisualPositionFrom(int pos, Position.Bias b, Shape a, int d, Position.Bias[] biasRet)841 public int getNextVisualPositionFrom(int pos, Position.Bias b, 842 Shape a, int d, 843 Position.Bias[] biasRet) 844 throws BadLocationException 845 { 846 int ret = pos; 847 Rectangle r; 848 View parent; 849 850 switch (d) 851 { 852 case EAST: 853 // TODO: take component orientation into account? 854 // Note: If pos is below zero the implementation will return 855 // pos + 1 regardless of whether that value is a correct offset 856 // in the document model. However this is what the RI does. 857 ret = Math.min(pos + 1, getEndOffset()); 858 break; 859 case WEST: 860 // TODO: take component orientation into account? 861 ret = Math.max(pos - 1, getStartOffset()); 862 break; 863 case NORTH: 864 // Try to find a suitable offset by examining the area above. 865 parent = getParent(); 866 r = parent.modelToView(pos, a, b).getBounds(); 867 ret = parent.viewToModel(r.x, r.y - 1, a, biasRet); 868 break; 869 case SOUTH: 870 // Try to find a suitable offset by examining the area below. 871 parent = getParent(); 872 r = parent.modelToView(pos, a, b).getBounds(); 873 ret = parent.viewToModel(r.x + r.width, r.y + r.height, a, biasRet); 874 break; 875 default: 876 throw new IllegalArgumentException("Illegal value for d"); 877 } 878 879 return ret; 880 } 881 } 882