1 /* TableView.java -- A table view for HTML tables 2 Copyright (C) 2006 Free Software Foundation, Inc. 3 4 This file is part of GNU Classpath. 5 6 GNU Classpath is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2, or (at your option) 9 any later version. 10 11 GNU Classpath is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with GNU Classpath; see the file COPYING. If not, write to the 18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19 02110-1301 USA. 20 21 Linking this library statically or dynamically with other modules is 22 making a combined work based on this library. Thus, the terms and 23 conditions of the GNU General Public License cover the whole 24 combination. 25 26 As a special exception, the copyright holders of this library give you 27 permission to link this library with independent modules to produce an 28 executable, regardless of the license terms of these independent 29 modules, and to copy and distribute the resulting executable under 30 terms of your choice, provided that you also meet, for each linked 31 independent module, the terms and conditions of the license of that 32 module. An independent module is a module which is not derived from 33 or based on this library. If you modify this library, you may extend 34 this exception to your version of the library, but you are not 35 obligated to do so. If you do not wish to do so, delete this 36 exception statement from your version. */ 37 38 39 package javax.swing.text.html; 40 41 import java.awt.Graphics; 42 import java.awt.Rectangle; 43 import java.awt.Shape; 44 45 import gnu.javax.swing.text.html.css.Length; 46 47 import javax.swing.SizeRequirements; 48 import javax.swing.event.DocumentEvent; 49 import javax.swing.text.AttributeSet; 50 import javax.swing.text.Element; 51 import javax.swing.text.StyleConstants; 52 import javax.swing.text.View; 53 import javax.swing.text.ViewFactory; 54 55 /** 56 * A view implementation that renders HTML tables. 57 * 58 * This is basically a vertical BoxView that contains the rows of the table 59 * and the rows are horizontal BoxViews that contain the actual columns. 60 */ 61 class TableView 62 extends BlockView 63 implements ViewFactory 64 { 65 66 /** 67 * Represents a single table row. 68 */ 69 class RowView 70 extends BlockView 71 { 72 /** 73 * Has true at column positions where an above row's cell overlaps into 74 * this row. 75 */ 76 boolean[] overlap; 77 78 /** 79 * Stores the row index of this row. 80 */ 81 int rowIndex; 82 83 /** 84 * Creates a new RowView. 85 * 86 * @param el the element for the row view 87 */ RowView(Element el)88 RowView(Element el) 89 { 90 super(el, X_AXIS); 91 } 92 replace(int offset, int len, View[] views)93 public void replace(int offset, int len, View[] views) 94 { 95 gridValid = false; 96 super.replace(offset, len, views); 97 } 98 99 /** 100 * Overridden to make rows not resizable along the Y axis. 101 */ getMaximumSpan(int axis)102 public float getMaximumSpan(int axis) 103 { 104 float span; 105 if (axis == Y_AXIS) 106 span = super.getPreferredSpan(axis); 107 else 108 span = Integer.MAX_VALUE; 109 return span; 110 } 111 getMinimumSpan(int axis)112 public float getMinimumSpan(int axis) 113 { 114 float span; 115 if (axis == X_AXIS) 116 span = totalColumnRequirements.minimum; 117 else 118 span = super.getMinimumSpan(axis); 119 return span; 120 } 121 getPreferredSpan(int axis)122 public float getPreferredSpan(int axis) 123 { 124 float span; 125 if (axis == X_AXIS) 126 span = totalColumnRequirements.preferred; 127 else 128 span = super.getPreferredSpan(axis); 129 return span; 130 } 131 132 /** 133 * Calculates the overall size requirements for the row along the 134 * major axis. This will be the sum of the column requirements. 135 */ calculateMajorAxisRequirements(int axis, SizeRequirements r)136 protected SizeRequirements calculateMajorAxisRequirements(int axis, 137 SizeRequirements r) 138 { 139 if (r == null) 140 r = new SizeRequirements(); 141 int adjust = (columnRequirements.length + 1) * cellSpacing; 142 r.minimum = totalColumnRequirements.minimum + adjust; 143 r.preferred = totalColumnRequirements.preferred + adjust; 144 r.maximum = totalColumnRequirements.maximum + adjust; 145 r.alignment = 0.0F; 146 return r; 147 } 148 149 /** 150 * Lays out the columns in this row. 151 */ layoutMinorAxis(int targetSpan, int axis, int[] offsets, int spans[])152 protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, 153 int spans[]) 154 { 155 super.layoutMinorAxis(targetSpan, axis, offsets, spans); 156 157 // Adjust columns that have rowSpan > 1. 158 int numCols = getViewCount(); 159 for (int i = 0; i < numCols; i++) 160 { 161 View v = getView(i); 162 if (v instanceof CellView) 163 { 164 CellView cell = (CellView) v; 165 if (cell.rowSpan > 1) 166 { 167 for (int r = 1; r < cell.rowSpan; r++) 168 { 169 spans[i] += TableView.this.getSpan(axis, rowIndex + r); 170 spans[i] += cellSpacing; 171 } 172 } 173 } 174 } 175 } 176 177 /** 178 * Lays out the columns in this row. 179 */ layoutMajorAxis(int targetSpan, int axis, int[] offsets, int spans[])180 protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, 181 int spans[]) 182 { 183 updateGrid(); 184 int realColumn = 0; 185 int colCount = getViewCount(); 186 for (int i = 0; i < numColumns;) 187 { 188 if (! overlap[i] && realColumn < colCount) 189 { 190 View v = getView(realColumn); 191 if (v instanceof CellView) 192 { 193 CellView cv = (CellView) v; 194 offsets[realColumn] = columnOffsets[i]; 195 spans[realColumn] = 0; 196 for (int j = 0; j < cv.colSpan; j++, i++) 197 { 198 spans[realColumn] += columnSpans[i]; 199 if (j < cv.colSpan - 1) 200 spans[realColumn] += cellSpacing; 201 } 202 } 203 realColumn++; 204 } 205 else 206 { 207 i++; 208 } 209 } 210 } 211 } 212 213 /** 214 * A view that renders HTML table cells (TD and TH tags). 215 */ 216 class CellView 217 extends BlockView 218 { 219 220 /** 221 * The number of columns that this view spans. 222 */ 223 int colSpan; 224 225 /** 226 * The number of rows that this cell spans. 227 */ 228 int rowSpan; 229 230 /** 231 * Creates a new CellView for the specified element. 232 * 233 * @param el the element for which to create the colspan 234 */ CellView(Element el)235 CellView(Element el) 236 { 237 super(el, Y_AXIS); 238 } 239 calculateMajorAxisRequirements(int axis, SizeRequirements r)240 protected SizeRequirements calculateMajorAxisRequirements(int axis, 241 SizeRequirements r) 242 { 243 r = super.calculateMajorAxisRequirements(axis, r); 244 r.maximum = Integer.MAX_VALUE; 245 return r; 246 } 247 248 /** 249 * Overridden to fetch the columnSpan attibute. 250 */ setPropertiesFromAttributes()251 protected void setPropertiesFromAttributes() 252 { 253 super.setPropertiesFromAttributes(); 254 colSpan = 1; 255 AttributeSet atts = getAttributes(); 256 Object o = atts.getAttribute(HTML.Attribute.COLSPAN); 257 if (o != null) 258 { 259 try 260 { 261 colSpan = Integer.parseInt(o.toString()); 262 } 263 catch (NumberFormatException ex) 264 { 265 // Couldn't parse the colspan, assume 1. 266 colSpan = 1; 267 } 268 } 269 rowSpan = 1; 270 o = atts.getAttribute(HTML.Attribute.ROWSPAN); 271 if (o != null) 272 { 273 try 274 { 275 rowSpan = Integer.parseInt(o.toString()); 276 } 277 catch (NumberFormatException ex) 278 { 279 // Couldn't parse the colspan, assume 1. 280 rowSpan = 1; 281 } 282 } 283 } 284 } 285 286 287 /** 288 * The attributes of this view. 289 */ 290 private AttributeSet attributes; 291 292 /** 293 * The column requirements. 294 * 295 * Package private to avoid accessor methods. 296 */ 297 SizeRequirements[] columnRequirements; 298 299 /** 300 * The overall requirements across all columns. 301 * 302 * Package private to avoid accessor methods. 303 */ 304 SizeRequirements totalColumnRequirements; 305 306 /** 307 * The column layout, offsets. 308 * 309 * Package private to avoid accessor methods. 310 */ 311 int[] columnOffsets; 312 313 /** 314 * The column layout, spans. 315 * 316 * Package private to avoid accessor methods. 317 */ 318 int[] columnSpans; 319 320 /** 321 * The widths of the columns that have been explicitly specified. 322 */ 323 Length[] columnWidths; 324 325 /** 326 * The total number of columns. 327 */ 328 int numColumns; 329 330 /** 331 * The table width. 332 */ 333 private Length width; 334 335 /** 336 * Indicates if the grid setup is ok. 337 */ 338 boolean gridValid = false; 339 340 /** 341 * Additional space that is added _between_ table cells. 342 * 343 * This is package private to avoid accessor methods. 344 */ 345 int cellSpacing; 346 347 /** 348 * A cached Rectangle object for reuse in paint(). 349 */ 350 private Rectangle tmpRect; 351 352 /** 353 * Creates a new HTML table view for the specified element. 354 * 355 * @param el the element for the table view 356 */ TableView(Element el)357 public TableView(Element el) 358 { 359 super(el, Y_AXIS); 360 totalColumnRequirements = new SizeRequirements(); 361 tmpRect = new Rectangle(); 362 } 363 364 /** 365 * Implementation of the ViewFactory interface for creating the 366 * child views correctly. 367 */ create(Element elem)368 public View create(Element elem) 369 { 370 View view = null; 371 AttributeSet atts = elem.getAttributes(); 372 Object name = atts.getAttribute(StyleConstants.NameAttribute); 373 AttributeSet pAtts = elem.getParentElement().getAttributes(); 374 Object pName = pAtts.getAttribute(StyleConstants.NameAttribute); 375 376 if (name == HTML.Tag.TR && pName == HTML.Tag.TABLE) 377 view = new RowView(elem); 378 else if ((name == HTML.Tag.TD || name == HTML.Tag.TH) 379 && pName == HTML.Tag.TR) 380 view = new CellView(elem); 381 else if (name == HTML.Tag.CAPTION) 382 view = new ParagraphView(elem); 383 else 384 { 385 // If we haven't mapped the element, then fall back to the standard 386 // view factory. 387 View parent = getParent(); 388 if (parent != null) 389 { 390 ViewFactory vf = parent.getViewFactory(); 391 if (vf != null) 392 view = vf.create(elem); 393 } 394 } 395 return view; 396 } 397 398 /** 399 * Returns this object as view factory so that we get our TR, TD, TH 400 * and CAPTION subelements created correctly. 401 */ getViewFactory()402 public ViewFactory getViewFactory() 403 { 404 return this; 405 } 406 407 /** 408 * Returns the attributes of this view. This is overridden to provide 409 * the attributes merged with the CSS stuff. 410 */ getAttributes()411 public AttributeSet getAttributes() 412 { 413 if (attributes == null) 414 attributes = getStyleSheet().getViewAttributes(this); 415 return attributes; 416 } 417 418 /** 419 * Returns the stylesheet associated with this view. 420 * 421 * @return the stylesheet associated with this view 422 */ getStyleSheet()423 protected StyleSheet getStyleSheet() 424 { 425 HTMLDocument doc = (HTMLDocument) getDocument(); 426 return doc.getStyleSheet(); 427 } 428 429 /** 430 * Overridden to calculate the size requirements according to the 431 * columns distribution. 432 */ calculateMinorAxisRequirements(int axis, SizeRequirements r)433 protected SizeRequirements calculateMinorAxisRequirements(int axis, 434 SizeRequirements r) 435 { 436 updateGrid(); 437 calculateColumnRequirements(); 438 439 // Calculate the horizontal requirements according to the superclass. 440 // This will return the maximum of the row's widths. 441 r = super.calculateMinorAxisRequirements(axis, r); 442 443 // Try to set the CSS width if it fits. 444 if (width != null) 445 { 446 int w = (int) width.getValue(); 447 if (r.minimum < w) 448 r.minimum = w; 449 } 450 451 // Adjust requirements when we have cell spacing. 452 int adjust = (columnRequirements.length + 1) * cellSpacing; 453 r.minimum += adjust; 454 r.preferred += adjust; 455 456 // Apply the alignment. 457 AttributeSet atts = getAttributes(); 458 Object o = atts.getAttribute(CSS.Attribute.TEXT_ALIGN); 459 r.alignment = 0.0F; 460 if (o != null) 461 { 462 String al = o.toString(); 463 if (al.equals("left")) 464 r.alignment = 0.0F; 465 else if (al.equals("center")) 466 r.alignment = 0.5F; 467 else if (al.equals("right")) 468 r.alignment = 1.0F; 469 } 470 471 // Make it not resize in the horizontal direction. 472 r.maximum = r.preferred; 473 return r; 474 } 475 476 /** 477 * Overridden to perform the table layout before calling the super 478 * implementation. 479 */ layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans)480 protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, 481 int[] spans) 482 { 483 updateGrid(); 484 485 // Mark all rows as invalid along their minor axis to force correct 486 // layout of multi-row cells. 487 int n = getViewCount(); 488 for (int i = 0; i < n; i++) 489 { 490 View row = getView(i); 491 if (row instanceof RowView) 492 ((RowView) row).layoutChanged(axis); 493 } 494 495 layoutColumns(targetSpan); 496 super.layoutMinorAxis(targetSpan, axis, offsets, spans); 497 } 498 499 /** 500 * Calculates the size requirements for the columns. 501 */ calculateColumnRequirements()502 private void calculateColumnRequirements() 503 { 504 int numRows = getViewCount(); 505 totalColumnRequirements.minimum = 0; 506 totalColumnRequirements.preferred = 0; 507 totalColumnRequirements.maximum = 0; 508 509 // In this first pass we find out a suitable total width to fit in 510 // all columns of all rows. 511 for (int r = 0; r < numRows; r++) 512 { 513 View rowView = getView(r); 514 int numCols; 515 if (rowView instanceof RowView) 516 numCols = ((RowView) rowView).getViewCount(); 517 else 518 numCols = 0; 519 520 // We collect the normal (non-relative) column requirements in the 521 // total variable and the relative requirements in the relTotal 522 // variable. In the end we create the maximum of both to get the 523 // real requirements. 524 SizeRequirements total = new SizeRequirements(); 525 SizeRequirements relTotal = new SizeRequirements(); 526 float totalPercent = 0.F; 527 int realCol = 0; 528 for (int c = 0; c < numCols; c++) 529 { 530 View v = rowView.getView(c); 531 if (v instanceof CellView) 532 { 533 CellView cellView = (CellView) v; 534 int colSpan = cellView.colSpan; 535 if (colSpan > 1) 536 { 537 int cellMin = (int) cellView.getMinimumSpan(X_AXIS); 538 int cellPref = (int) cellView.getPreferredSpan(X_AXIS); 539 int cellMax = (int) cellView.getMaximumSpan(X_AXIS); 540 int currentMin = 0; 541 int currentPref = 0; 542 long currentMax = 0; 543 for (int i = 0; i < colSpan; i++) 544 { 545 SizeRequirements req = columnRequirements[realCol]; 546 currentMin += req.minimum; 547 currentPref += req.preferred; 548 currentMax += req.maximum; 549 } 550 int deltaMin = cellMin - currentMin; 551 int deltaPref = cellPref - currentPref; 552 int deltaMax = (int) (cellMax - currentMax); 553 // Distribute delta. 554 for (int i = 0; i < colSpan; i++) 555 { 556 SizeRequirements req = columnRequirements[realCol]; 557 if (deltaMin > 0) 558 req.minimum += deltaMin / colSpan; 559 if (deltaPref > 0) 560 req.preferred += deltaPref / colSpan; 561 if (deltaMax > 0) 562 req.maximum += deltaMax / colSpan; 563 if (columnWidths[realCol] == null 564 || ! columnWidths[realCol].isPercentage()) 565 { 566 total.minimum += req.minimum; 567 total.preferred += req.preferred; 568 total.maximum += req.maximum; 569 } 570 else 571 { 572 relTotal.minimum = 573 Math.max(relTotal.minimum, 574 (int) (req.minimum 575 * columnWidths[realCol].getValue())); 576 relTotal.preferred = 577 Math.max(relTotal.preferred, 578 (int) (req.preferred 579 * columnWidths[realCol].getValue())); 580 relTotal.maximum = 581 Math.max(relTotal.maximum, 582 (int) (req.maximum 583 * columnWidths[realCol].getValue())); 584 totalPercent += columnWidths[realCol].getValue(); 585 } 586 } 587 realCol += colSpan; 588 } 589 else 590 { 591 // Shortcut for colSpan == 1. 592 SizeRequirements req = columnRequirements[realCol]; 593 req.minimum = Math.max(req.minimum, 594 (int) cellView.getMinimumSpan(X_AXIS)); 595 req.preferred = Math.max(req.preferred, 596 (int) cellView.getPreferredSpan(X_AXIS)); 597 req.maximum = Math.max(req.maximum, 598 (int) cellView.getMaximumSpan(X_AXIS)); 599 if (columnWidths[realCol] == null 600 || ! columnWidths[realCol].isPercentage()) 601 { 602 total.minimum += columnRequirements[realCol].minimum; 603 total.preferred += 604 columnRequirements[realCol].preferred; 605 total.maximum += columnRequirements[realCol].maximum; 606 } 607 else 608 { 609 relTotal.minimum = 610 Math.max(relTotal.minimum, 611 (int) (req.minimum 612 / columnWidths[c].getValue())); 613 relTotal.preferred = 614 Math.max(relTotal.preferred, 615 (int) (req.preferred 616 / columnWidths[c].getValue())); 617 relTotal.maximum = 618 Math.max(relTotal.maximum, 619 (int) (req.maximum 620 / columnWidths[c].getValue())); 621 totalPercent += columnWidths[c].getValue(); 622 } 623 realCol += 1; 624 } 625 } 626 } 627 628 // Update the total requirements as follows: 629 // 1. Multiply the absolute requirements with 1 - totalPercent. This 630 // gives the total requirements based on the wishes of the absolute 631 // cells. 632 // 2. Take the maximum of this value and the total relative 633 // requirements. Now we should have enough space for whatever cell 634 // in this column. 635 // 3. Take the maximum of this value and the previous maximum value. 636 total.minimum *= 1.F / (1.F - totalPercent); 637 total.preferred *= 1.F / (1.F - totalPercent); 638 total.maximum *= 1.F / (1.F - totalPercent); 639 640 int rowTotalMin = Math.max(total.minimum, relTotal.minimum); 641 int rowTotalPref = Math.max(total.preferred, relTotal.preferred); 642 int rowTotalMax = Math.max(total.maximum, relTotal.maximum); 643 totalColumnRequirements.minimum = 644 Math.max(totalColumnRequirements.minimum, rowTotalMin); 645 totalColumnRequirements.preferred = 646 Math.max(totalColumnRequirements.preferred, rowTotalPref); 647 totalColumnRequirements.maximum = 648 Math.max(totalColumnRequirements.maximum, rowTotalMax); 649 } 650 651 // Now we know what we want and can fix up the actual relative 652 // column requirements. 653 int numCols = columnRequirements.length; 654 for (int i = 0; i < numCols; i++) 655 { 656 if (columnWidths[i] != null) 657 { 658 columnRequirements[i].minimum = (int) 659 columnWidths[i].getValue(totalColumnRequirements.minimum); 660 columnRequirements[i].preferred = (int) 661 columnWidths[i].getValue(totalColumnRequirements.preferred); 662 columnRequirements[i].maximum = (int) 663 columnWidths[i].getValue(totalColumnRequirements.maximum); 664 } 665 } 666 } 667 668 /** 669 * Lays out the columns. 670 * 671 * @param targetSpan the target span into which the table is laid out 672 */ layoutColumns(int targetSpan)673 private void layoutColumns(int targetSpan) 674 { 675 // Set the spans to the preferred sizes. Determine the space 676 // that we have to adjust the sizes afterwards. 677 long sumPref = 0; 678 int n = columnRequirements.length; 679 for (int i = 0; i < n; i++) 680 { 681 SizeRequirements col = columnRequirements[i]; 682 if (columnWidths[i] != null) 683 columnSpans[i] = (int) columnWidths[i].getValue(targetSpan); 684 else 685 columnSpans[i] = col.preferred; 686 sumPref += columnSpans[i]; 687 } 688 689 // Try to adjust the spans so that we fill the targetSpan. 690 // For adjustments we have to use the targetSpan minus the cumulated 691 // cell spacings. 692 long diff = targetSpan - (n + 1) * cellSpacing - sumPref; 693 float factor = 0.0F; 694 int[] diffs = null; 695 if (diff != 0) 696 { 697 long total = 0; 698 diffs = new int[n]; 699 for (int i = 0; i < n; i++) 700 { 701 // Only adjust the width if we haven't set a column width here. 702 if (columnWidths[i] == null) 703 { 704 SizeRequirements col = columnRequirements[i]; 705 int span; 706 if (diff < 0) 707 { 708 span = col.minimum; 709 diffs[i] = columnSpans[i] - span; 710 } 711 else 712 { 713 span = col.maximum; 714 diffs[i] = span - columnSpans[i]; 715 } 716 total += span; 717 } 718 else 719 total += columnSpans[i]; 720 } 721 722 float maxAdjust = Math.abs(total - sumPref); 723 factor = diff / maxAdjust; 724 factor = Math.min(factor, 1.0F); 725 factor = Math.max(factor, -1.0F); 726 } 727 728 // Actually perform adjustments. 729 int totalOffs = cellSpacing; 730 for (int i = 0; i < n; i++) 731 { 732 columnOffsets[i] = totalOffs; 733 if (diff != 0) 734 { 735 float adjust = factor * diffs[i]; 736 columnSpans[i] += Math.round(adjust); 737 } 738 // Avoid overflow here. 739 totalOffs = (int) Math.min((long) totalOffs + (long) columnSpans[i] 740 + (long) cellSpacing, Integer.MAX_VALUE); 741 } 742 } 743 744 /** 745 * Updates the arrays that contain the row and column data in response 746 * to a change to the table structure. 747 * 748 * Package private to avoid accessor methods. 749 */ updateGrid()750 void updateGrid() 751 { 752 if (! gridValid) 753 { 754 AttributeSet atts = getAttributes(); 755 StyleSheet ss = getStyleSheet(); 756 float emBase = ss.getEMBase(atts); 757 float exBase = ss.getEXBase(atts); 758 int maxColumns = 0; 759 int numRows = getViewCount(); 760 for (int r = 0; r < numRows; r++) 761 { 762 View rowView = getView(r); 763 int numCols = 0; 764 if (rowView instanceof RowView) 765 { 766 int numCells = ((RowView) rowView).getViewCount(); 767 for (int i = 0; i < numCells; i++) 768 { 769 View v = rowView.getView(i); 770 if (v instanceof CellView) 771 numCols += ((CellView) v).colSpan; 772 } 773 } 774 maxColumns = Math.max(numCols, maxColumns); 775 } 776 numColumns = maxColumns; 777 columnWidths = new Length[maxColumns]; 778 int[] rowSpans = new int[maxColumns]; 779 for (int r = 0; r < numRows; r++) 780 { 781 View view = getView(r); 782 if (view instanceof RowView) 783 { 784 RowView rowView = (RowView) view; 785 rowView.rowIndex = r; 786 rowView.overlap = new boolean[maxColumns]; 787 int colIndex = 0; 788 int colCount = rowView.getViewCount(); 789 for (int c = 0; c < maxColumns;) 790 { 791 if (rowSpans[c] > 0) 792 { 793 rowSpans[c]--; 794 rowView.overlap[c] = true; 795 c++; 796 } 797 else if (colIndex < colCount) 798 { 799 View v = rowView.getView(colIndex); 800 colIndex++; 801 if (v instanceof CellView) 802 { 803 CellView cv = (CellView) v; 804 Object o = 805 cv.getAttributes().getAttribute(CSS.Attribute.WIDTH); 806 if (o != null && columnWidths[c] == null 807 && o instanceof Length) 808 { 809 columnWidths[c]= (Length) o; 810 columnWidths[c].setFontBases(emBase, exBase); 811 } 812 int rs = cv.rowSpan - 1; 813 for (int col = cv.colSpan - 1; col >= 0; col--) 814 { 815 rowSpans[c] = rs; 816 c++; 817 } 818 } 819 } 820 else 821 { 822 c++; 823 } 824 } 825 } 826 } 827 columnRequirements = new SizeRequirements[maxColumns]; 828 for (int i = 0; i < maxColumns; i++) 829 columnRequirements[i] = new SizeRequirements(); 830 columnOffsets = new int[maxColumns]; 831 columnSpans = new int[maxColumns]; 832 833 gridValid = true; 834 } 835 } 836 837 /** 838 * Overridden to restrict the table width to the preferred size. 839 */ getMaximumSpan(int axis)840 public float getMaximumSpan(int axis) 841 { 842 float span; 843 if (axis == X_AXIS) 844 span = super.getPreferredSpan(axis); 845 else 846 span = super.getMaximumSpan(axis); 847 return span; 848 } 849 850 /** 851 * Overridden to fetch the CSS attributes when view gets connected. 852 */ setParent(View parent)853 public void setParent(View parent) 854 { 855 super.setParent(parent); 856 if (parent != null) 857 setPropertiesFromAttributes(); 858 } 859 860 /** 861 * Fetches CSS and HTML layout attributes. 862 */ setPropertiesFromAttributes()863 protected void setPropertiesFromAttributes() 864 { 865 super.setPropertiesFromAttributes(); 866 867 // Fetch and parse cell spacing. 868 AttributeSet atts = getAttributes(); 869 StyleSheet ss = getStyleSheet(); 870 float emBase = ss.getEMBase(atts); 871 float exBase = ss.getEXBase(atts); 872 Object o = atts.getAttribute(CSS.Attribute.BORDER_SPACING); 873 if (o != null && o instanceof Length) 874 { 875 Length l = (Length) o; 876 l.setFontBases(emBase, exBase); 877 cellSpacing = (int) l.getValue(); 878 } 879 o = atts.getAttribute(CSS.Attribute.WIDTH); 880 if (o != null && o instanceof Length) 881 { 882 width = (Length) o; 883 width.setFontBases(emBase, exBase); 884 } 885 } 886 887 /** 888 * Overridden to adjust for cellSpacing. 889 */ calculateMajorAxisRequirements(int axis, SizeRequirements r)890 protected SizeRequirements calculateMajorAxisRequirements(int axis, 891 SizeRequirements r) 892 { 893 r = super.calculateMajorAxisRequirements(axis, r); 894 int adjust = (getViewCount() + 1) * cellSpacing; 895 r.minimum += adjust; 896 r.preferred += adjust; 897 r.maximum += adjust; 898 return r; 899 } 900 901 /** 902 * Overridden to adjust for cellSpacing. 903 */ layoutMajorAxis(int targetSpan, int axis, int[] offsets, int spans[])904 protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, 905 int spans[]) 906 { 907 // Mark all rows as invalid along their minor axis to force correct 908 // layout of multi-row cells. 909 int n = getViewCount(); 910 for (int i = 0; i < n; i++) 911 { 912 View row = getView(i); 913 if (row instanceof RowView) 914 ((RowView) row).layoutChanged(axis); 915 } 916 917 int adjust = (getViewCount() + 1) * cellSpacing; 918 super.layoutMajorAxis(targetSpan - adjust, axis, offsets, spans); 919 for (int i = 0; i < offsets.length; i++) 920 { 921 offsets[i] += (i + 1) * cellSpacing; 922 } 923 } 924 925 /** 926 * Overridden to replace view factory with this one. 927 */ insertUpdate(DocumentEvent e, Shape a, ViewFactory f)928 public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) 929 { 930 super.insertUpdate(e, a, this); 931 } 932 933 /** 934 * Overridden to replace view factory with this one. 935 */ removeUpdate(DocumentEvent e, Shape a, ViewFactory f)936 public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) 937 { 938 super.removeUpdate(e, a, this); 939 } 940 941 /** 942 * Overridden to replace view factory with this one. 943 */ changedUpdate(DocumentEvent e, Shape a, ViewFactory f)944 public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) 945 { 946 super.changedUpdate(e, a, this); 947 } 948 replace(int offset, int len, View[] views)949 public void replace(int offset, int len, View[] views) 950 { 951 gridValid = false; 952 super.replace(offset, len, views); 953 } 954 955 /** 956 * We can't use the super class's paint() method because it might cut 957 * off multi-row children. Instead we trigger painting for all rows 958 * and let the rows sort out what to paint and what not. 959 */ paint(Graphics g, Shape a)960 public void paint(Graphics g, Shape a) 961 { 962 Rectangle rect = a instanceof Rectangle ? (Rectangle) a : a.getBounds(); 963 painter.paint(g, rect.x, rect.y, rect.width, rect.height, this); 964 int nRows = getViewCount(); 965 Rectangle inside = getInsideAllocation(a); 966 for (int r = 0; r < nRows; r++) 967 { 968 tmpRect.setBounds(inside); 969 childAllocation(r, tmpRect); 970 paintChild(g, tmpRect, r); 971 } 972 } 973 974 } 975