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 }