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