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