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