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