1. /*
  2. * @(#)BoxView.java 1.44 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.io.PrintStream;
  12. import java.util.Vector;
  13. import java.awt.*;
  14. import javax.swing.event.DocumentEvent;
  15. import javax.swing.SizeRequirements;
  16. /**
  17. * A view that arranges its children into a box
  18. * shape by tiling it's children along an axis.
  19. * The box is somewhat like that found in TeX where
  20. * there is alignment of the children, flexibility of the
  21. * children is considered, etc. This is considered an
  22. * useful building block that might be useful to represent
  23. * things like a collection of lines, paragraphs, lists,
  24. * columns, pages, etc.
  25. *
  26. * @author Timothy Prinzing
  27. * @version 1.44 02/02/00
  28. */
  29. public class BoxView extends CompositeView {
  30. /**
  31. * Constructs a BoxView.
  32. *
  33. * @param elem the element this view is responsible for
  34. * @param axis either View.X_AXIS or View.Y_AXIS
  35. */
  36. public BoxView(Element elem, int axis) {
  37. super(elem);
  38. tempRect = new Rectangle();
  39. this.axis = axis;
  40. xOffsets = new int[0];
  41. xSpans = new int[0];
  42. xValid = false;
  43. xAllocValid = false;
  44. yOffsets = new int[0];
  45. ySpans = new int[0];
  46. yValid = false;
  47. yAllocValid = false;
  48. }
  49. /**
  50. * Fetch the axis property.
  51. *
  52. * @return the major axis of the box, either
  53. * View.X_AXIS or View.Y_AXIS.
  54. */
  55. public int getAxis() {
  56. return axis;
  57. }
  58. /**
  59. * Set the axis property.
  60. *
  61. * @param axis either View.X_AXIS or View.Y_AXIS
  62. */
  63. public void setAxis(int axis) {
  64. this.axis = axis;
  65. preferenceChanged(null, true, true);
  66. }
  67. /**
  68. * Invalidate the layout along an axis. This happens
  69. * automatically if the preferences have changed for
  70. * any of the child views. In some cases the layout
  71. * may need to be recalculated when the preferences
  72. * have not changed. The layout can be marked as
  73. * invalid by calling this method. The layout will
  74. * be updated the next time the setSize method is called
  75. * on this view (typically in paint).
  76. *
  77. * @param axis either View.X_AXIS or View.Y_AXIS
  78. */
  79. public void layoutChanged(int axis) {
  80. if (axis == X_AXIS) {
  81. xAllocValid = false;
  82. } else {
  83. yAllocValid = false;
  84. }
  85. }
  86. /**
  87. * Paints a child. By default
  88. * that is all it does, but a subclass can use this to paint
  89. * things relative to the child.
  90. *
  91. * @param g the graphics context
  92. * @param alloc the allocated region to paint into
  93. * @param index the child index, >= 0 && < getViewCount()
  94. */
  95. protected void paintChild(Graphics g, Rectangle alloc, int index) {
  96. View child = getView(index);
  97. child.paint(g, alloc);
  98. }
  99. /**
  100. * Invalidates the layout and resizes the cache of
  101. * requests/allocations. The child allocations can still
  102. * be accessed for the old layout, but the new children
  103. * will have an offset and span of 0.
  104. *
  105. * @param index the starting index into the child views to insert
  106. * the new views. This should be a value >= 0 and <= getViewCount.
  107. * @param length the number of existing child views to remove.
  108. * This should be a value >= 0 and <= (getViewCount() - offset).
  109. * @param views the child views to add. This value can be null
  110. * to indicate no children are being added (useful to remove).
  111. */
  112. public void replace(int index, int length, View[] elems) {
  113. super.replace(index, length, elems);
  114. // invalidate cache
  115. int nInserted = (elems != null) ? elems.length : 0;
  116. xOffsets = updateLayoutArray(xOffsets, index, nInserted);
  117. xSpans = updateLayoutArray(xSpans, index, nInserted);
  118. xValid = false;
  119. xAllocValid = false;
  120. yOffsets = updateLayoutArray(yOffsets, index, nInserted);
  121. ySpans = updateLayoutArray(ySpans, index, nInserted);
  122. yValid = false;
  123. yAllocValid = false;
  124. }
  125. /**
  126. * Resize the given layout array to match the new number of
  127. * child views. The current number of child views are used to
  128. * produce the new array. The contents of the old array are
  129. * inserted into the new array at the appropriate places so that
  130. * the old layout information is transferred to the new array.
  131. */
  132. int[] updateLayoutArray(int[] oldArray, int offset, int nInserted) {
  133. int n = getViewCount();
  134. int[] newArray = new int[n];
  135. System.arraycopy(oldArray, 0, newArray, 0, offset);
  136. System.arraycopy(oldArray, offset,
  137. newArray, offset + nInserted, n - nInserted - offset);
  138. return newArray;
  139. }
  140. /**
  141. * Forward the given DocumentEvent to the child views
  142. * that need to be notified of the change to the model.
  143. * If a child changed it's requirements and the allocation
  144. * was valid prior to forwarding the portion of the box
  145. * from the starting child to the end of the box will
  146. * be repainted.
  147. *
  148. * @param ec changes to the element this view is responsible
  149. * for (may be null if there were no changes).
  150. * @param e the change information from the associated document
  151. * @param a the current allocation of the view
  152. * @param f the factory to use to rebuild if the view has children
  153. * @see #insertUpdate
  154. * @see #removeUpdate
  155. * @see #changedUpdate
  156. */
  157. protected void forwardUpdate(DocumentEvent.ElementChange ec,
  158. DocumentEvent e, Shape a, ViewFactory f) {
  159. boolean wasValid = isAllocationValid();
  160. super.forwardUpdate(ec, e, a, f);
  161. // determine if a repaint is needed
  162. if (wasValid && (! isAllocationValid())) {
  163. // repaint is needed, if there is a hosting component and
  164. // and an allocated shape.
  165. Component c = getContainer();
  166. if ((a != null) && (c != null)) {
  167. int pos = e.getOffset();
  168. int index = getViewIndexAtPosition(pos);
  169. Rectangle alloc = getInsideAllocation(a);
  170. if (axis == X_AXIS) {
  171. alloc.x += xOffsets[index];
  172. alloc.width -= xSpans[index];
  173. } else {
  174. alloc.y += yOffsets[index];
  175. alloc.height -= ySpans[index];
  176. }
  177. c.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
  178. }
  179. }
  180. }
  181. // --- View methods ---------------------------------------------
  182. /**
  183. * This is called by a child to indicated its
  184. * preferred span has changed. This is implemented to
  185. * throw away cached layout information so that new
  186. * calculations will be done the next time the children
  187. * need an allocation.
  188. *
  189. * @param child the child view
  190. * @param width true if the width preference should change
  191. * @param height true if the height preference should change
  192. */
  193. public void preferenceChanged(View child, boolean width, boolean height) {
  194. if (width) {
  195. xValid = false;
  196. xAllocValid = false;
  197. }
  198. if (height) {
  199. yValid = false;
  200. yAllocValid = false;
  201. }
  202. super.preferenceChanged(child, width, height);
  203. }
  204. /**
  205. * Gets the resize weight. A value of 0 or less is not resizable.
  206. *
  207. * @param axis may be either View.X_AXIS or View.Y_AXIS
  208. * @return the weight
  209. * @exception IllegalArgumentException for an invalid axis
  210. */
  211. public int getResizeWeight(int axis) {
  212. checkRequests();
  213. switch (axis) {
  214. case View.X_AXIS:
  215. if ((xRequest.preferred != xRequest.minimum) &&
  216. (xRequest.preferred != xRequest.maximum)) {
  217. return 1;
  218. }
  219. return 0;
  220. case View.Y_AXIS:
  221. if ((yRequest.preferred != yRequest.minimum) &&
  222. (yRequest.preferred != yRequest.maximum)) {
  223. return 1;
  224. }
  225. return 0;
  226. default:
  227. throw new IllegalArgumentException("Invalid axis: " + axis);
  228. }
  229. }
  230. /**
  231. * Sets the size of the view. If the size has changed, layout
  232. * is redone. The size is the full size of the view including
  233. * the inset areas.
  234. *
  235. * @param width the width >= 0
  236. * @param height the height >= 0
  237. */
  238. public void setSize(float width, float height) {
  239. if (((int) width) != this.width) {
  240. xAllocValid = false;
  241. }
  242. if (((int) height) != this.height) {
  243. yAllocValid = false;
  244. }
  245. if ((! xAllocValid) || (! yAllocValid)) {
  246. this.width = (int) width;
  247. this.height = (int) height;
  248. layout(this.width - getLeftInset() - getRightInset(),
  249. this.height - getTopInset() - getBottomInset());
  250. }
  251. }
  252. /**
  253. * Renders using the given rendering surface and area
  254. * on that surface. Only the children that intersect
  255. * the clip bounds of the given Graphics will be
  256. * rendered.
  257. *
  258. * @param g the rendering surface to use
  259. * @param allocation the allocated region to render into
  260. * @see View#paint
  261. */
  262. public void paint(Graphics g, Shape allocation) {
  263. Rectangle alloc = (allocation instanceof Rectangle) ?
  264. (Rectangle)allocation : allocation.getBounds();
  265. setSize(alloc.width, alloc.height);
  266. int n = getViewCount();
  267. int x = alloc.x + getLeftInset();
  268. int y = alloc.y + getTopInset();
  269. Rectangle clip = g.getClipBounds();
  270. for (int i = 0; i < n; i++) {
  271. tempRect.x = x + xOffsets[i];
  272. tempRect.y = y + yOffsets[i];
  273. tempRect.width = xSpans[i];
  274. tempRect.height = ySpans[i];
  275. if (tempRect.intersects(clip)) {
  276. paintChild(g, tempRect, i);
  277. }
  278. }
  279. }
  280. /**
  281. * Fetches the allocation for the given child view.
  282. * This enables finding out where various views
  283. * are located. This is implemented to return null
  284. * if the layout is invalid, otherwise the
  285. * superclass behavior is executed.
  286. *
  287. * @param index the index of the child, >= 0 && < getViewCount()
  288. * @param a the allocation to this view.
  289. * @return the allocation to the child
  290. */
  291. public Shape getChildAllocation(int index, Shape a) {
  292. if (a != null) {
  293. Shape ca = super.getChildAllocation(index, a);
  294. if ((ca != null) && (! isAllocationValid())) {
  295. // The child allocation may not have been set yet.
  296. Rectangle r = (ca instanceof Rectangle) ?
  297. (Rectangle) ca : ca.getBounds();
  298. if ((r.width == 0) && (r.height == 0)) {
  299. return null;
  300. }
  301. }
  302. return ca;
  303. }
  304. return null;
  305. }
  306. /**
  307. * Provides a mapping from the document model coordinate space
  308. * to the coordinate space of the view mapped to it. This makes
  309. * sure the allocation is valid before letting the superclass
  310. * do its thing.
  311. *
  312. * @param pos the position to convert >= 0
  313. * @param a the allocated region to render into
  314. * @return the bounding box of the given position
  315. * @exception BadLocationException if the given position does
  316. * not represent a valid location in the associated document
  317. * @see View#modelToView
  318. */
  319. public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {
  320. if (! isAllocationValid()) {
  321. Rectangle alloc = a.getBounds();
  322. setSize(alloc.width, alloc.height);
  323. }
  324. return super.modelToView(pos, a, b);
  325. }
  326. /**
  327. * Provides a mapping from the view coordinate space to the logical
  328. * coordinate space of the model.
  329. *
  330. * @param x x coordinate of the view location to convert >= 0
  331. * @param y y coordinate of the view location to convert >= 0
  332. * @param a the allocated region to render into
  333. * @return the location within the model that best represents the
  334. * given point in the view >= 0
  335. * @see View#viewToModel
  336. */
  337. public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) {
  338. if (! isAllocationValid()) {
  339. Rectangle alloc = a.getBounds();
  340. setSize(alloc.width, alloc.height);
  341. }
  342. return super.viewToModel(x, y, a, bias);
  343. }
  344. /**
  345. * Determines the desired alignment for this view along an
  346. * axis. This is implemented to give the total alignment
  347. * needed to position the children with the alignment points
  348. * lined up along the axis orthoginal to the axis that is
  349. * being tiled. The axis being tiled will request to be
  350. * centered (i.e. 0.5f).
  351. *
  352. * @param axis may be either View.X_AXIS or View.Y_AXIS
  353. * @returns the desired alignment >= 0.0f && <= 1.0f. This should
  354. * be a value between 0.0 and 1.0 where 0 indicates alignment at the
  355. * origin and 1.0 indicates alignment to the full span
  356. * away from the origin. An alignment of 0.5 would be the
  357. * center of the view.
  358. * @exception IllegalArgumentException for an invalid axis
  359. */
  360. public float getAlignment(int axis) {
  361. checkRequests();
  362. switch (axis) {
  363. case View.X_AXIS:
  364. return xRequest.alignment;
  365. case View.Y_AXIS:
  366. return yRequest.alignment;
  367. default:
  368. throw new IllegalArgumentException("Invalid axis: " + axis);
  369. }
  370. }
  371. /**
  372. * Determines the preferred span for this view along an
  373. * axis.
  374. *
  375. * @param axis may be either View.X_AXIS or View.Y_AXIS
  376. * @returns the span the view would like to be rendered into >= 0.
  377. * Typically the view is told to render into the span
  378. * that is returned, although there is no guarantee.
  379. * The parent may choose to resize or break the view.
  380. * @exception IllegalArgumentException for an invalid axis type
  381. */
  382. public float getPreferredSpan(int axis) {
  383. checkRequests();
  384. switch (axis) {
  385. case View.X_AXIS:
  386. return ((float)xRequest.preferred) + getLeftInset() + getRightInset();
  387. case View.Y_AXIS:
  388. return ((float)yRequest.preferred) + getTopInset() + getBottomInset();
  389. default:
  390. throw new IllegalArgumentException("Invalid axis: " + axis);
  391. }
  392. }
  393. /**
  394. * Determines the minimum span for this view along an
  395. * axis.
  396. *
  397. * @param axis may be either View.X_AXIS or View.Y_AXIS
  398. * @returns the span the view would like to be rendered into >= 0.
  399. * Typically the view is told to render into the span
  400. * that is returned, although there is no guarantee.
  401. * The parent may choose to resize or break the view.
  402. * @exception IllegalArgumentException for an invalid axis type
  403. */
  404. public float getMinimumSpan(int axis) {
  405. checkRequests();
  406. switch (axis) {
  407. case View.X_AXIS:
  408. return ((float)xRequest.minimum) + getLeftInset() + getRightInset();
  409. case View.Y_AXIS:
  410. return ((float)yRequest.minimum) + getTopInset() + getBottomInset();
  411. default:
  412. throw new IllegalArgumentException("Invalid axis: " + axis);
  413. }
  414. }
  415. /**
  416. * Determines the maximum span for this view along an
  417. * axis.
  418. *
  419. * @param axis may be either View.X_AXIS or View.Y_AXIS
  420. * @returns the span the view would like to be rendered into >= 0.
  421. * Typically the view is told to render into the span
  422. * that is returned, although there is no guarantee.
  423. * The parent may choose to resize or break the view.
  424. * @exception IllegalArgumentException for an invalid axis type
  425. */
  426. public float getMaximumSpan(int axis) {
  427. checkRequests();
  428. switch (axis) {
  429. case View.X_AXIS:
  430. return ((float)xRequest.maximum) + getLeftInset() + getRightInset();
  431. case View.Y_AXIS:
  432. return ((float)yRequest.maximum) + getTopInset() + getBottomInset();
  433. default:
  434. throw new IllegalArgumentException("Invalid axis: " + axis);
  435. }
  436. }
  437. // --- local methods ----------------------------------------------------
  438. /**
  439. * Are the allocations for the children still
  440. * valid?
  441. *
  442. * @return true if allocations still valid
  443. */
  444. protected boolean isAllocationValid() {
  445. return (xAllocValid && yAllocValid);
  446. }
  447. /**
  448. * Determines if a point falls before an allocated region.
  449. *
  450. * @param x the X coordinate >= 0
  451. * @param y the Y coordinate >= 0
  452. * @param innerAlloc the allocated region. This is the area
  453. * inside of the insets.
  454. * @return true if the point lies before the region else false
  455. */
  456. protected boolean isBefore(int x, int y, Rectangle innerAlloc) {
  457. if (axis == View.X_AXIS) {
  458. return (x < innerAlloc.x);
  459. } else {
  460. return (y < innerAlloc.y);
  461. }
  462. }
  463. /**
  464. * Determines if a point falls after an allocated region.
  465. *
  466. * @param x the X coordinate >= 0
  467. * @param y the Y coordinate >= 0
  468. * @param innerAlloc the allocated region. This is the area
  469. * inside of the insets.
  470. * @return true if the point lies after the region else false
  471. */
  472. protected boolean isAfter(int x, int y, Rectangle innerAlloc) {
  473. if (axis == View.X_AXIS) {
  474. return (x > (innerAlloc.width + innerAlloc.x));
  475. } else {
  476. return (y > (innerAlloc.height + innerAlloc.y));
  477. }
  478. }
  479. /**
  480. * Fetches the child view at the given point.
  481. *
  482. * @param x the X coordinate >= 0
  483. * @param y the Y coordinate >= 0
  484. * @param alloc the parents inner allocation on entry, which should
  485. * be changed to the childs allocation on exit.
  486. * @return the view
  487. */
  488. protected View getViewAtPoint(int x, int y, Rectangle alloc) {
  489. int n = getViewCount();
  490. if (axis == View.X_AXIS) {
  491. if (x < (alloc.x + xOffsets[0])) {
  492. childAllocation(0, alloc);
  493. return getView(0);
  494. }
  495. for (int i = 0; i < n; i++) {
  496. if (x < (alloc.x + xOffsets[i])) {
  497. childAllocation(i - 1, alloc);
  498. return getView(i - 1);
  499. }
  500. }
  501. childAllocation(n - 1, alloc);
  502. return getView(n - 1);
  503. } else {
  504. if (y < (alloc.y + yOffsets[0])) {
  505. childAllocation(0, alloc);
  506. return getView(0);
  507. }
  508. for (int i = 0; i < n; i++) {
  509. if (y < (alloc.y + yOffsets[i])) {
  510. childAllocation(i - 1, alloc);
  511. return getView(i - 1);
  512. }
  513. }
  514. childAllocation(n - 1, alloc);
  515. return getView(n - 1);
  516. }
  517. }
  518. /**
  519. * Allocates a region for a child view.
  520. *
  521. * @param index the index of the child view to
  522. * allocate, >= 0 && < getViewCount()
  523. * @param alloc the allocated region
  524. */
  525. protected void childAllocation(int index, Rectangle alloc) {
  526. alloc.x += xOffsets[index];
  527. alloc.y += yOffsets[index];
  528. alloc.width = xSpans[index];
  529. alloc.height = ySpans[index];
  530. }
  531. /**
  532. * Performs layout of the children. The size is the
  533. * area inside of the insets. This method calls
  534. * the methods
  535. * <a href="#layoutMajorAxis">layoutMajorAxis</a> and
  536. * <a href="#layoutMinorAxis">layoutMinorAxis</a> as
  537. * needed. To change how layout is done those methods
  538. * should be reimplemented.
  539. *
  540. * @param width the width >= 0
  541. * @param height the height >= 0
  542. */
  543. protected void layout(int width, int height) {
  544. checkRequests();
  545. if (axis == X_AXIS) {
  546. if (! xAllocValid) {
  547. layoutMajorAxis(width, X_AXIS, xOffsets, xSpans);
  548. }
  549. if (! yAllocValid) {
  550. layoutMinorAxis(height, Y_AXIS, yOffsets, ySpans);
  551. }
  552. } else {
  553. if (! xAllocValid) {
  554. layoutMinorAxis(width, X_AXIS, xOffsets, xSpans);
  555. }
  556. if (! yAllocValid) {
  557. layoutMajorAxis(height, Y_AXIS, yOffsets, ySpans);
  558. }
  559. }
  560. xAllocValid = true;
  561. yAllocValid = true;
  562. // flush changes to the children
  563. int n = getViewCount();
  564. for (int i = 0; i < n; i++) {
  565. View v = getView(i);
  566. v.setSize((float) xSpans[i], (float) ySpans[i]);
  567. }
  568. }
  569. /**
  570. * The current width of the box. This is the width that
  571. * it was last allocated.
  572. */
  573. public int getWidth() {
  574. return width;
  575. }
  576. /**
  577. * The current height of the box. This is the height that
  578. * it was last allocated.
  579. */
  580. public int getHeight() {
  581. return height;
  582. }
  583. /**
  584. * Perform layout for the major axis of the box (i.e. the
  585. * axis that it represents). The results of the layout should
  586. * be placed in the given arrays which represent the allocations
  587. * to the children along the major axis.
  588. *
  589. * @param targetSpan the total span given to the view, which
  590. * whould be used to layout the children.
  591. * @param axis the axis being layed out.
  592. * @param offsets the offsets from the origin of the view for
  593. * each of the child views. This is a return value and is
  594. * filled in by the implementation of this method.
  595. * @param spans the span of each child view. This is a return
  596. * value and is filled in by the implementation of this method.
  597. * @returns the offset and span for each child view in the
  598. * offsets and spans parameters.
  599. */
  600. protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
  601. /*
  602. * first pass, calculate the preferred sizes
  603. * and the flexibility to adjust the sizes.
  604. */
  605. long minimum = 0;
  606. long maximum = 0;
  607. long preferred = 0;
  608. int n = getViewCount();
  609. for (int i = 0; i < n; i++) {
  610. View v = getView(i);
  611. spans[i] = (int) v.getPreferredSpan(axis);
  612. preferred += spans[i];
  613. minimum += v.getMinimumSpan(axis);
  614. maximum += v.getMaximumSpan(axis);
  615. }
  616. /*
  617. * Second pass, expand or contract by as much as possible to reach
  618. * the target span.
  619. */
  620. // determine the adjustment to be made
  621. long desiredAdjustment = targetSpan - preferred;
  622. float adjustmentFactor = 0.0f;
  623. if (desiredAdjustment != 0) {
  624. float maximumAdjustment = (desiredAdjustment > 0) ?
  625. maximum - preferred : preferred - minimum;
  626. if (maximumAdjustment == 0.0f) {
  627. adjustmentFactor = 0.0f;
  628. }
  629. else {
  630. adjustmentFactor = desiredAdjustment / maximumAdjustment;
  631. adjustmentFactor = Math.min(adjustmentFactor, 1.0f);
  632. adjustmentFactor = Math.max(adjustmentFactor, -1.0f);
  633. }
  634. }
  635. // make the adjustments
  636. int totalOffset = 0;
  637. for (int i = 0; i < n; i++) {
  638. View v = getView(i);
  639. offsets[i] = totalOffset;
  640. int availableSpan = (adjustmentFactor > 0.0f) ?
  641. (int) v.getMaximumSpan(axis) - spans[i] :
  642. spans[i] - (int) v.getMinimumSpan(axis);
  643. float adjF = adjustmentFactor * availableSpan;
  644. if (adjF < 0) {
  645. adjF -= .5f;
  646. }
  647. else {
  648. adjF += .5f;
  649. }
  650. int adj = (int)adjF;
  651. spans[i] += adj;
  652. totalOffset = (int) Math.min((long) totalOffset + (long) spans[i], Integer.MAX_VALUE);
  653. }
  654. }
  655. /**
  656. * Perform layout for the minor axis of the box (i.e. the
  657. * axis orthoginal to the axis that it represents). The results
  658. * of the layout should be placed in the given arrays which represent
  659. * the allocations to the children along the minor axis.
  660. *
  661. * @param targetSpan the total span given to the view, which
  662. * whould be used to layout the children.
  663. * @param axis the axis being layed out.
  664. * @param offsets the offsets from the origin of the view for
  665. * each of the child views. This is a return value and is
  666. * filled in by the implementation of this method.
  667. * @param spans the span of each child view. This is a return
  668. * value and is filled in by the implementation of this method.
  669. * @returns the offset and span for each child view in the
  670. * offsets and spans parameters.
  671. */
  672. protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
  673. int n = getViewCount();
  674. for (int i = 0; i < n; i++) {
  675. View v = getView(i);
  676. int min = (int) v.getMinimumSpan(axis);
  677. int max = (int) v.getMaximumSpan(axis);
  678. if (max < targetSpan) {
  679. // can't make the child this wide, align it
  680. float align = v.getAlignment(axis);
  681. offsets[i] = (int) ((targetSpan - max) * align);
  682. spans[i] = max;
  683. } else {
  684. // make it the target width, or as small as it can get.
  685. offsets[i] = 0;
  686. spans[i] = Math.max(min, targetSpan);
  687. }
  688. }
  689. }
  690. protected SizeRequirements calculateMajorAxisRequirements(int axis, SizeRequirements r) {
  691. // calculate tiled request
  692. float min = 0;
  693. float pref = 0;
  694. float max = 0;
  695. int n = getViewCount();
  696. for (int i = 0; i < n; i++) {
  697. View v = getView(i);
  698. min += v.getMinimumSpan(axis);
  699. pref += v.getPreferredSpan(axis);
  700. max += v.getMaximumSpan(axis);
  701. }
  702. if (r == null) {
  703. r = new SizeRequirements();
  704. }
  705. r.alignment = 0.5f;
  706. r.minimum = (int) min;
  707. r.preferred = (int) pref;
  708. r.maximum = (int) max;
  709. return r;
  710. }
  711. protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
  712. int min = 0;
  713. long pref = 0;
  714. int max = Integer.MAX_VALUE;
  715. int n = getViewCount();
  716. for (int i = 0; i < n; i++) {
  717. View v = getView(i);
  718. min = Math.max((int) v.getMinimumSpan(axis), min);
  719. pref = Math.max((int) v.getPreferredSpan(axis), pref);
  720. max = Math.max((int) v.getMaximumSpan(axis), max);
  721. }
  722. if (r == null) {
  723. r = new SizeRequirements();
  724. r.alignment = 0.5f;
  725. }
  726. r.preferred = (int) pref;
  727. r.minimum = min;
  728. r.maximum = max;
  729. return r;
  730. }
  731. /**
  732. * Checks the request cache and update if needed.
  733. */
  734. void checkRequests() {
  735. if (axis == X_AXIS) {
  736. if (! xValid) {
  737. xRequest = calculateMajorAxisRequirements(X_AXIS, xRequest);
  738. }
  739. if (! yValid) {
  740. yRequest = calculateMinorAxisRequirements(Y_AXIS, yRequest);
  741. }
  742. } else {
  743. if (! xValid) {
  744. xRequest = calculateMinorAxisRequirements(X_AXIS, xRequest);
  745. }
  746. if (! yValid) {
  747. yRequest = calculateMajorAxisRequirements(Y_AXIS, yRequest);
  748. }
  749. }
  750. yValid = true;
  751. xValid = true;
  752. }
  753. protected void baselineLayout(int targetSpan, int axis, int[] offsets, int[] spans) {
  754. int totalBelow = (int) (targetSpan * getAlignment(axis));
  755. int totalAbove = targetSpan - totalBelow;
  756. int n = getViewCount();
  757. for (int i = 0; i < n; i++) {
  758. View v = getView(i);
  759. float align = v.getAlignment(axis);
  760. int span = (int) v.getPreferredSpan(axis);
  761. int below = (int) (span * align);
  762. int above = span - below;
  763. if (span > targetSpan) {
  764. // check compress
  765. if ((int) v.getMinimumSpan(axis) < span) {
  766. below = totalBelow;
  767. above = totalAbove;
  768. } else {
  769. if ((v.getResizeWeight(axis) > 0) && (v.getMaximumSpan(axis) != span)) {
  770. throw new Error("should not happen: " + v.getClass());
  771. }
  772. }
  773. } else if (span < targetSpan) {
  774. // check expand
  775. if ((int) v.getMaximumSpan(axis) > span) {
  776. below = totalBelow;
  777. above = totalAbove;
  778. }
  779. }
  780. /*
  781. if (v.getResizeWeight(axis) > 0) {
  782. below = totalBelow;
  783. above = totalAbove;
  784. }
  785. */
  786. offsets[i] = totalBelow - below;
  787. spans[i] = below + above;
  788. }
  789. }
  790. protected SizeRequirements baselineRequirements(int axis, SizeRequirements r) {
  791. int totalAbove = 0;
  792. int totalBelow = 0;
  793. int resizeWeight = 0;
  794. int n = getViewCount();
  795. for (int i = 0; i < n; i++) {
  796. View v = getView(i);
  797. int span = (int) v.getPreferredSpan(axis);
  798. int below = (int) (v.getAlignment(axis) * span);
  799. int above = span - below;
  800. totalAbove = Math.max(above, totalAbove);
  801. totalBelow = Math.max(below, totalBelow);
  802. resizeWeight += v.getResizeWeight(axis);
  803. }
  804. if (r == null) {
  805. r = new SizeRequirements();
  806. }
  807. r.preferred = totalAbove + totalBelow;
  808. if (resizeWeight != 0) {
  809. r.maximum = Integer.MAX_VALUE;
  810. r.minimum = 0;
  811. } else {
  812. r.maximum = r.preferred;
  813. r.minimum = r.preferred;
  814. }
  815. if (r.preferred > 0) {
  816. r.alignment = (float) totalBelow / r.preferred;
  817. } else {
  818. r.alignment = 0.5f;
  819. }
  820. return r;
  821. }
  822. /**
  823. * Fetch the offset of a particular childs current layout
  824. */
  825. protected int getOffset(int axis, int childIndex) {
  826. int[] offsets = (axis == X_AXIS) ? xOffsets : yOffsets;
  827. return offsets[childIndex];
  828. }
  829. /**
  830. * Fetch the span of a particular childs current layout
  831. */
  832. protected int getSpan(int axis, int childIndex) {
  833. int[] spans = (axis == X_AXIS) ? xSpans : ySpans;
  834. return spans[childIndex];
  835. }
  836. protected boolean flipEastAndWestAtEnds(int position,
  837. Position.Bias bias) {
  838. if(axis == Y_AXIS) {
  839. int testPos = (bias == Position.Bias.Backward) ?
  840. Math.max(0, position - 1) : position;
  841. int index = getViewIndexAtPosition(testPos);
  842. if(index != -1) {
  843. View v = getView(index);
  844. if(v != null && v instanceof CompositeView) {
  845. return ((CompositeView)v).flipEastAndWestAtEnds(position,
  846. bias);
  847. }
  848. }
  849. }
  850. return false;
  851. }
  852. // --- variables ------------------------------------------------
  853. int axis;
  854. int width;
  855. int height;
  856. /*
  857. * Request cache
  858. */
  859. boolean xValid;
  860. boolean yValid;
  861. SizeRequirements xRequest;
  862. SizeRequirements yRequest;
  863. /*
  864. * Allocation cache
  865. */
  866. boolean xAllocValid;
  867. int[] xOffsets;
  868. int[] xSpans;
  869. boolean yAllocValid;
  870. int[] yOffsets;
  871. int[] ySpans;
  872. /** used in paint. */
  873. Rectangle tempRect;
  874. }