1/*
2 * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25package javax.swing.text;
26
27import java.awt.*;
28import java.util.Vector;
29import javax.swing.event.*;
30import javax.swing.SizeRequirements;
31
32/**
33 * A View that tries to flow it's children into some
34 * partially constrained space.  This can be used to
35 * build things like paragraphs, pages, etc.  The
36 * flow is made up of the following pieces of functionality.
37 * <ul>
38 * <li>A logical set of child views, which as used as a
39 * layout pool from which a physical view is formed.
40 * <li>A strategy for translating the logical view to
41 * a physical (flowed) view.
42 * <li>Constraints for the strategy to work against.
43 * <li>A physical structure, that represents the flow.
44 * The children of this view are where the pieces of
45 * of the logical views are placed to create the flow.
46 * </ul>
47 *
48 * @author  Timothy Prinzing
49 * @see     View
50 * @since 1.3
51 */
52public abstract class FlowView extends BoxView {
53
54    /**
55     * Constructs a FlowView for the given element.
56     *
57     * @param elem the element that this view is responsible for
58     * @param axis may be either View.X_AXIS or View.Y_AXIS
59     */
60    public FlowView(Element elem, int axis) {
61        super(elem, axis);
62        layoutSpan = Integer.MAX_VALUE;
63        strategy = new FlowStrategy();
64    }
65
66    /**
67     * Fetches the axis along which views should be
68     * flowed.  By default, this will be the axis
69     * orthogonal to the axis along which the flow
70     * rows are tiled (the axis of the default flow
71     * rows themselves).  This is typically used
72     * by the <code>FlowStrategy</code>.
73     * @return the axis along which views should be
74     * flowed
75     */
76    public int getFlowAxis() {
77        if (getAxis() == Y_AXIS) {
78            return X_AXIS;
79        }
80        return Y_AXIS;
81    }
82
83    /**
84     * Fetch the constraining span to flow against for
85     * the given child index.  This is called by the
86     * FlowStrategy while it is updating the flow.
87     * A flow can be shaped by providing different values
88     * for the row constraints.  By default, the entire
89     * span inside of the insets along the flow axis
90     * is returned.
91     *
92     * @param index the index of the row being updated.
93     *   This should be a value &gt;= 0 and &lt; getViewCount().
94     * @return the constraining span to flow against for
95     * the given child index
96     * @see #getFlowStart
97     */
98    public int getFlowSpan(int index) {
99        return layoutSpan;
100    }
101
102    /**
103     * Fetch the location along the flow axis that the
104     * flow span will start at.  This is called by the
105     * FlowStrategy while it is updating the flow.
106     * A flow can be shaped by providing different values
107     * for the row constraints.
108
109     * @param index the index of the row being updated.
110     *   This should be a value &gt;= 0 and &lt; getViewCount().
111     * @return the location along the flow axis that the
112     * flow span will start at
113     * @see #getFlowSpan
114     */
115    public int getFlowStart(int index) {
116        return 0;
117    }
118
119    /**
120     * Create a View that should be used to hold a
121     * a rows worth of children in a flow.  This is
122     * called by the FlowStrategy when new children
123     * are added or removed (i.e. rows are added or
124     * removed) in the process of updating the flow.
125     * @return a View that should be used to hold a
126     * a rows worth of children in a flow
127     */
128    protected abstract View createRow();
129
130    // ---- BoxView methods -------------------------------------
131
132    /**
133     * Loads all of the children to initialize the view.
134     * This is called by the <code>setParent</code> method.
135     * This is reimplemented to not load any children directly
136     * (as they are created in the process of formatting).
137     * If the layoutPool variable is null, an instance of
138     * LogicalView is created to represent the logical view
139     * that is used in the process of formatting.
140     *
141     * @param f the view factory
142     */
143    protected void loadChildren(ViewFactory f) {
144        if (layoutPool == null) {
145            layoutPool = new LogicalView(getElement());
146        }
147        layoutPool.setParent(this);
148
149        // This synthetic insertUpdate call gives the strategy a chance
150        // to initialize.
151        strategy.insertUpdate(this, null, null);
152    }
153
154    /**
155     * Fetches the child view index representing the given position in
156     * the model.
157     *
158     * @param pos the position &gt;= 0
159     * @return  index of the view representing the given position, or
160     *   -1 if no view represents that position
161     */
162    protected int getViewIndexAtPosition(int pos) {
163        if (pos >= getStartOffset() && (pos < getEndOffset())) {
164            for (int counter = 0; counter < getViewCount(); counter++) {
165                View v = getView(counter);
166                if(pos >= v.getStartOffset() &&
167                   pos < v.getEndOffset()) {
168                    return counter;
169                }
170            }
171        }
172        return -1;
173    }
174
175    /**
176     * Lays out the children.  If the span along the flow
177     * axis has changed, layout is marked as invalid which
178     * which will cause the superclass behavior to recalculate
179     * the layout along the box axis.  The FlowStrategy.layout
180     * method will be called to rebuild the flow rows as
181     * appropriate.  If the height of this view changes
182     * (determined by the preferred size along the box axis),
183     * a preferenceChanged is called.  Following all of that,
184     * the normal box layout of the superclass is performed.
185     *
186     * @param width  the width to lay out against &gt;= 0.  This is
187     *   the width inside of the inset area.
188     * @param height the height to lay out against &gt;= 0 This
189     *   is the height inside of the inset area.
190     */
191    protected void layout(int width, int height) {
192        final int faxis = getFlowAxis();
193        int newSpan;
194        if (faxis == X_AXIS) {
195            newSpan = width;
196        } else {
197            newSpan = height;
198        }
199        if (layoutSpan != newSpan) {
200            layoutChanged(faxis);
201            layoutChanged(getAxis());
202            layoutSpan = newSpan;
203        }
204
205        // repair the flow if necessary
206        if (! isLayoutValid(faxis)) {
207            final int heightAxis = getAxis();
208            int oldFlowHeight = (heightAxis == X_AXIS)? getWidth() : getHeight();
209            strategy.layout(this);
210            int newFlowHeight = (int) getPreferredSpan(heightAxis);
211            if (oldFlowHeight != newFlowHeight) {
212                View p = getParent();
213                if (p != null) {
214                    p.preferenceChanged(this, (heightAxis == X_AXIS), (heightAxis == Y_AXIS));
215                }
216
217                // PENDING(shannonh)
218                // Temporary fix for 4250847
219                // Can be removed when TraversalContext is added
220                Component host = getContainer();
221                if (host != null) {
222                    //nb idk 12/12/2001 host should not be equal to null. We need to add assertion here
223                    host.repaint();
224                }
225            }
226        }
227
228        super.layout(width, height);
229    }
230
231    /**
232     * Calculate requirements along the minor axis.  This
233     * is implemented to forward the request to the logical
234     * view by calling getMinimumSpan, getPreferredSpan, and
235     * getMaximumSpan on it.
236     */
237    protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
238        if (r == null) {
239            r = new SizeRequirements();
240        }
241        float pref = layoutPool.getPreferredSpan(axis);
242        float min = layoutPool.getMinimumSpan(axis);
243        // Don't include insets, Box.getXXXSpan will include them.
244        r.minimum = (int)min;
245        r.preferred = Math.max(r.minimum, (int) pref);
246        r.maximum = Integer.MAX_VALUE;
247        r.alignment = 0.5f;
248        return r;
249    }
250
251    // ---- View methods ----------------------------------------------------
252
253    /**
254     * Gives notification that something was inserted into the document
255     * in a location that this view is responsible for.
256     *
257     * @param changes the change information from the associated document
258     * @param a the current allocation of the view
259     * @param f the factory to use to rebuild if the view has children
260     * @see View#insertUpdate
261     */
262    public void insertUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
263        layoutPool.insertUpdate(changes, a, f);
264        strategy.insertUpdate(this, changes, getInsideAllocation(a));
265    }
266
267    /**
268     * Gives notification that something was removed from the document
269     * in a location that this view is responsible for.
270     *
271     * @param changes the change information from the associated document
272     * @param a the current allocation of the view
273     * @param f the factory to use to rebuild if the view has children
274     * @see View#removeUpdate
275     */
276    public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
277        layoutPool.removeUpdate(changes, a, f);
278        strategy.removeUpdate(this, changes, getInsideAllocation(a));
279    }
280
281    /**
282     * Gives notification from the document that attributes were changed
283     * in a location that this view is responsible for.
284     *
285     * @param changes the change information from the associated document
286     * @param a the current allocation of the view
287     * @param f the factory to use to rebuild if the view has children
288     * @see View#changedUpdate
289     */
290    public void changedUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
291        layoutPool.changedUpdate(changes, a, f);
292        strategy.changedUpdate(this, changes, getInsideAllocation(a));
293    }
294
295    /** {@inheritDoc} */
296    public void setParent(View parent) {
297        super.setParent(parent);
298        if (parent == null
299                && layoutPool != null ) {
300            layoutPool.setParent(null);
301        }
302    }
303
304    // --- variables -----------------------------------------------
305
306    /**
307     * Default constraint against which the flow is
308     * created against.
309     */
310    protected int layoutSpan;
311
312    /**
313     * These are the views that represent the child elements
314     * of the element this view represents (The logical view
315     * to translate to a physical view).  These are not
316     * directly children of this view.  These are either
317     * placed into the rows directly or used for the purpose
318     * of breaking into smaller chunks, to form the physical
319     * view.
320     */
321    protected View layoutPool;
322
323    /**
324     * The behavior for keeping the flow updated.  By
325     * default this is a singleton shared by all instances
326     * of FlowView (FlowStrategy is stateless).  Subclasses
327     * can create an alternative strategy, which might keep
328     * state.
329     */
330    protected FlowStrategy strategy;
331
332    /**
333     * Strategy for maintaining the physical form
334     * of the flow.  The default implementation is
335     * completely stateless, and recalculates the
336     * entire flow if the layout is invalid on the
337     * given FlowView.  Alternative strategies can
338     * be implemented by subclassing, and might
339     * perform incremental repair to the layout
340     * or alternative breaking behavior.
341     * @since 1.3
342     */
343    public static class FlowStrategy {
344        Position damageStart = null;
345        Vector<View> viewBuffer;
346
347        void addDamage(FlowView fv, int offset) {
348            if (offset >= fv.getStartOffset() && offset < fv.getEndOffset()) {
349                if (damageStart == null || offset < damageStart.getOffset()) {
350                    try {
351                        damageStart = fv.getDocument().createPosition(offset);
352                    } catch (BadLocationException e) {
353                        // shouldn't happen since offset is inside view bounds
354                        assert(false);
355                    }
356                }
357            }
358        }
359
360        void unsetDamage() {
361            damageStart = null;
362        }
363
364        /**
365         * Gives notification that something was inserted into the document
366         * in a location that the given flow view is responsible for.  The
367         * strategy should update the appropriate changed region (which
368         * depends upon the strategy used for repair).
369         *
370         * @param fv the flow view
371         * @param e the change information from the associated document
372         * @param alloc the current allocation of the view inside of the insets.
373         *   This value will be null if the view has not yet been displayed.
374         * @see View#insertUpdate
375         */
376        public void insertUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) {
377            // FlowView.loadChildren() makes a synthetic call into this,
378            // passing null as e
379            if (e != null) {
380                addDamage(fv, e.getOffset());
381            }
382
383            if (alloc != null) {
384                Component host = fv.getContainer();
385                if (host != null) {
386                    host.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
387                }
388            } else {
389                fv.preferenceChanged(null, true, true);
390            }
391        }
392
393        /**
394         * Gives notification that something was removed from the document
395         * in a location that the given flow view is responsible for.
396         *
397         * @param fv the flow view
398         * @param e the change information from the associated document
399         * @param alloc the current allocation of the view inside of the insets.
400         * @see View#removeUpdate
401         */
402        public void removeUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) {
403            addDamage(fv, e.getOffset());
404            if (alloc != null) {
405                Component host = fv.getContainer();
406                if (host != null) {
407                    host.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
408                }
409            } else {
410                fv.preferenceChanged(null, true, true);
411            }
412        }
413
414        /**
415         * Gives notification from the document that attributes were changed
416         * in a location that this view is responsible for.
417         *
418         * @param fv     the <code>FlowView</code> containing the changes
419         * @param e      the <code>DocumentEvent</code> describing the changes
420         *               done to the Document
421         * @param alloc  Bounds of the View
422         * @see View#changedUpdate
423         */
424        public void changedUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) {
425            addDamage(fv, e.getOffset());
426            if (alloc != null) {
427                Component host = fv.getContainer();
428                if (host != null) {
429                    host.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
430                }
431            } else {
432                fv.preferenceChanged(null, true, true);
433            }
434        }
435
436        /**
437         * This method gives flow strategies access to the logical
438         * view of the FlowView.
439         * @param fv the FlowView
440         * @return the logical view of the FlowView
441         */
442        protected View getLogicalView(FlowView fv) {
443            return fv.layoutPool;
444        }
445
446        /**
447         * Update the flow on the given FlowView.  By default, this causes
448         * all of the rows (child views) to be rebuilt to match the given
449         * constraints for each row.  This is called by a FlowView.layout
450         * to update the child views in the flow.
451         *
452         * @param fv the view to reflow
453         */
454        public void layout(FlowView fv) {
455            View pool = getLogicalView(fv);
456            int rowIndex, p0;
457            int p1 = fv.getEndOffset();
458
459            if (fv.majorAllocValid) {
460                if (damageStart == null) {
461                    return;
462                }
463                // In some cases there's no view at position damageStart, so
464                // step back and search again. See 6452106 for details.
465                int offset = damageStart.getOffset();
466                while ((rowIndex = fv.getViewIndexAtPosition(offset)) < 0) {
467                    offset--;
468                }
469                if (rowIndex > 0) {
470                    rowIndex--;
471                }
472                p0 = fv.getView(rowIndex).getStartOffset();
473            } else {
474                rowIndex = 0;
475                p0 = fv.getStartOffset();
476            }
477            reparentViews(pool, p0);
478
479            viewBuffer = new Vector<View>(10, 10);
480            int rowCount = fv.getViewCount();
481            while (p0 < p1) {
482                View row;
483                if (rowIndex >= rowCount) {
484                    row = fv.createRow();
485                    fv.append(row);
486                } else {
487                    row = fv.getView(rowIndex);
488                }
489                p0 = layoutRow(fv, rowIndex, p0);
490                rowIndex++;
491            }
492            viewBuffer = null;
493
494            if (rowIndex < rowCount) {
495                fv.replace(rowIndex, rowCount - rowIndex, null);
496            }
497            unsetDamage();
498        }
499
500        /**
501         * Creates a row of views that will fit within the
502         * layout span of the row.  This is called by the layout method.
503         * This is implemented to fill the row by repeatedly calling
504         * the createView method until the available span has been
505         * exhausted, a forced break was encountered, or the createView
506         * method returned null.  If the remaining span was exhausted,
507         * the adjustRow method will be called to perform adjustments
508         * to the row to try and make it fit into the given span.
509         *
510         * @param fv the flow view
511         * @param rowIndex the index of the row to fill in with views.  The
512         *   row is assumed to be empty on entry.
513         * @param pos  The current position in the children of
514         *   this views element from which to start.
515         * @return the position to start the next row
516         */
517        protected int layoutRow(FlowView fv, int rowIndex, int pos) {
518            View row = fv.getView(rowIndex);
519            float x = fv.getFlowStart(rowIndex);
520            float spanLeft = fv.getFlowSpan(rowIndex);
521            int end = fv.getEndOffset();
522            TabExpander te = (fv instanceof TabExpander) ? (TabExpander)fv : null;
523            final int flowAxis = fv.getFlowAxis();
524
525            int breakWeight = BadBreakWeight;
526            float breakX = 0f;
527            float breakSpan = 0f;
528            int breakIndex = -1;
529            int n = 0;
530
531            viewBuffer.clear();
532            while (pos < end && spanLeft >= 0) {
533                View v = createView(fv, pos, (int)spanLeft, rowIndex);
534                if (v == null) {
535                    break;
536                }
537
538                int bw = v.getBreakWeight(flowAxis, x, spanLeft);
539                if (bw >= ForcedBreakWeight) {
540                    View w = v.breakView(flowAxis, pos, x, spanLeft);
541                    if (w != null) {
542                        viewBuffer.add(w);
543                    } else if (n == 0) {
544                        // if the view does not break, and it is the only view
545                        // in a row, use the whole view
546                        viewBuffer.add(v);
547                    }
548                    break;
549                } else if (bw >= breakWeight && bw > BadBreakWeight) {
550                    breakWeight = bw;
551                    breakX = x;
552                    breakSpan = spanLeft;
553                    breakIndex = n;
554                }
555
556                float chunkSpan;
557                if (flowAxis == X_AXIS && v instanceof TabableView) {
558                    chunkSpan = ((TabableView)v).getTabbedSpan(x, te);
559                } else {
560                    chunkSpan = v.getPreferredSpan(flowAxis);
561                }
562
563                if (chunkSpan > spanLeft && breakIndex >= 0) {
564                    // row is too long, and we may break
565                    if (breakIndex < n) {
566                        v = viewBuffer.get(breakIndex);
567                    }
568                    for (int i = n - 1; i >= breakIndex; i--) {
569                        viewBuffer.remove(i);
570                    }
571                    v = v.breakView(flowAxis, v.getStartOffset(), breakX, breakSpan);
572                }
573
574                spanLeft -= chunkSpan;
575                x += chunkSpan;
576                viewBuffer.add(v);
577                pos = v.getEndOffset();
578                n++;
579            }
580
581            View[] views = new View[viewBuffer.size()];
582            viewBuffer.toArray(views);
583            row.replace(0, row.getViewCount(), views);
584            return (views.length > 0 ? row.getEndOffset() : pos);
585        }
586
587        /**
588         * Adjusts the given row if possible to fit within the
589         * layout span.  By default this will try to find the
590         * highest break weight possible nearest the end of
591         * the row.  If a forced break is encountered, the
592         * break will be positioned there.
593         *
594         * @param fv the flow view
595         * @param rowIndex the row to adjust to the current layout
596         *  span.
597         * @param desiredSpan the current layout span &gt;= 0
598         * @param x the location r starts at.
599         */
600        protected void adjustRow(FlowView fv, int rowIndex, int desiredSpan, int x) {
601            final int flowAxis = fv.getFlowAxis();
602            View r = fv.getView(rowIndex);
603            int n = r.getViewCount();
604            int span = 0;
605            int bestWeight = BadBreakWeight;
606            int bestSpan = 0;
607            int bestIndex = -1;
608            View v;
609            for (int i = 0; i < n; i++) {
610                v = r.getView(i);
611                int spanLeft = desiredSpan - span;
612
613                int w = v.getBreakWeight(flowAxis, x + span, spanLeft);
614                if ((w >= bestWeight) && (w > BadBreakWeight)) {
615                    bestWeight = w;
616                    bestIndex = i;
617                    bestSpan = span;
618                    if (w >= ForcedBreakWeight) {
619                        // it's a forced break, so there is
620                        // no point in searching further.
621                        break;
622                    }
623                }
624                span += v.getPreferredSpan(flowAxis);
625            }
626            if (bestIndex < 0) {
627                // there is nothing that can be broken, leave
628                // it in it's current state.
629                return;
630            }
631
632            // Break the best candidate view, and patch up the row.
633            int spanLeft = desiredSpan - bestSpan;
634            v = r.getView(bestIndex);
635            v = v.breakView(flowAxis, v.getStartOffset(), x + bestSpan, spanLeft);
636            View[] va = new View[1];
637            va[0] = v;
638            View lv = getLogicalView(fv);
639            int p0 = r.getView(bestIndex).getStartOffset();
640            int p1 = r.getEndOffset();
641            for (int i = 0; i < lv.getViewCount(); i++) {
642                View tmpView = lv.getView(i);
643                if (tmpView.getEndOffset() > p1) {
644                    break;
645                }
646                if (tmpView.getStartOffset() >= p0) {
647                    tmpView.setParent(lv);
648                }
649            }
650            r.replace(bestIndex, n - bestIndex, va);
651        }
652
653        void reparentViews(View pool, int startPos) {
654            int n = pool.getViewIndex(startPos, Position.Bias.Forward);
655            if (n >= 0) {
656                for (int i = n; i < pool.getViewCount(); i++) {
657                    pool.getView(i).setParent(pool);
658                }
659            }
660        }
661
662        /**
663         * Creates a view that can be used to represent the current piece
664         * of the flow.  This can be either an entire view from the
665         * logical view, or a fragment of the logical view.
666         *
667         * @param fv the view holding the flow
668         * @param startOffset the start location for the view being created
669         * @param spanLeft the about of span left to fill in the row
670         * @param rowIndex the row the view will be placed into
671         * @return a view that can be used to represent the current piece
672         * of the flow
673         */
674        protected View createView(FlowView fv, int startOffset, int spanLeft, int rowIndex) {
675            // Get the child view that contains the given starting position
676            View lv = getLogicalView(fv);
677            int childIndex = lv.getViewIndex(startOffset, Position.Bias.Forward);
678            View v = lv.getView(childIndex);
679            if (startOffset==v.getStartOffset()) {
680                // return the entire view
681                return v;
682            }
683
684            // return a fragment.
685            v = v.createFragment(startOffset, v.getEndOffset());
686            return v;
687        }
688    }
689
690    /**
691     * This class can be used to represent a logical view for
692     * a flow.  It keeps the children updated to reflect the state
693     * of the model, gives the logical child views access to the
694     * view hierarchy, and calculates a preferred span.  It doesn't
695     * do any rendering, layout, or model/view translation.
696     */
697    static class LogicalView extends CompositeView {
698
699        LogicalView(Element elem) {
700            super(elem);
701        }
702
703        protected int getViewIndexAtPosition(int pos) {
704            Element elem = getElement();
705            if (elem.isLeaf()) {
706                return 0;
707            }
708            return super.getViewIndexAtPosition(pos);
709        }
710
711        protected void loadChildren(ViewFactory f) {
712            Element elem = getElement();
713            if (elem.isLeaf()) {
714                View v = new LabelView(elem);
715                append(v);
716            } else {
717                super.loadChildren(f);
718            }
719        }
720
721        /**
722         * Fetches the attributes to use when rendering.  This view
723         * isn't directly responsible for an element so it returns
724         * the outer classes attributes.
725         */
726        public AttributeSet getAttributes() {
727            View p = getParent();
728            return (p != null) ? p.getAttributes() : null;
729        }
730
731        /**
732         * Determines the preferred span for this view along an
733         * axis.
734         *
735         * @param axis may be either View.X_AXIS or View.Y_AXIS
736         * @return   the span the view would like to be rendered into.
737         *           Typically the view is told to render into the span
738         *           that is returned, although there is no guarantee.
739         *           The parent may choose to resize or break the view.
740         * @see View#getPreferredSpan
741         */
742        public float getPreferredSpan(int axis) {
743            float maxpref = 0;
744            float pref = 0;
745            int n = getViewCount();
746            for (int i = 0; i < n; i++) {
747                View v = getView(i);
748                pref += v.getPreferredSpan(axis);
749                if (v.getBreakWeight(axis, 0, Integer.MAX_VALUE) >= ForcedBreakWeight) {
750                    maxpref = Math.max(maxpref, pref);
751                    pref = 0;
752                }
753            }
754            maxpref = Math.max(maxpref, pref);
755            return maxpref;
756        }
757
758        /**
759         * Determines the minimum span for this view along an
760         * axis.  The is implemented to find the minimum unbreakable
761         * span.
762         *
763         * @param axis may be either View.X_AXIS or View.Y_AXIS
764         * @return  the span the view would like to be rendered into.
765         *           Typically the view is told to render into the span
766         *           that is returned, although there is no guarantee.
767         *           The parent may choose to resize or break the view.
768         * @see View#getPreferredSpan
769         */
770        public float getMinimumSpan(int axis) {
771            float maxmin = 0;
772            float min = 0;
773            boolean nowrap = false;
774            int n = getViewCount();
775            for (int i = 0; i < n; i++) {
776                View v = getView(i);
777                if (v.getBreakWeight(axis, 0, Integer.MAX_VALUE) == BadBreakWeight) {
778                    min += v.getPreferredSpan(axis);
779                    nowrap = true;
780                } else if (nowrap) {
781                    maxmin = Math.max(min, maxmin);
782                    nowrap = false;
783                    min = 0;
784                }
785                if (v instanceof ComponentView) {
786                    maxmin = Math.max(maxmin, v.getMinimumSpan(axis));
787                }
788            }
789            maxmin = Math.max(maxmin, min);
790            return maxmin;
791        }
792
793        /**
794         * Forward the DocumentEvent to the given child view.  This
795         * is implemented to reparent the child to the logical view
796         * (the children may have been parented by a row in the flow
797         * if they fit without breaking) and then execute the superclass
798         * behavior.
799         *
800         * @param v the child view to forward the event to.
801         * @param e the change information from the associated document
802         * @param a the current allocation of the view
803         * @param f the factory to use to rebuild if the view has children
804         * @see #forwardUpdate
805         * @since 1.3
806         */
807        protected void forwardUpdateToView(View v, DocumentEvent e,
808                                           Shape a, ViewFactory f) {
809            View parent = v.getParent();
810            v.setParent(this);
811            super.forwardUpdateToView(v, e, a, f);
812            v.setParent(parent);
813        }
814
815        /** {@inheritDoc} */
816        @Override
817        protected void forwardUpdate(DocumentEvent.ElementChange ec,
818                                          DocumentEvent e, Shape a, ViewFactory f) {
819            // Update the view responsible for the changed element by invocation of
820            // super method.
821            super.forwardUpdate(ec, e, a, f);
822            // Re-calculate the update indexes and update the views followed by
823            // the changed place. Note: we update the views only when insertion or
824            // removal takes place.
825            DocumentEvent.EventType type = e.getType();
826            if (type == DocumentEvent.EventType.INSERT ||
827                type == DocumentEvent.EventType.REMOVE) {
828                firstUpdateIndex = Math.min((lastUpdateIndex + 1), (getViewCount() - 1));
829                lastUpdateIndex = Math.max((getViewCount() - 1), 0);
830                for (int i = firstUpdateIndex; i <= lastUpdateIndex; i++) {
831                    View v = getView(i);
832                    if (v != null) {
833                        v.updateAfterChange();
834                    }
835                }
836            }
837        }
838
839        // The following methods don't do anything useful, they
840        // simply keep the class from being abstract.
841
842        /**
843         * Renders using the given rendering surface and area on that
844         * surface.  This is implemented to do nothing, the logical
845         * view is never visible.
846         *
847         * @param g the rendering surface to use
848         * @param allocation the allocated region to render into
849         * @see View#paint
850         */
851        public void paint(Graphics g, Shape allocation) {
852        }
853
854        /**
855         * Tests whether a point lies before the rectangle range.
856         * Implemented to return false, as hit detection is not
857         * performed on the logical view.
858         *
859         * @param x the X coordinate &gt;= 0
860         * @param y the Y coordinate &gt;= 0
861         * @param alloc the rectangle
862         * @return true if the point is before the specified range
863         */
864        protected boolean isBefore(int x, int y, Rectangle alloc) {
865            return false;
866        }
867
868        /**
869         * Tests whether a point lies after the rectangle range.
870         * Implemented to return false, as hit detection is not
871         * performed on the logical view.
872         *
873         * @param x the X coordinate &gt;= 0
874         * @param y the Y coordinate &gt;= 0
875         * @param alloc the rectangle
876         * @return true if the point is after the specified range
877         */
878        protected boolean isAfter(int x, int y, Rectangle alloc) {
879            return false;
880        }
881
882        /**
883         * Fetches the child view at the given point.
884         * Implemented to return null, as hit detection is not
885         * performed on the logical view.
886         *
887         * @param x the X coordinate &gt;= 0
888         * @param y the Y coordinate &gt;= 0
889         * @param alloc the parent's allocation on entry, which should
890         *   be changed to the child's allocation on exit
891         * @return the child view
892         */
893        protected View getViewAtPoint(int x, int y, Rectangle alloc) {
894            return null;
895        }
896
897        /**
898         * Returns the allocation for a given child.
899         * Implemented to do nothing, as the logical view doesn't
900         * perform layout on the children.
901         *
902         * @param index the index of the child, &gt;= 0 &amp;&amp; &lt; getViewCount()
903         * @param a  the allocation to the interior of the box on entry,
904         *   and the allocation of the child view at the index on exit.
905         */
906        protected void childAllocation(int index, Rectangle a) {
907        }
908    }
909
910
911}
912