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