001 /* FlowView.java -- A composite View 002 Copyright (C) 2005 Free Software Foundation, Inc. 003 004 This file is part of GNU Classpath. 005 006 GNU Classpath is free software; you can redistribute it and/or modify 007 it under the terms of the GNU General Public License as published by 008 the Free Software Foundation; either version 2, or (at your option) 009 any later version. 010 011 GNU Classpath is distributed in the hope that it will be useful, but 012 WITHOUT ANY WARRANTY; without even the implied warranty of 013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 General Public License for more details. 015 016 You should have received a copy of the GNU General Public License 017 along with GNU Classpath; see the file COPYING. If not, write to the 018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 019 02110-1301 USA. 020 021 Linking this library statically or dynamically with other modules is 022 making a combined work based on this library. Thus, the terms and 023 conditions of the GNU General Public License cover the whole 024 combination. 025 026 As a special exception, the copyright holders of this library give you 027 permission to link this library with independent modules to produce an 028 executable, regardless of the license terms of these independent 029 modules, and to copy and distribute the resulting executable under 030 terms of your choice, provided that you also meet, for each linked 031 independent module, the terms and conditions of the license of that 032 module. An independent module is a module which is not derived from 033 or based on this library. If you modify this library, you may extend 034 this exception to your version of the library, but you are not 035 obligated to do so. If you do not wish to do so, delete this 036 exception statement from your version. */ 037 038 039 package javax.swing.text; 040 041 import java.awt.Component; 042 import java.awt.Graphics; 043 import java.awt.Rectangle; 044 import java.awt.Shape; 045 046 import javax.swing.SizeRequirements; 047 import javax.swing.event.DocumentEvent; 048 049 /** 050 * A <code>View</code> that can flows it's children into it's layout space. 051 * 052 * The <code>FlowView</code> manages a set of logical views (that are 053 * the children of the {@link #layoutPool} field). These are translated 054 * at layout time into a set of physical views. These are the views that 055 * are managed as the real child views. Each of these child views represents 056 * a row and are laid out within a box using the superclasses behaviour. 057 * The concrete implementation of the rows must be provided by subclasses. 058 * 059 * @author Roman Kennke (roman@kennke.org) 060 */ 061 public abstract class FlowView extends BoxView 062 { 063 /** 064 * A strategy for translating the logical views of a <code>FlowView</code> 065 * into the real views. 066 */ 067 public static class FlowStrategy 068 { 069 /** 070 * Creates a new instance of <code>FlowStragegy</code>. 071 */ 072 public FlowStrategy() 073 { 074 // Nothing to do here. 075 } 076 077 /** 078 * Receives notification from a <code>FlowView</code> that some content 079 * has been inserted into the document at a location that the 080 * <code>FlowView</code> is responsible for. 081 * 082 * The default implementation simply calls {@link #layout}. 083 * 084 * @param fv the flow view that sends the notification 085 * @param e the document event describing the change 086 * @param alloc the current allocation of the flow view 087 */ 088 public void insertUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) 089 { 090 if (alloc == null) 091 { 092 fv.layoutChanged(X_AXIS); 093 fv.layoutChanged(Y_AXIS); 094 } 095 else 096 { 097 Component host = fv.getContainer(); 098 if (host != null) 099 host.repaint(alloc.x, alloc.y, alloc.width, alloc.height); 100 } 101 } 102 103 /** 104 * Receives notification from a <code>FlowView</code> that some content 105 * has been removed from the document at a location that the 106 * <code>FlowView</code> is responsible for. 107 * 108 * The default implementation simply calls {@link #layout}. 109 * 110 * @param fv the flow view that sends the notification 111 * @param e the document event describing the change 112 * @param alloc the current allocation of the flow view 113 */ 114 public void removeUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) 115 { 116 if (alloc == null) 117 { 118 fv.layoutChanged(X_AXIS); 119 fv.layoutChanged(Y_AXIS); 120 } 121 else 122 { 123 Component host = fv.getContainer(); 124 if (host != null) 125 host.repaint(alloc.x, alloc.y, alloc.width, alloc.height); 126 } 127 } 128 129 /** 130 * Receives notification from a <code>FlowView</code> that some attributes 131 * have changed in the document at a location that the 132 * <code>FlowView</code> is responsible for. 133 * 134 * The default implementation simply calls {@link #layout}. 135 * 136 * @param fv the flow view that sends the notification 137 * @param e the document event describing the change 138 * @param alloc the current allocation of the flow view 139 */ 140 public void changedUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) 141 { 142 if (alloc == null) 143 { 144 fv.layoutChanged(X_AXIS); 145 fv.layoutChanged(Y_AXIS); 146 } 147 else 148 { 149 Component host = fv.getContainer(); 150 if (host != null) 151 host.repaint(alloc.x, alloc.y, alloc.width, alloc.height); 152 } 153 } 154 155 /** 156 * Returns the logical view of the managed <code>FlowView</code>. 157 * 158 * @param fv the flow view for which to return the logical view 159 * 160 * @return the logical view of the managed <code>FlowView</code> 161 */ 162 protected View getLogicalView(FlowView fv) 163 { 164 return fv.layoutPool; 165 } 166 167 /** 168 * Performs the layout for the whole view. By default this rebuilds 169 * all the physical views from the logical views of the managed FlowView. 170 * 171 * This is called by {@link FlowView#layout} to update the layout of 172 * the view. 173 * 174 * @param fv the flow view for which we perform the layout 175 */ 176 public void layout(FlowView fv) 177 { 178 int start = fv.getStartOffset(); 179 int end = fv.getEndOffset(); 180 181 // Preserve the views from the logical view from beeing removed. 182 View lv = getLogicalView(fv); 183 int viewCount = lv.getViewCount(); 184 for (int i = 0; i < viewCount; i++) 185 { 186 View v = lv.getView(i); 187 v.setParent(lv); 188 } 189 190 // Then remove all views from the flow view. 191 fv.removeAll(); 192 193 for (int rowIndex = 0; start < end; rowIndex++) 194 { 195 View row = fv.createRow(); 196 fv.append(row); 197 int next = layoutRow(fv, rowIndex, start); 198 if (row.getViewCount() == 0) 199 { 200 row.append(createView(fv, start, Integer.MAX_VALUE, rowIndex)); 201 next = row.getEndOffset(); 202 } 203 if (start < next) 204 start = next; 205 else 206 assert false: "May not happen"; 207 } 208 } 209 210 /** 211 * Lays out one row of the flow view. This is called by {@link #layout} to 212 * fill one row with child views until the available span is exhausted. The 213 * default implementation fills the row by calling 214 * {@link #createView(FlowView, int, int, int)} until the available space is 215 * exhausted, a forced break is encountered or there are no more views in 216 * the logical view. If the available space is exhausted, 217 * {@link #adjustRow(FlowView, int, int, int)} is called to fit the row into 218 * the available span. 219 * 220 * @param fv the flow view for which we perform the layout 221 * @param rowIndex the index of the row 222 * @param pos the model position for the beginning of the row 223 * @return the start position of the next row 224 */ 225 protected int layoutRow(FlowView fv, int rowIndex, int pos) 226 { 227 View row = fv.getView(rowIndex); 228 int axis = fv.getFlowAxis(); 229 int span = fv.getFlowSpan(rowIndex); 230 int x = fv.getFlowStart(rowIndex); 231 int end = fv.getEndOffset(); 232 233 // Needed for adjusting indentation in adjustRow(). 234 int preX = x; 235 int availableSpan = span; 236 237 TabExpander tabExp = fv instanceof TabExpander ? (TabExpander) fv : null; 238 239 boolean forcedBreak = false; 240 while (pos < end && span >= 0) 241 { 242 View view = createView(fv, pos, span, rowIndex); 243 if (view == null 244 || (span == 0 && view.getPreferredSpan(axis) > 0)) 245 break; 246 247 int viewSpan; 248 if (axis == X_AXIS && view instanceof TabableView) 249 viewSpan = (int) ((TabableView) view).getTabbedSpan(x, tabExp); 250 else 251 viewSpan = (int) view.getPreferredSpan(axis); 252 253 // Break if the line if the view does not fit in this row or the 254 // line just must be broken. 255 int breakWeight = view.getBreakWeight(axis, pos, span); 256 if (breakWeight >= ForcedBreakWeight) 257 { 258 int rowViewCount = row.getViewCount(); 259 if (rowViewCount > 0) 260 { 261 view = view.breakView(axis, pos, x, span); 262 if (view != null) 263 { 264 if (axis == X_AXIS && view instanceof TabableView) 265 viewSpan = 266 (int) ((TabableView) view).getTabbedSpan(x, tabExp); 267 else 268 viewSpan = (int) view.getPreferredSpan(axis); 269 } 270 else 271 viewSpan = 0; 272 } 273 forcedBreak = true; 274 } 275 span -= viewSpan; 276 x += viewSpan; 277 if (view != null) 278 { 279 row.append(view); 280 pos = view.getEndOffset(); 281 } 282 if (forcedBreak) 283 break; 284 } 285 286 if (span < 0) 287 adjustRow(fv, rowIndex, availableSpan, preX); 288 else if (row.getViewCount() == 0) 289 { 290 View view = createView(fv, pos, Integer.MAX_VALUE, rowIndex); 291 row.append(view); 292 } 293 return row.getEndOffset(); 294 } 295 296 /** 297 * Creates physical views that form the rows of the flow view. This 298 * can be an entire view from the logical view (if it fits within the 299 * available span), a fragment of such a view (if it doesn't fit in the 300 * available span and can be broken down) or <code>null</code> (if it does 301 * not fit in the available span and also cannot be broken down). 302 * 303 * The default implementation fetches the logical view at the specified 304 * <code>startOffset</code>. If that view has a different startOffset than 305 * specified in the argument, a fragment is created using 306 * {@link View#createFragment(int, int)} that has the correct startOffset 307 * and the logical view's endOffset. 308 * 309 * @param fv the flow view 310 * @param startOffset the start offset for the view to be created 311 * @param spanLeft the available span 312 * @param rowIndex the index of the row 313 * 314 * @return a view to fill the row with, or <code>null</code> if there 315 * is no view or view fragment that fits in the available span 316 */ 317 protected View createView(FlowView fv, int startOffset, int spanLeft, 318 int rowIndex) 319 { 320 View logicalView = getLogicalView(fv); 321 int index = logicalView.getViewIndex(startOffset, 322 Position.Bias.Forward); 323 View retVal = logicalView.getView(index); 324 if (retVal.getStartOffset() != startOffset) 325 retVal = retVal.createFragment(startOffset, retVal.getEndOffset()); 326 return retVal; 327 } 328 329 /** 330 * Tries to adjust the specified row to fit within the desired span. The 331 * default implementation iterates through the children of the specified 332 * row to find the view that has the highest break weight and - if there 333 * is more than one view with such a break weight - which is nearest to 334 * the end of the row. If there is such a view that has a break weight > 335 * {@link View#BadBreakWeight}, this view is broken using the 336 * {@link View#breakView(int, int, float, float)} method and this view and 337 * all views after the now broken view are replaced by the broken view. 338 * 339 * @param fv the flow view 340 * @param rowIndex the index of the row to be adjusted 341 * @param desiredSpan the layout span 342 * @param x the X location at which the row starts 343 */ 344 protected void adjustRow(FlowView fv, int rowIndex, int desiredSpan, int x) { 345 // Determine the last view that has the highest break weight. 346 int axis = fv.getFlowAxis(); 347 View row = fv.getView(rowIndex); 348 int count = row.getViewCount(); 349 int breakIndex = -1; 350 int breakWeight = BadBreakWeight; 351 int breakSpan = 0; 352 int currentSpan = 0; 353 for (int i = 0; i < count; ++i) 354 { 355 View view = row.getView(i); 356 int spanLeft = desiredSpan - currentSpan; 357 int weight = view.getBreakWeight(axis, x + currentSpan, spanLeft); 358 if (weight >= breakWeight && weight > BadBreakWeight) 359 { 360 breakIndex = i; 361 breakSpan = currentSpan; 362 breakWeight = weight; 363 if (weight >= ForcedBreakWeight) 364 // Don't search further. 365 break; 366 } 367 currentSpan += view.getPreferredSpan(axis); 368 } 369 370 // If there is a potential break location found, break the row at 371 // this location. 372 if (breakIndex >= 0) 373 { 374 int spanLeft = desiredSpan - breakSpan; 375 View toBeBroken = row.getView(breakIndex); 376 View brokenView = toBeBroken.breakView(axis, 377 toBeBroken.getStartOffset(), 378 x + breakSpan, spanLeft); 379 View lv = getLogicalView(fv); 380 for (int i = breakIndex; i < count; i++) 381 { 382 View tmp = row.getView(i); 383 if (contains(lv, tmp)) 384 tmp.setParent(lv); 385 else if (tmp.getViewCount() > 0) 386 reparent(tmp, lv); 387 } 388 row.replace(breakIndex, count - breakIndex, 389 new View[]{ brokenView }); 390 } 391 392 } 393 394 /** 395 * Helper method to determine if one view contains another as child. 396 */ 397 private boolean contains(View view, View child) 398 { 399 boolean ret = false; 400 int n = view.getViewCount(); 401 for (int i = 0; i < n && ret == false; i++) 402 { 403 if (view.getView(i) == child) 404 ret = true; 405 } 406 return ret; 407 } 408 409 /** 410 * Helper method that reparents the <code>view</code> and all of its 411 * decendents to the <code>parent</code> (the logical view). 412 * 413 * @param view the view to reparent 414 * @param parent the new parent 415 */ 416 private void reparent(View view, View parent) 417 { 418 int n = view.getViewCount(); 419 for (int i = 0; i < n; i++) 420 { 421 View tmp = view.getView(i); 422 if (contains(parent, tmp)) 423 tmp.setParent(parent); 424 else 425 reparent(tmp, parent); 426 } 427 } 428 } 429 430 /** 431 * This special subclass of <code>View</code> is used to represent 432 * the logical representation of this view. It does not support any 433 * visual representation, this is handled by the physical view implemented 434 * in the <code>FlowView</code>. 435 */ 436 class LogicalView extends CompositeView 437 { 438 /** 439 * Creates a new LogicalView instance. 440 */ 441 LogicalView(Element el) 442 { 443 super(el); 444 } 445 446 /** 447 * Overridden to return the attributes of the parent 448 * (== the FlowView instance). 449 */ 450 public AttributeSet getAttributes() 451 { 452 View p = getParent(); 453 return p != null ? p.getAttributes() : null; 454 } 455 456 protected void childAllocation(int index, Rectangle a) 457 { 458 // Nothing to do here (not visual). 459 } 460 461 protected View getViewAtPoint(int x, int y, Rectangle r) 462 { 463 // Nothing to do here (not visual). 464 return null; 465 } 466 467 protected boolean isAfter(int x, int y, Rectangle r) 468 { 469 // Nothing to do here (not visual). 470 return false; 471 } 472 473 protected boolean isBefore(int x, int y, Rectangle r) 474 { 475 // Nothing to do here (not visual). 476 return false; 477 } 478 479 public float getPreferredSpan(int axis) 480 { 481 float max = 0; 482 float pref = 0; 483 int n = getViewCount(); 484 for (int i = 0; i < n; i++) 485 { 486 View v = getView(i); 487 pref += v.getPreferredSpan(axis); 488 if (v.getBreakWeight(axis, 0, Integer.MAX_VALUE) 489 >= ForcedBreakWeight) 490 { 491 max = Math.max(max, pref); 492 pref = 0; 493 } 494 } 495 max = Math.max(max, pref); 496 return max; 497 } 498 499 public float getMinimumSpan(int axis) 500 { 501 float max = 0; 502 float min = 0; 503 boolean wrap = true; 504 int n = getViewCount(); 505 for (int i = 0; i < n; i++) 506 { 507 View v = getView(i); 508 if (v.getBreakWeight(axis, 0, Integer.MAX_VALUE) 509 == BadBreakWeight) 510 { 511 min += v.getPreferredSpan(axis); 512 wrap = false; 513 } 514 else if (! wrap) 515 { 516 max = Math.max(min, max); 517 wrap = true; 518 min = 0; 519 } 520 } 521 max = Math.max(max, min); 522 return max; 523 } 524 525 public void paint(Graphics g, Shape s) 526 { 527 // Nothing to do here (not visual). 528 } 529 530 /** 531 * Overridden to handle possible leaf elements. 532 */ 533 protected void loadChildren(ViewFactory f) 534 { 535 Element el = getElement(); 536 if (el.isLeaf()) 537 { 538 View v = new LabelView(el); 539 append(v); 540 } 541 else 542 super.loadChildren(f); 543 } 544 545 /** 546 * Overridden to reparent the children to this logical view, in case 547 * they have been parented by a row. 548 */ 549 protected void forwardUpdateToView(View v, DocumentEvent e, Shape a, 550 ViewFactory f) 551 { 552 v.setParent(this); 553 super.forwardUpdateToView(v, e, a, f); 554 } 555 556 /** 557 * Overridden to handle possible leaf element. 558 */ 559 protected int getViewIndexAtPosition(int pos) 560 { 561 int index = 0; 562 if (! getElement().isLeaf()) 563 index = super.getViewIndexAtPosition(pos); 564 return index; 565 } 566 } 567 568 /** 569 * The shared instance of FlowStrategy. 570 */ 571 static final FlowStrategy sharedStrategy = new FlowStrategy(); 572 573 /** 574 * The span of the <code>FlowView</code> that should be flowed. 575 */ 576 protected int layoutSpan; 577 578 /** 579 * Represents the logical child elements of this view, encapsulated within 580 * one parent view (an instance of a package private <code>LogicalView</code> 581 * class). These will be translated to a set of real views that are then 582 * displayed on screen. This translation is performed by the inner class 583 * {@link FlowStrategy}. 584 */ 585 protected View layoutPool; 586 587 /** 588 * The <code>FlowStrategy</code> to use for translating between the 589 * logical and physical view. 590 */ 591 protected FlowStrategy strategy; 592 593 /** 594 * Creates a new <code>FlowView</code> for the given 595 * <code>Element</code> and <code>axis</code>. 596 * 597 * @param element the element that is rendered by this FlowView 598 * @param axis the axis along which the view is tiled, either 599 * <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>, the flow 600 * axis is orthogonal to this one 601 */ 602 public FlowView(Element element, int axis) 603 { 604 super(element, axis); 605 strategy = sharedStrategy; 606 layoutSpan = Short.MAX_VALUE; 607 } 608 609 /** 610 * Returns the axis along which the view should be flowed. This is 611 * orthogonal to the axis along which the boxes are tiled. 612 * 613 * @return the axis along which the view should be flowed 614 */ 615 public int getFlowAxis() 616 { 617 int axis = getAxis(); 618 int flowAxis; 619 620 if (axis == X_AXIS) 621 flowAxis = Y_AXIS; 622 else 623 flowAxis = X_AXIS; 624 625 return flowAxis; 626 627 } 628 629 /** 630 * Returns the span of the flow for the specified child view. A flow 631 * layout can be shaped by providing different span values for different 632 * child indices. The default implementation returns the entire available 633 * span inside the view. 634 * 635 * @param index the index of the child for which to return the span 636 * 637 * @return the span of the flow for the specified child view 638 */ 639 public int getFlowSpan(int index) 640 { 641 return layoutSpan; 642 } 643 644 /** 645 * Returns the location along the flow axis where the flow span starts 646 * given a child view index. The flow can be shaped by providing 647 * different values here. 648 * 649 * @param index the index of the child for which to return the flow location 650 * 651 * @return the location along the flow axis where the flow span starts 652 */ 653 public int getFlowStart(int index) 654 { 655 return 0; 656 } 657 658 /** 659 * Creates a new view that represents a row within a flow. 660 * 661 * @return a view for a new row 662 */ 663 protected abstract View createRow(); 664 665 /** 666 * Loads the children of this view. The <code>FlowView</code> does not 667 * directly load its children. Instead it creates a logical view 668 * ({@link #layoutPool}) which is filled by the logical child views. 669 * The real children are created at layout time and each represent one 670 * row. 671 * 672 * This method is called by {@link View#setParent} in order to initialize 673 * the view. 674 * 675 * @param vf the view factory to use for creating the child views 676 */ 677 protected void loadChildren(ViewFactory vf) 678 { 679 if (layoutPool == null) 680 { 681 layoutPool = new LogicalView(getElement()); 682 } 683 layoutPool.setParent(this); 684 // Initialize the flow strategy. 685 strategy.insertUpdate(this, null, null); 686 } 687 688 /** 689 * Performs the layout of this view. If the span along the flow axis changed, 690 * this first calls {@link FlowStrategy#layout} in order to rebuild the 691 * rows of this view. Then the superclass's behaviour is called to arrange 692 * the rows within the box. 693 * 694 * @param width the width of the view 695 * @param height the height of the view 696 */ 697 protected void layout(int width, int height) 698 { 699 int flowAxis = getFlowAxis(); 700 int span; 701 if (flowAxis == X_AXIS) 702 span = (int) width; 703 else 704 span = (int) height; 705 706 if (layoutSpan != span) 707 { 708 layoutChanged(flowAxis); 709 layoutChanged(getAxis()); 710 layoutSpan = span; 711 } 712 713 if (! isLayoutValid(flowAxis)) 714 { 715 int axis = getAxis(); 716 int oldSpan = axis == X_AXIS ? getWidth() : getHeight(); 717 strategy.layout(this); 718 int newSpan = (int) getPreferredSpan(axis); 719 if (oldSpan != newSpan) 720 { 721 View parent = getParent(); 722 if (parent != null) 723 parent.preferenceChanged(this, axis == X_AXIS, axis == Y_AXIS); 724 } 725 } 726 727 super.layout(width, height); 728 } 729 730 /** 731 * Receice notification that some content has been inserted in the region 732 * that this view is responsible for. This calls 733 * {@link FlowStrategy#insertUpdate}. 734 * 735 * @param changes the document event describing the changes 736 * @param a the current allocation of the view 737 * @param vf the view factory that is used for creating new child views 738 */ 739 public void insertUpdate(DocumentEvent changes, Shape a, ViewFactory vf) 740 { 741 // First we must send the insertUpdate to the logical view so it can 742 // be updated accordingly. 743 layoutPool.insertUpdate(changes, a, vf); 744 strategy.insertUpdate(this, changes, getInsideAllocation(a)); 745 } 746 747 /** 748 * Receice notification that some content has been removed from the region 749 * that this view is responsible for. This calls 750 * {@link FlowStrategy#removeUpdate}. 751 * 752 * @param changes the document event describing the changes 753 * @param a the current allocation of the view 754 * @param vf the view factory that is used for creating new child views 755 */ 756 public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory vf) 757 { 758 layoutPool.removeUpdate(changes, a, vf); 759 strategy.removeUpdate(this, changes, getInsideAllocation(a)); 760 } 761 762 /** 763 * Receice notification that some attributes changed in the region 764 * that this view is responsible for. This calls 765 * {@link FlowStrategy#changedUpdate}. 766 * 767 * @param changes the document event describing the changes 768 * @param a the current allocation of the view 769 * @param vf the view factory that is used for creating new child views 770 */ 771 public void changedUpdate(DocumentEvent changes, Shape a, ViewFactory vf) 772 { 773 layoutPool.changedUpdate(changes, a, vf); 774 strategy.changedUpdate(this, changes, getInsideAllocation(a)); 775 } 776 777 /** 778 * Returns the index of the child <code>View</code> for the given model 779 * position. 780 * 781 * This is implemented to iterate over the children of this 782 * view (the rows) and return the index of the first view that contains 783 * the given position. 784 * 785 * @param pos the model position for whicht the child <code>View</code> is 786 * queried 787 * 788 * @return the index of the child <code>View</code> for the given model 789 * position 790 */ 791 protected int getViewIndexAtPosition(int pos) 792 { 793 // First make sure we have a valid layout. 794 if (!isAllocationValid()) 795 layout(getWidth(), getHeight()); 796 797 int count = getViewCount(); 798 int result = -1; 799 800 for (int i = 0; i < count; ++i) 801 { 802 View child = getView(i); 803 int start = child.getStartOffset(); 804 int end = child.getEndOffset(); 805 if (start <= pos && end > pos) 806 { 807 result = i; 808 break; 809 } 810 } 811 return result; 812 } 813 814 /** 815 * Calculates the size requirements of this <code>BoxView</code> along 816 * its minor axis, that is the axis opposite to the axis specified in the 817 * constructor. 818 * 819 * This is overridden and forwards the request to the logical view. 820 * 821 * @param axis the axis that is examined 822 * @param r the <code>SizeRequirements</code> object to hold the result, 823 * if <code>null</code>, a new one is created 824 * 825 * @return the size requirements for this <code>BoxView</code> along 826 * the specified axis 827 */ 828 protected SizeRequirements calculateMinorAxisRequirements(int axis, 829 SizeRequirements r) 830 { 831 SizeRequirements res = r; 832 if (res == null) 833 res = new SizeRequirements(); 834 res.minimum = (int) layoutPool.getMinimumSpan(axis); 835 res.preferred = Math.max(res.minimum, 836 (int) layoutPool.getPreferredSpan(axis)); 837 res.maximum = Integer.MAX_VALUE; 838 res.alignment = 0.5F; 839 return res; 840 } 841 }