001/* FlowView.java -- A composite View
002   Copyright (C) 2005  Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package javax.swing.text;
040
041import java.awt.Component;
042import java.awt.Graphics;
043import java.awt.Rectangle;
044import java.awt.Shape;
045
046import javax.swing.SizeRequirements;
047import 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 */
061public 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}