1. /*
  2. * @(#)TableView.java 1.32 03/01/23
  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.html;
  8. import java.awt.*;
  9. import java.util.BitSet;
  10. import java.util.Vector;
  11. import javax.swing.SizeRequirements;
  12. import javax.swing.event.DocumentEvent;
  13. import javax.swing.text.*;
  14. /**
  15. * HTML table view.
  16. *
  17. * @author Timothy Prinzing
  18. * @version 1.32 01/23/03
  19. * @see View
  20. */
  21. /*public*/ class TableView extends BoxView implements ViewFactory {
  22. /**
  23. * Constructs a TableView for the given element.
  24. *
  25. * @param elem the element that this view is responsible for
  26. */
  27. public TableView(Element elem) {
  28. super(elem, View.Y_AXIS);
  29. rows = new Vector();
  30. gridValid = false;
  31. captionIndex = -1;
  32. totalColumnRequirements = new SizeRequirements();
  33. }
  34. /**
  35. * Creates a new table row.
  36. *
  37. * @param elem an element
  38. * @return the row
  39. */
  40. protected RowView createTableRow(Element elem) {
  41. // PENDING(prinz) need to add support for some of the other
  42. // elements, but for now just ignore anything that is not
  43. // a TR.
  44. Object o = elem.getAttributes().getAttribute(StyleConstants.NameAttribute);
  45. if (o == HTML.Tag.TR) {
  46. return new RowView(elem);
  47. }
  48. return null;
  49. }
  50. /**
  51. * The number of columns in the table.
  52. */
  53. public int getColumnCount() {
  54. return columnSpans.length;
  55. }
  56. /**
  57. * Fetches the span (width) of the given column.
  58. * This is used by the nested cells to query the
  59. * sizes of grid locations outside of themselves.
  60. */
  61. public int getColumnSpan(int col) {
  62. if (col < columnSpans.length) {
  63. return columnSpans[col];
  64. }
  65. return 0;
  66. }
  67. /**
  68. * The number of rows in the table.
  69. */
  70. public int getRowCount() {
  71. return rows.size();
  72. }
  73. /**
  74. * Fetch the span of multiple rows. This includes
  75. * the border area.
  76. */
  77. public int getMultiRowSpan(int row0, int row1) {
  78. RowView rv0 = getRow(row0);
  79. RowView rv1 = getRow(row1);
  80. if ((rv0 != null) && (rv1 != null)) {
  81. int index0 = rv0.viewIndex;
  82. int index1 = rv1.viewIndex;
  83. int span = getOffset(Y_AXIS, index1) - getOffset(Y_AXIS, index0) +
  84. getSpan(Y_AXIS, index1);
  85. return span;
  86. }
  87. return 0;
  88. }
  89. /**
  90. * Fetches the span (height) of the given row.
  91. */
  92. public int getRowSpan(int row) {
  93. RowView rv = getRow(row);
  94. if (rv != null) {
  95. return getSpan(Y_AXIS, rv.viewIndex);
  96. }
  97. return 0;
  98. }
  99. RowView getRow(int row) {
  100. if (row < rows.size()) {
  101. return (RowView) rows.elementAt(row);
  102. }
  103. return null;
  104. }
  105. protected View getViewAtPoint(int x, int y, Rectangle alloc) {
  106. int n = getViewCount();
  107. View v = null;
  108. Rectangle allocation = new Rectangle();
  109. for (int i = 0; i < n; i++) {
  110. allocation.setBounds(alloc);
  111. childAllocation(i, allocation);
  112. v = getView(i);
  113. if (v instanceof RowView) {
  114. v = ((RowView)v).findViewAtPoint(x, y, allocation);
  115. if (v != null) {
  116. alloc.setBounds(allocation);
  117. return v;
  118. }
  119. }
  120. }
  121. return super.getViewAtPoint(x, y, alloc);
  122. }
  123. /**
  124. * Determines the number of columns occupied by
  125. * the table cell represented by given element.
  126. */
  127. protected int getColumnsOccupied(View v) {
  128. AttributeSet a = v.getElement().getAttributes();
  129. if (a.isDefined(HTML.Attribute.COLSPAN)) {
  130. String s = (String) a.getAttribute(HTML.Attribute.COLSPAN);
  131. if (s != null) {
  132. try {
  133. return Integer.parseInt(s);
  134. } catch (NumberFormatException nfe) {
  135. // fall through to one column
  136. }
  137. }
  138. }
  139. return 1;
  140. }
  141. /**
  142. * Determines the number of rows occupied by
  143. * the table cell represented by given element.
  144. */
  145. protected int getRowsOccupied(View v) {
  146. AttributeSet a = v.getElement().getAttributes();
  147. if (a.isDefined(HTML.Attribute.ROWSPAN)) {
  148. String s = (String) a.getAttribute(HTML.Attribute.ROWSPAN);
  149. if (s != null) {
  150. try {
  151. return Integer.parseInt(s);
  152. } catch (NumberFormatException nfe) {
  153. // fall through to one row
  154. }
  155. }
  156. }
  157. return 1;
  158. }
  159. protected void invalidateGrid() {
  160. gridValid = false;
  161. }
  162. protected StyleSheet getStyleSheet() {
  163. HTMLDocument doc = (HTMLDocument) getDocument();
  164. return doc.getStyleSheet();
  165. }
  166. /**
  167. * Update the insets, which contain the caption if there
  168. * is a caption.
  169. */
  170. void updateInsets() {
  171. short top = (short) painter.getInset(TOP, this);
  172. short bottom = (short) painter.getInset(BOTTOM, this);
  173. if (captionIndex != -1) {
  174. View caption = getView(captionIndex);
  175. short h = (short) caption.getPreferredSpan(Y_AXIS);
  176. AttributeSet a = caption.getAttributes();
  177. Object align = a.getAttribute(CSS.Attribute.CAPTION_SIDE);
  178. if ((align != null) && (align.equals("bottom"))) {
  179. bottom += h;
  180. } else {
  181. top += h;
  182. }
  183. }
  184. setInsets(top, (short) painter.getInset(LEFT, this),
  185. bottom, (short) painter.getInset(RIGHT, this));
  186. }
  187. /**
  188. * Update any cached values that come from attributes.
  189. */
  190. protected void setPropertiesFromAttributes() {
  191. StyleSheet sheet = getStyleSheet();
  192. attr = sheet.getViewAttributes(this);
  193. painter = sheet.getBoxPainter(attr);
  194. if (attr != null) {
  195. setInsets((short) painter.getInset(TOP, this),
  196. (short) painter.getInset(LEFT, this),
  197. (short) painter.getInset(BOTTOM, this),
  198. (short) painter.getInset(RIGHT, this));
  199. /*
  200. CSS.LengthValue lv = (CSS.LengthValue)
  201. attr.getAttribute(CSS.Attribute.BORDER_SPACING);
  202. if (lv != null) {
  203. cellSpacing = (int) lv.getValue();
  204. } else {
  205. cellSpacing = 0;
  206. }
  207. */
  208. }
  209. }
  210. /**
  211. * Fill in the grid locations that are placeholders
  212. * for multi-column, multi-row, and missing grid
  213. * locations.
  214. */
  215. void updateGrid() {
  216. if (! gridValid) {
  217. relativeCells = false;
  218. multiRowCells = false;
  219. // determine which views are table rows and clear out
  220. // grid points marked filled.
  221. captionIndex = -1;
  222. rows.removeAllElements();
  223. int n = getViewCount();
  224. for (int i = 0; i < n; i++) {
  225. View v = getView(i);
  226. if (v instanceof RowView) {
  227. rows.addElement(v);
  228. RowView rv = (RowView) v;
  229. rv.clearFilledColumns();
  230. rv.rowIndex = rows.size() - 1;
  231. rv.viewIndex = i;
  232. } else {
  233. Object o = v.getElement().getAttributes().getAttribute(StyleConstants.NameAttribute);
  234. if (o instanceof HTML.Tag) {
  235. HTML.Tag kind = (HTML.Tag) o;
  236. if (kind == HTML.Tag.CAPTION) {
  237. captionIndex = i;
  238. }
  239. }
  240. }
  241. }
  242. int maxColumns = 0;
  243. int nrows = rows.size();
  244. for (int row = 0; row < nrows; row++) {
  245. RowView rv = getRow(row);
  246. int col = 0;
  247. for (int cell = 0; cell < rv.getViewCount(); cell++, col++) {
  248. View cv = rv.getView(cell);
  249. if (! relativeCells) {
  250. AttributeSet a = cv.getAttributes();
  251. CSS.LengthValue lv = (CSS.LengthValue)
  252. a.getAttribute(CSS.Attribute.WIDTH);
  253. if ((lv != null) && (lv.isPercentage())) {
  254. relativeCells = true;
  255. }
  256. }
  257. // advance to a free column
  258. for (; rv.isFilled(col); col++);
  259. int rowSpan = getRowsOccupied(cv);
  260. if (rowSpan > 1) {
  261. multiRowCells = true;
  262. }
  263. int colSpan = getColumnsOccupied(cv);
  264. if ((colSpan > 1) || (rowSpan > 1)) {
  265. // fill in the overflow entries for this cell
  266. int rowLimit = row + rowSpan;
  267. int colLimit = col + colSpan;
  268. for (int i = row; i < rowLimit; i++) {
  269. for (int j = col; j < colLimit; j++) {
  270. if (i != row || j != col) {
  271. addFill(i, j);
  272. }
  273. }
  274. }
  275. if (colSpan > 1) {
  276. col += colSpan - 1;
  277. }
  278. }
  279. }
  280. maxColumns = Math.max(maxColumns, col);
  281. }
  282. // setup the column layout/requirements
  283. columnSpans = new int[maxColumns];
  284. columnOffsets = new int[maxColumns];
  285. columnRequirements = new SizeRequirements[maxColumns];
  286. for (int i = 0; i < maxColumns; i++) {
  287. columnRequirements[i] = new SizeRequirements();
  288. columnRequirements[i].maximum = Integer.MAX_VALUE;
  289. }
  290. gridValid = true;
  291. }
  292. }
  293. /**
  294. * Mark a grid location as filled in for a cells overflow.
  295. */
  296. void addFill(int row, int col) {
  297. RowView rv = getRow(row);
  298. if (rv != null) {
  299. rv.fillColumn(col);
  300. }
  301. }
  302. /**
  303. * Layout the columns to fit within the given target span.
  304. *
  305. * @param targetSpan the given span for total of all the table
  306. * columns
  307. * @param reqs the requirements desired for each column. This
  308. * is the column maximum of the cells minimum, preferred, and
  309. * maximum requested span
  310. * @param spans the return value of how much to allocated to
  311. * each column
  312. * @param offsets the return value of the offset from the
  313. * origin for each column
  314. * @return the offset from the origin and the span for each column
  315. * in the offsets and spans parameters
  316. */
  317. protected void layoutColumns(int targetSpan, int[] offsets, int[] spans,
  318. SizeRequirements[] reqs) {
  319. colIterator.setLayoutArrays(offsets, spans, targetSpan);
  320. CSS.calculateTiledLayout(colIterator, targetSpan);
  321. }
  322. /**
  323. * Calculate the requirements for each column. The calculation
  324. * is done as two passes over the table. The table cells that
  325. * occupy a single column are scanned first to determine the
  326. * maximum of minimum, preferred, and maximum spans along the
  327. * give axis. Table cells that span multiple columns are excluded
  328. * from the first pass. A second pass is made to determine if
  329. * the cells that span multiple columns are satisfied. If the
  330. * column requirements are not satisified, the needs of the
  331. * multi-column cell is mixed into the existing column requirements.
  332. * The calculation of the multi-column distribution is based upon
  333. * the proportions of the existing column requirements and taking
  334. * into consideration any constraining maximums.
  335. */
  336. void calculateColumnRequirements(int axis) {
  337. // pass 1 - single column cells
  338. boolean hasMultiColumn = false;
  339. int nrows = getRowCount();
  340. for (int i = 0; i < nrows; i++) {
  341. RowView row = getRow(i);
  342. int col = 0;
  343. int ncells = row.getViewCount();
  344. for (int cell = 0; cell < ncells; cell++, col++) {
  345. View cv = row.getView(cell);
  346. for (; row.isFilled(col); col++); // advance to a free column
  347. int rowSpan = getRowsOccupied(cv);
  348. int colSpan = getColumnsOccupied(cv);
  349. if (colSpan == 1) {
  350. checkSingleColumnCell(axis, col, cv);
  351. } else {
  352. hasMultiColumn = true;
  353. col += colSpan - 1;
  354. }
  355. }
  356. }
  357. // pass 2 - multi-column cells
  358. if (hasMultiColumn) {
  359. for (int i = 0; i < nrows; i++) {
  360. RowView row = getRow(i);
  361. int col = 0;
  362. int ncells = row.getViewCount();
  363. for (int cell = 0; cell < ncells; cell++, col++) {
  364. View cv = row.getView(cell);
  365. for (; row.isFilled(col); col++); // advance to a free column
  366. int colSpan = getColumnsOccupied(cv);
  367. if (colSpan > 1) {
  368. checkMultiColumnCell(axis, col, colSpan, cv);
  369. col += colSpan - 1;
  370. }
  371. }
  372. }
  373. }
  374. }
  375. /**
  376. * check the requirements of a table cell that spans a single column.
  377. */
  378. void checkSingleColumnCell(int axis, int col, View v) {
  379. SizeRequirements req = columnRequirements[col];
  380. req.minimum = Math.max((int) v.getMinimumSpan(axis), req.minimum);
  381. req.preferred = Math.max((int) v.getPreferredSpan(axis), req.preferred);
  382. }
  383. /**
  384. * check the requirements of a table cell that spans multiple
  385. * columns.
  386. */
  387. void checkMultiColumnCell(int axis, int col, int ncols, View v) {
  388. // calculate the totals
  389. long min = 0;
  390. long pref = 0;
  391. long max = 0;
  392. for (int i = 0; i < ncols; i++) {
  393. SizeRequirements req = columnRequirements[col + i];
  394. min += req.minimum;
  395. pref += req.preferred;
  396. max += req.maximum;
  397. }
  398. // check if the minimum size needs adjustment.
  399. int cmin = (int) v.getMinimumSpan(axis);
  400. if (cmin > min) {
  401. /*
  402. * the columns that this cell spans need adjustment to fit
  403. * this table cell.... calculate the adjustments.
  404. */
  405. SizeRequirements[] reqs = new SizeRequirements[ncols];
  406. for (int i = 0; i < ncols; i++) {
  407. reqs[i] = columnRequirements[col + i];
  408. }
  409. int[] spans = new int[ncols];
  410. int[] offsets = new int[ncols];
  411. SizeRequirements.calculateTiledPositions(cmin, null, reqs,
  412. offsets, spans);
  413. // apply the adjustments
  414. for (int i = 0; i < ncols; i++) {
  415. SizeRequirements req = reqs[i];
  416. req.minimum = Math.max(spans[i], req.minimum);
  417. req.preferred = Math.max(req.minimum, req.preferred);
  418. req.maximum = Math.max(req.preferred, req.maximum);
  419. }
  420. }
  421. // check if the preferred size needs adjustment.
  422. int cpref = (int) v.getPreferredSpan(axis);
  423. if (cpref > pref) {
  424. /*
  425. * the columns that this cell spans need adjustment to fit
  426. * this table cell.... calculate the adjustments.
  427. */
  428. SizeRequirements[] reqs = new SizeRequirements[ncols];
  429. for (int i = 0; i < ncols; i++) {
  430. reqs[i] = columnRequirements[col + i];
  431. }
  432. int[] spans = new int[ncols];
  433. int[] offsets = new int[ncols];
  434. SizeRequirements.calculateTiledPositions(cpref, null, reqs,
  435. offsets, spans);
  436. // apply the adjustments
  437. for (int i = 0; i < ncols; i++) {
  438. SizeRequirements req = reqs[i];
  439. req.preferred = Math.max(spans[i], req.preferred);
  440. req.maximum = Math.max(req.preferred, req.maximum);
  441. }
  442. }
  443. }
  444. // --- BoxView methods -----------------------------------------
  445. /**
  446. * Calculate the requirements for the minor axis. This is called by
  447. * the superclass whenever the requirements need to be updated (i.e.
  448. * a preferenceChanged was messaged through this view).
  449. * <p>
  450. * This is implemented to calculate the requirements as the sum of the
  451. * requirements of the columns and then adjust it if the
  452. * CSS width or height attribute is specified and applicable to
  453. * the axis.
  454. */
  455. protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
  456. updateGrid();
  457. // calculate column requirements for each column
  458. calculateColumnRequirements(axis);
  459. // the requirements are the sum of the columns.
  460. if (r == null) {
  461. r = new SizeRequirements();
  462. }
  463. long min = 0;
  464. long pref = 0;
  465. int n = columnRequirements.length;
  466. for (int i = 0; i < n; i++) {
  467. SizeRequirements req = columnRequirements[i];
  468. min += req.minimum;
  469. pref += req.preferred;
  470. }
  471. int adjust = (n - 1) * cellSpacing;
  472. min += adjust;
  473. pref += adjust;
  474. r.minimum = (int) min;
  475. r.preferred = (int) pref;
  476. r.maximum = (int) pref;
  477. AttributeSet attr = getAttributes();
  478. CSS.LengthValue cssWidth = (CSS.LengthValue)attr.getAttribute(
  479. CSS.Attribute.WIDTH);
  480. if (BlockView.spanSetFromAttributes(axis, r, cssWidth, null)) {
  481. if (r.minimum < (int)min) {
  482. // The user has requested a smaller size than is needed to
  483. // show the table, override it.
  484. r.maximum = r.minimum = r.preferred = (int) min;
  485. }
  486. }
  487. totalColumnRequirements.minimum = r.minimum;
  488. totalColumnRequirements.preferred = r.preferred;
  489. totalColumnRequirements.maximum = r.maximum;
  490. // set the alignment
  491. Object o = attr.getAttribute(CSS.Attribute.TEXT_ALIGN);
  492. if (o != null) {
  493. // set horizontal alignment
  494. String ta = o.toString();
  495. if (ta.equals("left")) {
  496. r.alignment = 0;
  497. } else if (ta.equals("center")) {
  498. r.alignment = 0.5f;
  499. } else if (ta.equals("right")) {
  500. r.alignment = 1;
  501. } else {
  502. r.alignment = 0;
  503. }
  504. } else {
  505. r.alignment = 0;
  506. }
  507. return r;
  508. }
  509. /**
  510. * Calculate the requirements for the major axis. This is called by
  511. * the superclass whenever the requirements need to be updated (i.e.
  512. * a preferenceChanged was messaged through this view).
  513. * <p>
  514. * This is implemented to provide the superclass behavior adjusted for
  515. * multi-row table cells.
  516. */
  517. protected SizeRequirements calculateMajorAxisRequirements(int axis, SizeRequirements r) {
  518. updateInsets();
  519. rowIterator.updateAdjustments();
  520. r = CSS.calculateTiledRequirements(rowIterator, r);
  521. r.maximum = r.preferred;
  522. return r;
  523. }
  524. /**
  525. * Perform layout for the minor axis of the box (i.e. the
  526. * axis orthoginal to the axis that it represents). The results
  527. * of the layout should be placed in the given arrays which represent
  528. * the allocations to the children along the minor axis. This
  529. * is called by the superclass whenever the layout needs to be
  530. * updated along the minor axis.
  531. * <p>
  532. * This is implemented to call the
  533. * <a href="#layoutColumns">layoutColumns</a> method, and then
  534. * forward to the superclass to actually carry out the layout
  535. * of the tables rows.
  536. *
  537. * @param targetSpan the total span given to the view, which
  538. * whould be used to layout the children
  539. * @param axis the axis being layed out
  540. * @param offsets the offsets from the origin of the view for
  541. * each of the child views. This is a return value and is
  542. * filled in by the implementation of this method
  543. * @param spans the span of each child view; this is a return
  544. * value and is filled in by the implementation of this method
  545. * @return the offset and span for each child view in the
  546. * offsets and spans parameters
  547. */
  548. protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
  549. // make grid is properly represented
  550. updateGrid();
  551. // all of the row layouts are invalid, so mark them that way
  552. int n = getRowCount();
  553. for (int i = 0; i < n; i++) {
  554. RowView row = getRow(i);
  555. row.layoutChanged(axis);
  556. }
  557. // calculate column spans
  558. layoutColumns(targetSpan, columnOffsets, columnSpans, columnRequirements);
  559. // continue normal layout
  560. super.layoutMinorAxis(targetSpan, axis, offsets, spans);
  561. }
  562. /**
  563. * Perform layout for the major axis of the box (i.e. the
  564. * axis that it represents). The results
  565. * of the layout should be placed in the given arrays which represent
  566. * the allocations to the children along the minor axis. This
  567. * is called by the superclass whenever the layout needs to be
  568. * updated along the minor axis.
  569. * <p>
  570. * This method is where the layout of the table rows within the
  571. * table takes place. This method is implemented to call the use
  572. * the RowIterator and the CSS collapsing tile to layout
  573. * with border spacing and border collapsing capabilities.
  574. *
  575. * @param targetSpan the total span given to the view, which
  576. * whould be used to layout the children
  577. * @param axis the axis being layed out
  578. * @param offsets the offsets from the origin of the view for
  579. * each of the child views; this is a return value and is
  580. * filled in by the implementation of this method
  581. * @param spans the span of each child view; this is a return
  582. * value and is filled in by the implementation of this method
  583. * @return the offset and span for each child view in the
  584. * offsets and spans parameters
  585. */
  586. protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
  587. rowIterator.setLayoutArrays(offsets, spans);
  588. CSS.calculateTiledLayout(rowIterator, targetSpan);
  589. if (captionIndex != -1) {
  590. // place the caption
  591. View caption = getView(captionIndex);
  592. int h = (int) caption.getPreferredSpan(Y_AXIS);
  593. spans[captionIndex] = h;
  594. short boxBottom = (short) painter.getInset(BOTTOM, this);
  595. if (boxBottom != getBottomInset()) {
  596. offsets[captionIndex] = targetSpan + boxBottom;
  597. } else {
  598. offsets[captionIndex] = - getTopInset();
  599. }
  600. }
  601. }
  602. /**
  603. * Fetches the child view that represents the given position in
  604. * the model. This is implemented to walk through the children
  605. * looking for a range that contains the given position. In this
  606. * view the children do not necessarily have a one to one mapping
  607. * with the child elements.
  608. *
  609. * @param pos the search position >= 0
  610. * @param a the allocation to the table on entry, and the
  611. * allocation of the view containing the position on exit
  612. * @return the view representing the given position, or
  613. * null if there isn't one
  614. */
  615. protected View getViewAtPosition(int pos, Rectangle a) {
  616. int n = getViewCount();
  617. for (int i = 0; i < n; i++) {
  618. View v = getView(i);
  619. int p0 = v.getStartOffset();
  620. int p1 = v.getEndOffset();
  621. if ((pos >= p0) && (pos < p1)) {
  622. // it's in this view.
  623. if (a != null) {
  624. childAllocation(i, a);
  625. }
  626. return v;
  627. }
  628. }
  629. if (pos == getEndOffset()) {
  630. View v = getView(n - 1);
  631. if (a != null) {
  632. this.childAllocation(n - 1, a);
  633. }
  634. return v;
  635. }
  636. return null;
  637. }
  638. // --- View methods ---------------------------------------------
  639. /**
  640. * Fetches the attributes to use when rendering. This is
  641. * implemented to multiplex the attributes specified in the
  642. * model with a StyleSheet.
  643. */
  644. public AttributeSet getAttributes() {
  645. if (attr == null) {
  646. StyleSheet sheet = getStyleSheet();
  647. attr = sheet.getViewAttributes(this);
  648. }
  649. return attr;
  650. }
  651. /**
  652. * Renders using the given rendering surface and area on that
  653. * surface. This is implemented to delegate to the css box
  654. * painter to paint the border and background prior to the
  655. * interior. The superclass culls rendering the children
  656. * that don't directly intersect the clip and the row may
  657. * have cells hanging from a row above in it. The table
  658. * does not use the superclass rendering behavior and instead
  659. * paints all of the rows and lets the rows cull those
  660. * cells not intersecting the clip region.
  661. *
  662. * @param g the rendering surface to use
  663. * @param allocation the allocated region to render into
  664. * @see View#paint
  665. */
  666. public void paint(Graphics g, Shape allocation) {
  667. // paint the border
  668. Rectangle a = allocation.getBounds();
  669. setSize(a.width, a.height);
  670. if (captionIndex != -1) {
  671. // adjust the border for the caption
  672. short top = (short) painter.getInset(TOP, this);
  673. short bottom = (short) painter.getInset(BOTTOM, this);
  674. if (top != getTopInset()) {
  675. int h = getTopInset() - top;
  676. a.y += h;
  677. a.height -= h;
  678. } else {
  679. a.height -= getBottomInset() - bottom;
  680. }
  681. }
  682. painter.paint(g, a.x, a.y, a.width, a.height, this);
  683. // paint interior
  684. int n = getViewCount();
  685. for (int i = 0; i < n; i++) {
  686. View v = getView(i);
  687. v.paint(g, getChildAllocation(i, allocation));
  688. }
  689. //super.paint(g, a);
  690. }
  691. /**
  692. * Establishes the parent view for this view. This is
  693. * guaranteed to be called before any other methods if the
  694. * parent view is functioning properly.
  695. * <p>
  696. * This is implemented
  697. * to forward to the superclass as well as call the
  698. * <a href="#setPropertiesFromAttributes">setPropertiesFromAttributes</a>
  699. * method to set the paragraph properties from the css
  700. * attributes. The call is made at this time to ensure
  701. * the ability to resolve upward through the parents
  702. * view attributes.
  703. *
  704. * @param parent the new parent, or null if the view is
  705. * being removed from a parent it was previously added
  706. * to
  707. */
  708. public void setParent(View parent) {
  709. super.setParent(parent);
  710. if (parent != null) {
  711. setPropertiesFromAttributes();
  712. }
  713. }
  714. /**
  715. * Fetches the ViewFactory implementation that is feeding
  716. * the view hierarchy.
  717. * This replaces the ViewFactory with an implementation that
  718. * calls through to the createTableRow and createTableCell
  719. * methods. If the element given to the factory isn't a
  720. * table row or cell, the request is delegated to the factory
  721. * produced by the superclass behavior.
  722. *
  723. * @return the factory, null if none
  724. */
  725. public ViewFactory getViewFactory() {
  726. return this;
  727. }
  728. /**
  729. * Gives notification that something was inserted into
  730. * the document in a location that this view is responsible for.
  731. * This replaces the ViewFactory with an implementation that
  732. * calls through to the createTableRow and createTableCell
  733. * methods. If the element given to the factory isn't a
  734. * table row or cell, the request is delegated to the factory
  735. * passed as an argument.
  736. *
  737. * @param e the change information from the associated document
  738. * @param a the current allocation of the view
  739. * @param f the factory to use to rebuild if the view has children
  740. * @see View#insertUpdate
  741. */
  742. public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) {
  743. super.insertUpdate(e, a, this);
  744. }
  745. /**
  746. * Gives notification that something was removed from the document
  747. * in a location that this view is responsible for.
  748. * This replaces the ViewFactory with an implementation that
  749. * calls through to the createTableRow and createTableCell
  750. * methods. If the element given to the factory isn't a
  751. * table row or cell, the request is delegated to the factory
  752. * passed as an argument.
  753. *
  754. * @param e the change information from the associated document
  755. * @param a the current allocation of the view
  756. * @param f the factory to use to rebuild if the view has children
  757. * @see View#removeUpdate
  758. */
  759. public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) {
  760. super.removeUpdate(e, a, this);
  761. }
  762. /**
  763. * Gives notification from the document that attributes were changed
  764. * in a location that this view is responsible for.
  765. * This replaces the ViewFactory with an implementation that
  766. * calls through to the createTableRow and createTableCell
  767. * methods. If the element given to the factory isn't a
  768. * table row or cell, the request is delegated to the factory
  769. * passed as an argument.
  770. *
  771. * @param e the change information from the associated document
  772. * @param a the current allocation of the view
  773. * @param f the factory to use to rebuild if the view has children
  774. * @see View#changedUpdate
  775. */
  776. public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
  777. super.changedUpdate(e, a, this);
  778. }
  779. protected void forwardUpdate(DocumentEvent.ElementChange ec,
  780. DocumentEvent e, Shape a, ViewFactory f) {
  781. super.forwardUpdate(ec, e, a, f);
  782. // A change in any of the table cells usually effects the whole table,
  783. // so redraw it all!
  784. if (a != null) {
  785. Component c = getContainer();
  786. if (c != null) {
  787. Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a :
  788. a.getBounds();
  789. c.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
  790. }
  791. }
  792. }
  793. /**
  794. * Change the child views. This is implemented to
  795. * provide the superclass behavior and invalidate the
  796. * grid so that rows and columns will be recalculated.
  797. */
  798. public void replace(int offset, int length, View[] views) {
  799. super.replace(offset, length, views);
  800. invalidateGrid();
  801. }
  802. // --- ViewFactory methods ------------------------------------------
  803. /**
  804. * The table itself acts as a factory for the various
  805. * views that actually represent pieces of the table.
  806. * All other factory activity is delegated to the factory
  807. * returned by the parent of the table.
  808. */
  809. public View create(Element elem) {
  810. Object o = elem.getAttributes().getAttribute(StyleConstants.NameAttribute);
  811. if (o instanceof HTML.Tag) {
  812. HTML.Tag kind = (HTML.Tag) o;
  813. if (kind == HTML.Tag.TR) {
  814. return createTableRow(elem);
  815. } else if ((kind == HTML.Tag.TD) || (kind == HTML.Tag.TH)) {
  816. return new CellView(elem);
  817. } else if (kind == HTML.Tag.CAPTION) {
  818. return new javax.swing.text.html.ParagraphView(elem);
  819. }
  820. }
  821. // default is to delegate to the normal factory
  822. View p = getParent();
  823. if (p != null) {
  824. ViewFactory f = p.getViewFactory();
  825. if (f != null) {
  826. return f.create(elem);
  827. }
  828. }
  829. return null;
  830. }
  831. // ---- variables ----------------------------------------------------
  832. private AttributeSet attr;
  833. private StyleSheet.BoxPainter painter;
  834. private int cellSpacing;
  835. /**
  836. * The index of the caption view if there is a caption.
  837. * This has a value of -1 if there is no caption. The
  838. * caption lives in the inset area of the table, and is
  839. * updated with each time the grid is recalculated.
  840. */
  841. private int captionIndex;
  842. /**
  843. * Do any of the table cells contain a relative size
  844. * specification? This is updated with each call to
  845. * updateGrid(). If this is true, the ColumnIterator
  846. * will do extra work to calculate relative cell
  847. * specifications.
  848. */
  849. private boolean relativeCells;
  850. /**
  851. * Do any of the table cells span multiple rows? If
  852. * true, the RowRequirementIterator will do additional
  853. * work to adjust the requirements of rows spanned by
  854. * a single table cell. This is updated with each call to
  855. * updateGrid().
  856. */
  857. private boolean multiRowCells;
  858. int[] columnSpans;
  859. int[] columnOffsets;
  860. /**
  861. * SizeRequirements for all the columns.
  862. */
  863. SizeRequirements totalColumnRequirements;
  864. SizeRequirements[] columnRequirements;
  865. RowIterator rowIterator = new RowIterator();
  866. ColumnIterator colIterator = new ColumnIterator();
  867. Vector rows;
  868. boolean gridValid;
  869. static final private BitSet EMPTY = new BitSet();
  870. class ColumnIterator implements CSS.LayoutIterator {
  871. /**
  872. * Disable percentage adjustments which should only apply
  873. * when calculating layout, not requirements.
  874. */
  875. void disablePercentages() {
  876. percentages = null;
  877. }
  878. /**
  879. * Update percentage adjustments if they are needed.
  880. */
  881. private void updatePercentagesAndAdjustmentWeights(int span) {
  882. adjustmentWeights = new int[columnRequirements.length];
  883. for (int i = 0; i < columnRequirements.length; i++) {
  884. adjustmentWeights[i] = 0;
  885. }
  886. if (relativeCells) {
  887. percentages = new int[columnRequirements.length];
  888. } else {
  889. percentages = null;
  890. }
  891. int nrows = getRowCount();
  892. for (int rowIndex = 0; rowIndex < nrows; rowIndex++) {
  893. RowView row = getRow(rowIndex);
  894. int col = 0;
  895. int ncells = row.getViewCount();
  896. for (int cell = 0; cell < ncells; cell++, col++) {
  897. View cv = row.getView(cell);
  898. for (; row.isFilled(col); col++); // advance to a free column
  899. int rowSpan = getRowsOccupied(cv);
  900. int colSpan = getColumnsOccupied(cv);
  901. AttributeSet a = cv.getAttributes();
  902. CSS.LengthValue lv = (CSS.LengthValue)
  903. a.getAttribute(CSS.Attribute.WIDTH);
  904. if ( lv != null ) {
  905. int len = (int) (lv.getValue(span) / colSpan + 0.5f);
  906. for (int i = 0; i < colSpan; i++) {
  907. if (lv.isPercentage()) {
  908. // add a percentage requirement
  909. percentages[col+i] = Math.max(percentages[col+i], len);
  910. }
  911. adjustmentWeights[col+i] = WorstAdjustmentWeight;
  912. }
  913. }
  914. col += colSpan - 1;
  915. }
  916. }
  917. }
  918. /**
  919. * Set the layout arrays to use for holding layout results
  920. */
  921. public void setLayoutArrays(int offsets[], int spans[], int targetSpan) {
  922. this.offsets = offsets;
  923. this.spans = spans;
  924. updatePercentagesAndAdjustmentWeights(targetSpan);
  925. }
  926. // --- RequirementIterator methods -------------------
  927. public int getCount() {
  928. return columnRequirements.length;
  929. }
  930. public void setIndex(int i) {
  931. col = i;
  932. }
  933. public void setOffset(int offs) {
  934. offsets[col] = offs;
  935. }
  936. public int getOffset() {
  937. return offsets[col];
  938. }
  939. public void setSpan(int span) {
  940. spans[col] = span;
  941. }
  942. public int getSpan() {
  943. return spans[col];
  944. }
  945. public float getMinimumSpan(float parentSpan) {
  946. if ((percentages != null) && (percentages[col] != 0)) {
  947. return Math.max(percentages[col], columnRequirements[col].minimum);
  948. }
  949. return columnRequirements[col].minimum;
  950. }
  951. public float getPreferredSpan(float parentSpan) {
  952. if ((percentages != null) && (percentages[col] != 0)) {
  953. return Math.max(percentages[col], columnRequirements[col].preferred);
  954. }
  955. return columnRequirements[col].preferred;
  956. }
  957. public float getMaximumSpan(float parentSpan) {
  958. if ((percentages != null) && (percentages[col] != 0)) {
  959. return Math.max(percentages[col], columnRequirements[col].preferred);
  960. }
  961. return columnRequirements[col].maximum;
  962. }
  963. public float getLeadingCollapseSpan() {
  964. return cellSpacing;
  965. }
  966. public float getTrailingCollapseSpan() {
  967. return cellSpacing;
  968. }
  969. public int getAdjustmentWeight() {
  970. return adjustmentWeights[col];
  971. }
  972. /**
  973. * Current column index
  974. */
  975. private int col;
  976. /**
  977. * percentage values (may be null since there
  978. * might not be any).
  979. */
  980. private int[] percentages;
  981. private int[] adjustmentWeights;
  982. private int[] offsets;
  983. private int[] spans;
  984. }
  985. class RowIterator implements CSS.LayoutIterator {
  986. RowIterator() {
  987. }
  988. void updateAdjustments() {
  989. int axis = Y_AXIS;
  990. if (multiRowCells) {
  991. // adjust requirements of multi-row cells
  992. int n = getRowCount();
  993. adjustments = new int[n];
  994. for (int i = 0; i < n; i++) {
  995. RowView rv = getRow(i);
  996. if (rv.multiRowCells == true) {
  997. int ncells = rv.getViewCount();
  998. for (int j = 0; j < ncells; j++) {
  999. View v = rv.getView(j);
  1000. int nrows = getRowsOccupied(v);
  1001. if (nrows > 1) {
  1002. int spanNeeded = (int) v.getPreferredSpan(axis);
  1003. adjustMultiRowSpan(spanNeeded, nrows, i);
  1004. }
  1005. }
  1006. }
  1007. }
  1008. } else {
  1009. adjustments = null;
  1010. }
  1011. }
  1012. /**
  1013. * Fixup preferences to accomodate a multi-row table cell
  1014. * if not already covered by existing preferences. This is
  1015. * a no-op if not all of the rows needed (to do this check/fixup)
  1016. * have arrived yet.
  1017. */
  1018. void adjustMultiRowSpan(int spanNeeded, int nrows, int rowIndex) {
  1019. if ((rowIndex + nrows) > getCount()) {
  1020. // rows are missing (could be a bad rowspan specification)
  1021. // or not all the rows have arrived. Do the best we can with
  1022. // the current set of rows.
  1023. nrows = getCount() - rowIndex;
  1024. if (nrows < 1) {
  1025. return;
  1026. }
  1027. }
  1028. int span = 0;
  1029. for (int i = 0; i < nrows; i++) {
  1030. RowView rv = getRow(rowIndex + i);
  1031. span += rv.getPreferredSpan(Y_AXIS);
  1032. }
  1033. if (spanNeeded > span) {
  1034. int adjust = (spanNeeded - span);
  1035. int rowAdjust = adjust / nrows;
  1036. int firstAdjust = rowAdjust + (adjust - (rowAdjust * nrows));
  1037. RowView rv = getRow(rowIndex);
  1038. adjustments[rowIndex] = Math.max(adjustments[rowIndex],
  1039. firstAdjust);
  1040. for (int i = 1; i < nrows; i++) {
  1041. adjustments[rowIndex + i] = Math.max(
  1042. adjustments[rowIndex + i], rowAdjust);
  1043. }
  1044. }
  1045. }
  1046. void setLayoutArrays(int[] offsets, int[] spans) {
  1047. this.offsets = offsets;
  1048. this.spans = spans;
  1049. }
  1050. // --- RequirementIterator methods -------------------
  1051. public void setOffset(int offs) {
  1052. RowView rv = getRow(row);
  1053. if (rv != null) {
  1054. offsets[rv.viewIndex] = offs;
  1055. }
  1056. }
  1057. public int getOffset() {
  1058. RowView rv = getRow(row);
  1059. if (rv != null) {
  1060. return offsets[rv.viewIndex];
  1061. }
  1062. return 0;
  1063. }
  1064. public void setSpan(int span) {
  1065. RowView rv = getRow(row);
  1066. if (rv != null) {
  1067. spans[rv.viewIndex] = span;
  1068. }
  1069. }
  1070. public int getSpan() {
  1071. RowView rv = getRow(row);
  1072. if (rv != null) {
  1073. return spans[rv.viewIndex];
  1074. }
  1075. return 0;
  1076. }
  1077. public int getCount() {
  1078. return rows.size();
  1079. }
  1080. public void setIndex(int i) {
  1081. row = i;
  1082. }
  1083. public float getMinimumSpan(float parentSpan) {
  1084. return getPreferredSpan(parentSpan);
  1085. }
  1086. public float getPreferredSpan(float parentSpan) {
  1087. RowView rv = getRow(row);
  1088. if (rv != null) {
  1089. int adjust = (adjustments != null) ? adjustments[row] : 0;
  1090. adjust += 2 * cellSpacing;
  1091. return rv.getPreferredSpan(TableView.this.getAxis()) + adjust;
  1092. }
  1093. return 0;
  1094. }
  1095. public float getMaximumSpan(float parentSpan) {
  1096. return getPreferredSpan(parentSpan);
  1097. }
  1098. public float getLeadingCollapseSpan() {
  1099. return cellSpacing;
  1100. }
  1101. public float getTrailingCollapseSpan() {
  1102. return cellSpacing;
  1103. }
  1104. public int getAdjustmentWeight() {
  1105. return 0;
  1106. }
  1107. /**
  1108. * Current row index
  1109. */
  1110. private int row;
  1111. /**
  1112. * Adjustments to the row requirements to handle multi-row
  1113. * table cells.
  1114. */
  1115. private int[] adjustments;
  1116. private int[] offsets;
  1117. private int[] spans;
  1118. }
  1119. /**
  1120. * View of a row in a row-centric table.
  1121. */
  1122. public class RowView extends BoxView {
  1123. /**
  1124. * Constructs a TableView for the given element.
  1125. *
  1126. * @param elem the element that this view is responsible for
  1127. */
  1128. public RowView(Element elem) {
  1129. super(elem, View.X_AXIS);
  1130. fillColumns = new BitSet();
  1131. RowView.this.setPropertiesFromAttributes();
  1132. }
  1133. void clearFilledColumns() {
  1134. fillColumns.and(EMPTY);
  1135. }
  1136. void fillColumn(int col) {
  1137. fillColumns.set(col);
  1138. }
  1139. boolean isFilled(int col) {
  1140. return fillColumns.get(col);
  1141. }
  1142. /**
  1143. * The number of columns present in this row.
  1144. */
  1145. int getColumnCount() {
  1146. int nfill = 0;
  1147. int n = fillColumns.size();
  1148. for (int i = 0; i < n; i++) {
  1149. if (fillColumns.get(i)) {
  1150. nfill ++;
  1151. }
  1152. }
  1153. return getViewCount() + nfill;
  1154. }
  1155. /**
  1156. * Fetches the attributes to use when rendering. This is
  1157. * implemented to multiplex the attributes specified in the
  1158. * model with a StyleSheet.
  1159. */
  1160. public AttributeSet getAttributes() {
  1161. return attr;
  1162. }
  1163. View findViewAtPoint(int x, int y, Rectangle alloc) {
  1164. int n = getViewCount();
  1165. for (int i = 0; i < n; i++) {
  1166. if (getChildAllocation(i, alloc).contains(x, y)) {
  1167. childAllocation(i, alloc);
  1168. return getView(i);
  1169. }
  1170. }
  1171. return null;
  1172. }
  1173. protected StyleSheet getStyleSheet() {
  1174. HTMLDocument doc = (HTMLDocument) getDocument();
  1175. return doc.getStyleSheet();
  1176. }
  1177. /**
  1178. * This is called by a child to indicate its
  1179. * preferred span has changed. This is implemented to
  1180. * execute the superclass behavior and well as try to
  1181. * determine if a row with a multi-row cell hangs across
  1182. * this row. If a multi-row cell covers this row it also
  1183. * needs to propagate a preferenceChanged so that it will
  1184. * recalculate the multi-row cell.
  1185. *
  1186. * @param child the child view
  1187. * @param width true if the width preference should change
  1188. * @param height true if the height preference should change
  1189. */
  1190. public void preferenceChanged(View child, boolean width, boolean height) {
  1191. super.preferenceChanged(child, width, height);
  1192. if (TableView.this.multiRowCells && height) {
  1193. for (int i = rowIndex - 1; i >= 0; i--) {
  1194. RowView rv = TableView.this.getRow(i);
  1195. if (rv.multiRowCells) {
  1196. rv.preferenceChanged(null, false, true);
  1197. break;
  1198. }
  1199. }
  1200. }
  1201. }
  1202. // The major axis requirements for a row are dictated by the column
  1203. // requirements. These methods use the value calculated by
  1204. // TableView.
  1205. protected SizeRequirements calculateMajorAxisRequirements(int axis, SizeRequirements r) {
  1206. SizeRequirements req = new SizeRequirements();
  1207. req.minimum = totalColumnRequirements.minimum;
  1208. req.maximum = totalColumnRequirements.maximum;
  1209. req.preferred = totalColumnRequirements.preferred;
  1210. req.alignment = 0f;
  1211. return req;
  1212. }
  1213. public float getMinimumSpan(int axis) {
  1214. float value;
  1215. if (axis == View.X_AXIS) {
  1216. value = totalColumnRequirements.minimum + getLeftInset() +
  1217. getRightInset();
  1218. }
  1219. else {
  1220. value = super.getMinimumSpan(axis);
  1221. }
  1222. return value;
  1223. }
  1224. public float getMaximumSpan(int axis) {
  1225. float value;
  1226. if (axis == View.X_AXIS) {
  1227. // We're flexible.
  1228. value = (float)Integer.MAX_VALUE;
  1229. }
  1230. else {
  1231. value = super.getMaximumSpan(axis);
  1232. }
  1233. return value;
  1234. }
  1235. public float getPreferredSpan(int axis) {
  1236. float value;
  1237. if (axis == View.X_AXIS) {
  1238. value = totalColumnRequirements.preferred + getLeftInset() +
  1239. getRightInset();
  1240. }
  1241. else {
  1242. value = super.getPreferredSpan(axis);
  1243. }
  1244. return value;
  1245. }
  1246. public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
  1247. super.changedUpdate(e, a, f);
  1248. int pos = e.getOffset();
  1249. if (pos <= getStartOffset() && (pos + e.getLength()) >=
  1250. getEndOffset()) {
  1251. RowView.this.setPropertiesFromAttributes();
  1252. }
  1253. }
  1254. /**
  1255. * Renders using the given rendering surface and area on that
  1256. * surface. This is implemented to delegate to the css box
  1257. * painter to paint the border and background prior to the
  1258. * interior.
  1259. *
  1260. * @param g the rendering surface to use
  1261. * @param allocation the allocated region to render into
  1262. * @see View#paint
  1263. */
  1264. public void paint(Graphics g, Shape allocation) {
  1265. Rectangle a = (Rectangle) allocation;
  1266. painter.paint(g, a.x, a.y, a.width, a.height, this);
  1267. super.paint(g, a);
  1268. }
  1269. /**
  1270. * Change the child views. This is implemented to
  1271. * provide the superclass behavior and invalidate the
  1272. * grid so that rows and columns will be recalculated.
  1273. */
  1274. public void replace(int offset, int length, View[] views) {
  1275. super.replace(offset, length, views);
  1276. invalidateGrid();
  1277. }
  1278. /**
  1279. * Calculate the height requirements of the table row. The
  1280. * requirements of multi-row cells are not considered for this
  1281. * calculation. The table itself will check and adjust the row
  1282. * requirements for all the rows that have multi-row cells spanning
  1283. * them. This method updates the multi-row flag that indicates that
  1284. * this row and rows below need additional consideration.
  1285. */
  1286. protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
  1287. // return super.calculateMinorAxisRequirements(axis, r);
  1288. long min = 0;
  1289. long pref = 0;
  1290. long max = 0;
  1291. multiRowCells = false;
  1292. int n = getViewCount();
  1293. for (int i = 0; i < n; i++) {
  1294. View v = getView(i);
  1295. if (getRowsOccupied(v) > 1) {
  1296. multiRowCells = true;
  1297. max = Math.max((int) v.getMaximumSpan(axis), max);
  1298. } else {
  1299. min = Math.max((int) v.getMinimumSpan(axis), min);
  1300. pref = Math.max((int) v.getPreferredSpan(axis), pref);
  1301. max = Math.max((int) v.getMaximumSpan(axis), max);
  1302. }
  1303. }
  1304. if (r == null) {
  1305. r = new SizeRequirements();
  1306. r.alignment = 0.5f;
  1307. }
  1308. r.preferred = (int) pref;
  1309. r.minimum = (int) min;
  1310. r.maximum = (int) max;
  1311. return r;
  1312. }
  1313. /**
  1314. * Perform layout for the major axis of the box (i.e. the
  1315. * axis that it represents). The results of the layout should
  1316. * be placed in the given arrays which represent the allocations
  1317. * to the children along the major axis.
  1318. * <p>
  1319. * This is re-implemented to give each child the span of the column
  1320. * width for the table, and to give cells that span multiple columns
  1321. * the multi-column span.
  1322. *
  1323. * @param targetSpan the total span given to the view, which
  1324. * whould be used to layout the children
  1325. * @param axis the axis being layed out
  1326. * @param offsets the offsets from the origin of the view for
  1327. * each of the child views; this is a return value and is
  1328. * filled in by the implementation of this method
  1329. * @param spans the span of each child view; this is a return
  1330. * value and is filled in by the implementation of this method
  1331. * @return the offset and span for each child view in the
  1332. * offsets and spans parameters
  1333. */
  1334. protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
  1335. int col = 0;
  1336. int ncells = getViewCount();
  1337. for (int cell = 0; cell < ncells; cell++, col++) {
  1338. View cv = getView(cell);
  1339. for (; isFilled(col); col++); // advance to a free column
  1340. int colSpan = getColumnsOccupied(cv);
  1341. spans[cell] = columnSpans[col];
  1342. offsets[cell] = columnOffsets[col];
  1343. if (colSpan > 1) {
  1344. int n = columnSpans.length;
  1345. for (int j = 1; j < colSpan; j++) {
  1346. // Because the table may be only partially formed, some
  1347. // of the columns may not yet exist. Therefore we check
  1348. // the bounds.
  1349. if ((col+j) < n) {
  1350. spans[cell] += columnSpans[col+j];
  1351. }
  1352. }
  1353. col += colSpan - 1;
  1354. }
  1355. }
  1356. }
  1357. /**
  1358. * Perform layout for the minor axis of the box (i.e. the
  1359. * axis orthoginal to the axis that it represents). The results
  1360. * of the layout should be placed in the given arrays which represent
  1361. * the allocations to the children along the minor axis. This
  1362. * is called by the superclass whenever the layout needs to be
  1363. * updated along the minor axis.
  1364. * <p>
  1365. * This is implemented to delegate to the superclass, then adjust
  1366. * the span for any cell that spans multiple rows.
  1367. *
  1368. * @param targetSpan the total span given to the view, which
  1369. * whould be used to layout the children
  1370. * @param axis the axis being layed out
  1371. * @param offsets the offsets from the origin of the view for
  1372. * each of the child views; this is a return value and is
  1373. * filled in by the implementation of this method
  1374. * @param spans the span of each child view; this is a return
  1375. * value and is filled in by the implementation of this method
  1376. * @return the offset and span for each child view in the
  1377. * offsets and spans parameters
  1378. */
  1379. protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
  1380. super.layoutMinorAxis(targetSpan, axis, offsets, spans);
  1381. int col = 0;
  1382. int ncells = getViewCount();
  1383. for (int cell = 0; cell < ncells; cell++, col++) {
  1384. View cv = getView(cell);
  1385. for (; isFilled(col); col++); // advance to a free column
  1386. int colSpan = getColumnsOccupied(cv);
  1387. int rowSpan = getRowsOccupied(cv);
  1388. if (rowSpan > 1) {
  1389. int row0 = rowIndex;
  1390. int row1 = Math.min(rowIndex + rowSpan - 1, getRowCount()-1);
  1391. spans[cell] = getMultiRowSpan(row0, row1);
  1392. }
  1393. if (colSpan > 1) {
  1394. col += colSpan - 1;
  1395. }
  1396. }
  1397. }
  1398. /**
  1399. * Determines the resizability of the view along the
  1400. * given axis. A value of 0 or less is not resizable.
  1401. *
  1402. * @param axis may be either View.X_AXIS or View.Y_AXIS
  1403. * @return the resize weight
  1404. * @exception IllegalArgumentException for an invalid axis
  1405. */
  1406. public int getResizeWeight(int axis) {
  1407. return 1;
  1408. }
  1409. /**
  1410. * Fetches the child view that represents the given position in
  1411. * the model. This is implemented to walk through the children
  1412. * looking for a range that contains the given position. In this
  1413. * view the children do not necessarily have a one to one mapping
  1414. * with the child elements.
  1415. *
  1416. * @param pos the search position >= 0
  1417. * @param a the allocation to the table on entry, and the
  1418. * allocation of the view containing the position on exit
  1419. * @return the view representing the given position, or
  1420. * null if there isn't one
  1421. */
  1422. protected View getViewAtPosition(int pos, Rectangle a) {
  1423. int n = getViewCount();
  1424. for (int i = 0; i < n; i++) {
  1425. View v = getView(i);
  1426. int p0 = v.getStartOffset();
  1427. int p1 = v.getEndOffset();
  1428. if ((pos >= p0) && (pos < p1)) {
  1429. // it's in this view.
  1430. if (a != null) {
  1431. childAllocation(i, a);
  1432. }
  1433. return v;
  1434. }
  1435. }
  1436. if (pos == getEndOffset()) {
  1437. View v = getView(n - 1);
  1438. if (a != null) {
  1439. this.childAllocation(n - 1, a);
  1440. }
  1441. return v;
  1442. }
  1443. return null;
  1444. }
  1445. /**
  1446. * Update any cached values that come from attributes.
  1447. */
  1448. void setPropertiesFromAttributes() {
  1449. StyleSheet sheet = getStyleSheet();
  1450. attr = sheet.getViewAttributes(this);
  1451. painter = sheet.getBoxPainter(attr);
  1452. }
  1453. private StyleSheet.BoxPainter painter;
  1454. private AttributeSet attr;
  1455. /** columns filled by multi-column or multi-row cells */
  1456. BitSet fillColumns;
  1457. /**
  1458. * The row index within the overall grid
  1459. */
  1460. int rowIndex;
  1461. /**
  1462. * The view index (for row index to view index conversion).
  1463. * This is set by the updateGrid method.
  1464. */
  1465. int viewIndex;
  1466. /**
  1467. * Does this table row have cells that span multiple rows?
  1468. */
  1469. boolean multiRowCells;
  1470. }
  1471. /**
  1472. * Default view of an html table cell. This needs to be moved
  1473. * somewhere else.
  1474. */
  1475. class CellView extends BlockView {
  1476. /**
  1477. * Constructs a TableCell for the given element.
  1478. *
  1479. * @param elem the element that this view is responsible for
  1480. */
  1481. public CellView(Element elem) {
  1482. super(elem, Y_AXIS);
  1483. }
  1484. /**
  1485. * Perform layout for the major axis of the box (i.e. the
  1486. * axis that it represents). The results of the layout should
  1487. * be placed in the given arrays which represent the allocations
  1488. * to the children along the major axis. This is called by the
  1489. * superclass to recalculate the positions of the child views
  1490. * when the layout might have changed.
  1491. * <p>
  1492. * This is implemented to delegate to the superclass to
  1493. * tile the children. If the target span is greater than
  1494. * was needed, the offsets are adjusted to align the children
  1495. * (i.e. position according to the html valign attribute).
  1496. *
  1497. * @param targetSpan the total span given to the view, which
  1498. * whould be used to layout the children
  1499. * @param axis the axis being layed out
  1500. * @param offsets the offsets from the origin of the view for
  1501. * each of the child views; this is a return value and is
  1502. * filled in by the implementation of this method
  1503. * @param spans the span of each child view; this is a return
  1504. * value and is filled in by the implementation of this method
  1505. * @return the offset and span for each child view in the
  1506. * offsets and spans parameters
  1507. */
  1508. protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
  1509. super.layoutMajorAxis(targetSpan, axis, offsets, spans);
  1510. // calculate usage
  1511. int used = 0;
  1512. int n = spans.length;
  1513. for (int i = 0; i < n; i++) {
  1514. used += spans[i];
  1515. }
  1516. // calculate adjustments
  1517. int adjust = 0;
  1518. if (used < targetSpan) {
  1519. // PENDING(prinz) change to use the css alignment.
  1520. String valign = (String) getElement().getAttributes().getAttribute(
  1521. HTML.Attribute.VALIGN);
  1522. if (valign == null) {
  1523. AttributeSet rowAttr = getElement().getParentElement().getAttributes();
  1524. valign = (String) rowAttr.getAttribute(HTML.Attribute.VALIGN);
  1525. }
  1526. if ((valign == null) || valign.equals("middle")) {
  1527. adjust = (targetSpan - used) / 2;
  1528. } else if (valign.equals("bottom")) {
  1529. adjust = targetSpan - used;
  1530. }
  1531. }
  1532. // make adjustments.
  1533. if (adjust != 0) {
  1534. for (int i = 0; i < n; i++) {
  1535. offsets[i] += adjust;
  1536. }
  1537. }
  1538. }
  1539. /**
  1540. * Calculate the requirements needed along the major axis.
  1541. * This is called by the superclass whenever the requirements
  1542. * need to be updated (i.e. a preferenceChanged was messaged
  1543. * through this view).
  1544. * <p>
  1545. * This is implemented to delegate to the superclass, but
  1546. * indicate the maximum size is very large (i.e. the cell
  1547. * is willing to expend to occupy the full height of the row).
  1548. *
  1549. * @param axis the axis being layed out.
  1550. * @param r the requirements to fill in. If null, a new one
  1551. * should be allocated.
  1552. */
  1553. protected SizeRequirements calculateMajorAxisRequirements(int axis,
  1554. SizeRequirements r) {
  1555. SizeRequirements req = super.calculateMajorAxisRequirements(axis, r);
  1556. req.maximum = Integer.MAX_VALUE;
  1557. return req;
  1558. }
  1559. }
  1560. }