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