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