1/* 2 * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25package javax.swing.text; 26 27import java.awt.*; 28import java.util.BitSet; 29import java.util.Vector; 30import javax.swing.SizeRequirements; 31import javax.swing.event.DocumentEvent; 32 33import javax.swing.text.html.HTML; 34 35/** 36 * <p> 37 * Implements View interface for a table, that is composed of an 38 * element structure where the child elements of the element 39 * this view is responsible for represent rows and the child 40 * elements of the row elements are cells. The cell elements can 41 * have an arbitrary element structure under them, which will 42 * be built with the ViewFactory returned by the getViewFactory 43 * method. 44 * <pre> 45 * 46 * TABLE 47 * ROW 48 * CELL 49 * CELL 50 * ROW 51 * CELL 52 * CELL 53 * 54 * </pre> 55 * <p> 56 * This is implemented as a hierarchy of boxes, the table itself 57 * is a vertical box, the rows are horizontal boxes, and the cells 58 * are vertical boxes. The cells are allowed to span multiple 59 * columns and rows. By default, the table can be thought of as 60 * being formed over a grid (i.e. somewhat like one would find in 61 * gridbag layout), where table cells can request to span more 62 * than one grid cell. The default horizontal span of table cells 63 * will be based upon this grid, but can be changed by reimplementing 64 * the requested span of the cell (i.e. table cells can have independent 65 * spans if desired). 66 * 67 * @author Timothy Prinzing 68 * @see View 69 */ 70public abstract class TableView extends BoxView { 71 72 /** 73 * Constructs a TableView for the given element. 74 * 75 * @param elem the element that this view is responsible for 76 */ 77 public TableView(Element elem) { 78 super(elem, View.Y_AXIS); 79 rows = new Vector<TableRow>(); 80 gridValid = false; 81 totalColumnRequirements = new SizeRequirements(); 82 } 83 84 /** 85 * Creates a new table row. 86 * 87 * @param elem an element 88 * @return the row 89 */ 90 protected TableRow createTableRow(Element elem) { 91 return new TableRow(elem); 92 } 93 94 /** 95 * @deprecated Table cells can now be any arbitrary 96 * View implementation and should be produced by the 97 * ViewFactory rather than the table. 98 * 99 * @param elem an element 100 * @return the cell 101 */ 102 @Deprecated 103 protected TableCell createTableCell(Element elem) { 104 return new TableCell(elem); 105 } 106 107 /** 108 * The number of columns in the table. 109 */ 110 int getColumnCount() { 111 return columnSpans.length; 112 } 113 114 /** 115 * Fetches the span (width) of the given column. 116 * This is used by the nested cells to query the 117 * sizes of grid locations outside of themselves. 118 */ 119 int getColumnSpan(int col) { 120 return columnSpans[col]; 121 } 122 123 /** 124 * The number of rows in the table. 125 */ 126 int getRowCount() { 127 return rows.size(); 128 } 129 130 /** 131 * Fetches the span (height) of the given row. 132 */ 133 int getRowSpan(int row) { 134 View rv = getRow(row); 135 if (rv != null) { 136 return (int) rv.getPreferredSpan(Y_AXIS); 137 } 138 return 0; 139 } 140 141 TableRow getRow(int row) { 142 if (row < rows.size()) { 143 return rows.elementAt(row); 144 } 145 return null; 146 } 147 148 /** 149 * Determines the number of columns occupied by 150 * the table cell represented by given element. 151 */ 152 /*protected*/ int getColumnsOccupied(View v) { 153 // PENDING(prinz) this code should be in the html 154 // paragraph, but we can't add api to enable it. 155 AttributeSet a = v.getElement().getAttributes(); 156 String s = (String) a.getAttribute(HTML.Attribute.COLSPAN); 157 if (s != null) { 158 try { 159 return Integer.parseInt(s); 160 } catch (NumberFormatException nfe) { 161 // fall through to one column 162 } 163 } 164 165 return 1; 166 } 167 168 /** 169 * Determines the number of rows occupied by 170 * the table cell represented by given element. 171 */ 172 /*protected*/ int getRowsOccupied(View v) { 173 // PENDING(prinz) this code should be in the html 174 // paragraph, but we can't add api to enable it. 175 AttributeSet a = v.getElement().getAttributes(); 176 String s = (String) a.getAttribute(HTML.Attribute.ROWSPAN); 177 if (s != null) { 178 try { 179 return Integer.parseInt(s); 180 } catch (NumberFormatException nfe) { 181 // fall through to one row 182 } 183 } 184 185 return 1; 186 } 187 188 /*protected*/ void invalidateGrid() { 189 gridValid = false; 190 } 191 192 protected void forwardUpdate(DocumentEvent.ElementChange ec, 193 DocumentEvent e, Shape a, ViewFactory f) { 194 super.forwardUpdate(ec, e, a, f); 195 // A change in any of the table cells usually effects the whole table, 196 // so redraw it all! 197 if (a != null) { 198 Component c = getContainer(); 199 if (c != null) { 200 Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a : 201 a.getBounds(); 202 c.repaint(alloc.x, alloc.y, alloc.width, alloc.height); 203 } 204 } 205 } 206 207 /** 208 * Change the child views. This is implemented to 209 * provide the superclass behavior and invalidate the 210 * grid so that rows and columns will be recalculated. 211 */ 212 public void replace(int offset, int length, View[] views) { 213 super.replace(offset, length, views); 214 invalidateGrid(); 215 } 216 217 /** 218 * Fill in the grid locations that are placeholders 219 * for multi-column, multi-row, and missing grid 220 * locations. 221 */ 222 void updateGrid() { 223 if (! gridValid) { 224 // determine which views are table rows and clear out 225 // grid points marked filled. 226 rows.removeAllElements(); 227 int n = getViewCount(); 228 for (int i = 0; i < n; i++) { 229 View v = getView(i); 230 if (v instanceof TableRow) { 231 rows.addElement((TableRow) v); 232 TableRow rv = (TableRow) v; 233 rv.clearFilledColumns(); 234 rv.setRow(i); 235 } 236 } 237 238 int maxColumns = 0; 239 int nrows = rows.size(); 240 for (int row = 0; row < nrows; row++) { 241 TableRow rv = getRow(row); 242 int col = 0; 243 for (int cell = 0; cell < rv.getViewCount(); cell++, col++) { 244 View cv = rv.getView(cell); 245 // advance to a free column 246 for (; rv.isFilled(col); col++); 247 int rowSpan = getRowsOccupied(cv); 248 int colSpan = getColumnsOccupied(cv); 249 if ((colSpan > 1) || (rowSpan > 1)) { 250 // fill in the overflow entries for this cell 251 int rowLimit = row + rowSpan; 252 int colLimit = col + colSpan; 253 for (int i = row; i < rowLimit; i++) { 254 for (int j = col; j < colLimit; j++) { 255 if (i != row || j != col) { 256 addFill(i, j); 257 } 258 } 259 } 260 if (colSpan > 1) { 261 col += colSpan - 1; 262 } 263 } 264 } 265 maxColumns = Math.max(maxColumns, col); 266 } 267 268 // setup the column layout/requirements 269 columnSpans = new int[maxColumns]; 270 columnOffsets = new int[maxColumns]; 271 columnRequirements = new SizeRequirements[maxColumns]; 272 for (int i = 0; i < maxColumns; i++) { 273 columnRequirements[i] = new SizeRequirements(); 274 } 275 gridValid = true; 276 } 277 } 278 279 /** 280 * Mark a grid location as filled in for a cells overflow. 281 */ 282 void addFill(int row, int col) { 283 TableRow rv = getRow(row); 284 if (rv != null) { 285 rv.fillColumn(col); 286 } 287 } 288 289 /** 290 * Lays out the columns to fit within the given target span. 291 * Returns the results through {@code offsets} and {@code spans}. 292 * 293 * @param targetSpan the given span for total of all the table 294 * columns 295 * @param reqs the requirements desired for each column. This 296 * is the column maximum of the cells minimum, preferred, and 297 * maximum requested span 298 * @param spans the return value of how much to allocated to 299 * each column 300 * @param offsets the return value of the offset from the 301 * origin for each column 302 */ 303 protected void layoutColumns(int targetSpan, int[] offsets, int[] spans, 304 SizeRequirements[] reqs) { 305 // allocate using the convenience method on SizeRequirements 306 SizeRequirements.calculateTiledPositions(targetSpan, null, reqs, 307 offsets, spans); 308 } 309 310 /** 311 * Perform layout for the minor axis of the box (i.e. the 312 * axis orthogonal to the axis that it represents). The results 313 * of the layout should be placed in the given arrays which represent 314 * the allocations to the children along the minor axis. This 315 * is called by the superclass whenever the layout needs to be 316 * updated along the minor axis. 317 * <p> 318 * This is implemented to call the 319 * {@link #layoutColumns layoutColumns} method, and then 320 * forward to the superclass to actually carry out the layout 321 * of the tables rows. 322 * 323 * @param targetSpan the total span given to the view, which 324 * would be used to layout the children. 325 * @param axis the axis being layed out. 326 * @param offsets the offsets from the origin of the view for 327 * each of the child views. This is a return value and is 328 * filled in by the implementation of this method. 329 * @param spans the span of each child view. This is a return 330 * value and is filled in by the implementation of this method. 331 */ 332 protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) { 333 // make grid is properly represented 334 updateGrid(); 335 336 // all of the row layouts are invalid, so mark them that way 337 int n = getRowCount(); 338 for (int i = 0; i < n; i++) { 339 TableRow row = getRow(i); 340 row.layoutChanged(axis); 341 } 342 343 // calculate column spans 344 layoutColumns(targetSpan, columnOffsets, columnSpans, columnRequirements); 345 346 // continue normal layout 347 super.layoutMinorAxis(targetSpan, axis, offsets, spans); 348 } 349 350 /** 351 * Calculate the requirements for the minor axis. This is called by 352 * the superclass whenever the requirements need to be updated (i.e. 353 * a preferenceChanged was messaged through this view). 354 * <p> 355 * This is implemented to calculate the requirements as the sum of the 356 * requirements of the columns. 357 */ 358 protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) { 359 updateGrid(); 360 361 // calculate column requirements for each column 362 calculateColumnRequirements(axis); 363 364 365 // the requirements are the sum of the columns. 366 if (r == null) { 367 r = new SizeRequirements(); 368 } 369 long min = 0; 370 long pref = 0; 371 long max = 0; 372 for (SizeRequirements req : columnRequirements) { 373 min += req.minimum; 374 pref += req.preferred; 375 max += req.maximum; 376 } 377 r.minimum = (int) min; 378 r.preferred = (int) pref; 379 r.maximum = (int) max; 380 r.alignment = 0; 381 382 totalColumnRequirements.minimum = r.minimum; 383 totalColumnRequirements.preferred = r.preferred; 384 totalColumnRequirements.maximum = r.maximum; 385 386 return r; 387 } 388 389 /* 390 boolean shouldTrace() { 391 AttributeSet a = getElement().getAttributes(); 392 Object o = a.getAttribute(HTML.Attribute.ID); 393 if ((o != null) && o.equals("debug")) { 394 return true; 395 } 396 return false; 397 } 398 */ 399 400 /** 401 * Calculate the requirements for each column. The calculation 402 * is done as two passes over the table. The table cells that 403 * occupy a single column are scanned first to determine the 404 * maximum of minimum, preferred, and maximum spans along the 405 * give axis. Table cells that span multiple columns are excluded 406 * from the first pass. A second pass is made to determine if 407 * the cells that span multiple columns are satisfied. If the 408 * column requirements are not satisified, the needs of the 409 * multi-column cell is mixed into the existing column requirements. 410 * The calculation of the multi-column distribution is based upon 411 * the proportions of the existing column requirements and taking 412 * into consideration any constraining maximums. 413 */ 414 void calculateColumnRequirements(int axis) { 415 416 for (SizeRequirements req : columnRequirements) { 417 req.minimum = 0; 418 req.preferred = 0; 419 req.maximum = Integer.MAX_VALUE; 420 } 421 422 // pass 1 - single column cells 423 boolean hasMultiColumn = false; 424 int nrows = getRowCount(); 425 for (int i = 0; i < nrows; i++) { 426 TableRow row = getRow(i); 427 int col = 0; 428 int ncells = row.getViewCount(); 429 for (int cell = 0; cell < ncells; cell++, col++) { 430 View cv = row.getView(cell); 431 for (; row.isFilled(col); col++); // advance to a free column 432 int rowSpan = getRowsOccupied(cv); 433 int colSpan = getColumnsOccupied(cv); 434 if (colSpan == 1) { 435 checkSingleColumnCell(axis, col, cv); 436 } else { 437 hasMultiColumn = true; 438 col += colSpan - 1; 439 } 440 } 441 } 442 443 // pass 2 - multi-column cells 444 if (hasMultiColumn) { 445 for (int i = 0; i < nrows; i++) { 446 TableRow row = getRow(i); 447 int col = 0; 448 int ncells = row.getViewCount(); 449 for (int cell = 0; cell < ncells; cell++, col++) { 450 View cv = row.getView(cell); 451 for (; row.isFilled(col); col++); // advance to a free column 452 int colSpan = getColumnsOccupied(cv); 453 if (colSpan > 1) { 454 checkMultiColumnCell(axis, col, colSpan, cv); 455 col += colSpan - 1; 456 } 457 } 458 } 459 } 460 461 /* 462 if (shouldTrace()) { 463 System.err.println("calc:"); 464 for (int i = 0; i < columnRequirements.length; i++) { 465 System.err.println(" " + i + ": " + columnRequirements[i]); 466 } 467 } 468 */ 469 } 470 471 /** 472 * check the requirements of a table cell that spans a single column. 473 */ 474 void checkSingleColumnCell(int axis, int col, View v) { 475 SizeRequirements req = columnRequirements[col]; 476 req.minimum = Math.max((int) v.getMinimumSpan(axis), req.minimum); 477 req.preferred = Math.max((int) v.getPreferredSpan(axis), req.preferred); 478 req.maximum = Math.max((int) v.getMaximumSpan(axis), req.maximum); 479 } 480 481 /** 482 * check the requirements of a table cell that spans multiple 483 * columns. 484 */ 485 void checkMultiColumnCell(int axis, int col, int ncols, View v) { 486 // calculate the totals 487 long min = 0; 488 long pref = 0; 489 long max = 0; 490 for (int i = 0; i < ncols; i++) { 491 SizeRequirements req = columnRequirements[col + i]; 492 min += req.minimum; 493 pref += req.preferred; 494 max += req.maximum; 495 } 496 497 // check if the minimum size needs adjustment. 498 int cmin = (int) v.getMinimumSpan(axis); 499 if (cmin > min) { 500 /* 501 * the columns that this cell spans need adjustment to fit 502 * this table cell.... calculate the adjustments. The 503 * maximum for each cell is the maximum of the existing 504 * maximum or the amount needed by the cell. 505 */ 506 SizeRequirements[] reqs = new SizeRequirements[ncols]; 507 for (int i = 0; i < ncols; i++) { 508 SizeRequirements r = reqs[i] = columnRequirements[col + i]; 509 r.maximum = Math.max(r.maximum, (int) v.getMaximumSpan(axis)); 510 } 511 int[] spans = new int[ncols]; 512 int[] offsets = new int[ncols]; 513 SizeRequirements.calculateTiledPositions(cmin, null, reqs, 514 offsets, spans); 515 // apply the adjustments 516 for (int i = 0; i < ncols; i++) { 517 SizeRequirements req = reqs[i]; 518 req.minimum = Math.max(spans[i], req.minimum); 519 req.preferred = Math.max(req.minimum, req.preferred); 520 req.maximum = Math.max(req.preferred, req.maximum); 521 } 522 } 523 524 // check if the preferred size needs adjustment. 525 int cpref = (int) v.getPreferredSpan(axis); 526 if (cpref > pref) { 527 /* 528 * the columns that this cell spans need adjustment to fit 529 * this table cell.... calculate the adjustments. The 530 * maximum for each cell is the maximum of the existing 531 * maximum or the amount needed by the cell. 532 */ 533 SizeRequirements[] reqs = new SizeRequirements[ncols]; 534 for (int i = 0; i < ncols; i++) { 535 SizeRequirements r = reqs[i] = columnRequirements[col + i]; 536 } 537 int[] spans = new int[ncols]; 538 int[] offsets = new int[ncols]; 539 SizeRequirements.calculateTiledPositions(cpref, null, reqs, 540 offsets, spans); 541 // apply the adjustments 542 for (int i = 0; i < ncols; i++) { 543 SizeRequirements req = reqs[i]; 544 req.preferred = Math.max(spans[i], req.preferred); 545 req.maximum = Math.max(req.preferred, req.maximum); 546 } 547 } 548 549 } 550 551 /** 552 * Fetches the child view that represents the given position in 553 * the model. This is implemented to walk through the children 554 * looking for a range that contains the given position. In this 555 * view the children do not necessarily have a one to one mapping 556 * with the child elements. 557 * 558 * @param pos the search position >= 0 559 * @param a the allocation to the table on entry, and the 560 * allocation of the view containing the position on exit 561 * @return the view representing the given position, or 562 * <code>null</code> if there isn't one 563 */ 564 protected View getViewAtPosition(int pos, Rectangle a) { 565 int n = getViewCount(); 566 for (int i = 0; i < n; i++) { 567 View v = getView(i); 568 int p0 = v.getStartOffset(); 569 int p1 = v.getEndOffset(); 570 if ((pos >= p0) && (pos < p1)) { 571 // it's in this view. 572 if (a != null) { 573 childAllocation(i, a); 574 } 575 return v; 576 } 577 } 578 if (pos == getEndOffset()) { 579 View v = getView(n - 1); 580 if (a != null) { 581 this.childAllocation(n - 1, a); 582 } 583 return v; 584 } 585 return null; 586 } 587 588 // ---- variables ---------------------------------------------------- 589 590 int[] columnSpans; 591 int[] columnOffsets; 592 593 SizeRequirements totalColumnRequirements; 594 595 SizeRequirements[] columnRequirements; 596 Vector<TableRow> rows; 597 boolean gridValid; 598 private static final BitSet EMPTY = new BitSet(); 599 600 /** 601 * View of a row in a row-centric table. 602 */ 603 public class TableRow extends BoxView { 604 605 /** 606 * Constructs a TableView for the given element. 607 * 608 * @param elem the element that this view is responsible for 609 * @since 1.4 610 */ 611 public TableRow(Element elem) { 612 super(elem, View.X_AXIS); 613 fillColumns = new BitSet(); 614 } 615 616 void clearFilledColumns() { 617 fillColumns.and(EMPTY); 618 } 619 620 void fillColumn(int col) { 621 fillColumns.set(col); 622 } 623 624 boolean isFilled(int col) { 625 return fillColumns.get(col); 626 } 627 628 /** get location in the overall set of rows */ 629 int getRow() { 630 return row; 631 } 632 633 /** 634 * set location in the overall set of rows, this is 635 * set by the TableView.updateGrid() method. 636 */ 637 void setRow(int row) { 638 this.row = row; 639 } 640 641 /** 642 * The number of columns present in this row. 643 */ 644 int getColumnCount() { 645 int nfill = 0; 646 int n = fillColumns.size(); 647 for (int i = 0; i < n; i++) { 648 if (fillColumns.get(i)) { 649 nfill ++; 650 } 651 } 652 return getViewCount() + nfill; 653 } 654 655 /** 656 * Change the child views. This is implemented to 657 * provide the superclass behavior and invalidate the 658 * grid so that rows and columns will be recalculated. 659 */ 660 public void replace(int offset, int length, View[] views) { 661 super.replace(offset, length, views); 662 invalidateGrid(); 663 } 664 665 @Override 666 protected SizeRequirements calculateMajorAxisRequirements(int axis, SizeRequirements r) { 667 SizeRequirements req = new SizeRequirements(); 668 req.minimum = totalColumnRequirements.minimum; 669 req.maximum = totalColumnRequirements.maximum; 670 req.preferred = totalColumnRequirements.preferred; 671 req.alignment = 0f; 672 return req; 673 } 674 675 @Override 676 public float getMinimumSpan(int axis) { 677 float value; 678 679 if (axis == View.X_AXIS) { 680 value = totalColumnRequirements.minimum + getLeftInset() + getRightInset(); 681 } else { 682 value = super.getMinimumSpan(axis); 683 } 684 return value; 685 } 686 687 @Override 688 public float getMaximumSpan(int axis) { 689 float value; 690 691 if (axis == View.X_AXIS) { 692 // We're flexible. 693 value = (float) Integer.MAX_VALUE; 694 } else { 695 value = super.getMaximumSpan(axis); 696 } 697 return value; 698 } 699 700 @Override 701 public float getPreferredSpan(int axis) { 702 float value; 703 704 if (axis == View.X_AXIS) { 705 value = totalColumnRequirements.preferred + getLeftInset() + getRightInset(); 706 } else { 707 value = super.getPreferredSpan(axis); 708 } 709 return value; 710 } 711 712 /** 713 * Perform layout for the major axis of the box (i.e. the 714 * axis that it represents). The results of the layout should 715 * be placed in the given arrays which represent the allocations 716 * to the children along the major axis. 717 * <p> 718 * This is re-implemented to give each child the span of the column 719 * width for the table, and to give cells that span multiple columns 720 * the multi-column span. 721 * 722 * @param targetSpan the total span given to the view, which 723 * would be used to layout the children. 724 * @param axis the axis being layed out. 725 * @param offsets the offsets from the origin of the view for 726 * each of the child views. This is a return value and is 727 * filled in by the implementation of this method. 728 * @param spans the span of each child view. This is a return 729 * value and is filled in by the implementation of this method. 730 */ 731 protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) { 732 int col = 0; 733 int ncells = getViewCount(); 734 for (int cell = 0; cell < ncells; cell++, col++) { 735 View cv = getView(cell); 736 for (; isFilled(col); col++); // advance to a free column 737 int colSpan = getColumnsOccupied(cv); 738 spans[cell] = columnSpans[col]; 739 offsets[cell] = columnOffsets[col]; 740 if (colSpan > 1) { 741 int n = columnSpans.length; 742 for (int j = 1; j < colSpan; j++) { 743 // Because the table may be only partially formed, some 744 // of the columns may not yet exist. Therefore we check 745 // the bounds. 746 if ((col+j) < n) { 747 spans[cell] += columnSpans[col+j]; 748 } 749 } 750 col += colSpan - 1; 751 } 752 } 753 } 754 755 /** 756 * Perform layout for the minor axis of the box (i.e. the 757 * axis orthogonal to the axis that it represents). The results 758 * of the layout should be placed in the given arrays which represent 759 * the allocations to the children along the minor axis. This 760 * is called by the superclass whenever the layout needs to be 761 * updated along the minor axis. 762 * <p> 763 * This is implemented to delegate to the superclass, then adjust 764 * the span for any cell that spans multiple rows. 765 * 766 * @param targetSpan the total span given to the view, which 767 * would be used to layout the children. 768 * @param axis the axis being layed out. 769 * @param offsets the offsets from the origin of the view for 770 * each of the child views. This is a return value and is 771 * filled in by the implementation of this method. 772 * @param spans the span of each child view. This is a return 773 * value and is filled in by the implementation of this method. 774 */ 775 protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) { 776 super.layoutMinorAxis(targetSpan, axis, offsets, spans); 777 int col = 0; 778 int ncells = getViewCount(); 779 for (int cell = 0; cell < ncells; cell++, col++) { 780 View cv = getView(cell); 781 for (; isFilled(col); col++); // advance to a free column 782 int colSpan = getColumnsOccupied(cv); 783 int rowSpan = getRowsOccupied(cv); 784 if (rowSpan > 1) { 785 for (int j = 1; j < rowSpan; j++) { 786 // test bounds of each row because it may not exist 787 // either because of error or because the table isn't 788 // fully loaded yet. 789 int row = getRow() + j; 790 if (row < TableView.this.getViewCount()) { 791 int span = TableView.this.getSpan(Y_AXIS, getRow()+j); 792 spans[cell] += span; 793 } 794 } 795 } 796 if (colSpan > 1) { 797 col += colSpan - 1; 798 } 799 } 800 } 801 802 /** 803 * Determines the resizability of the view along the 804 * given axis. A value of 0 or less is not resizable. 805 * 806 * @param axis may be either View.X_AXIS or View.Y_AXIS 807 * @return the resize weight 808 * @exception IllegalArgumentException for an invalid axis 809 */ 810 public int getResizeWeight(int axis) { 811 return 1; 812 } 813 814 /** 815 * Fetches the child view that represents the given position in 816 * the model. This is implemented to walk through the children 817 * looking for a range that contains the given position. In this 818 * view the children do not necessarily have a one to one mapping 819 * with the child elements. 820 * 821 * @param pos the search position >= 0 822 * @param a the allocation to the table on entry, and the 823 * allocation of the view containing the position on exit 824 * @return the view representing the given position, or 825 * <code>null</code> if there isn't one 826 */ 827 protected View getViewAtPosition(int pos, Rectangle a) { 828 int n = getViewCount(); 829 for (int i = 0; i < n; i++) { 830 View v = getView(i); 831 int p0 = v.getStartOffset(); 832 int p1 = v.getEndOffset(); 833 if ((pos >= p0) && (pos < p1)) { 834 // it's in this view. 835 if (a != null) { 836 childAllocation(i, a); 837 } 838 return v; 839 } 840 } 841 if (pos == getEndOffset()) { 842 View v = getView(n - 1); 843 if (a != null) { 844 this.childAllocation(n - 1, a); 845 } 846 return v; 847 } 848 return null; 849 } 850 851 /** columns filled by multi-column or multi-row cells */ 852 BitSet fillColumns; 853 /** the row within the overall grid */ 854 int row; 855 } 856 857 /** 858 * @deprecated A table cell can now be any View implementation. 859 */ 860 @Deprecated 861 public class TableCell extends BoxView implements GridCell { 862 863 /** 864 * Constructs a TableCell for the given element. 865 * 866 * @param elem the element that this view is responsible for 867 * @since 1.4 868 */ 869 public TableCell(Element elem) { 870 super(elem, View.Y_AXIS); 871 } 872 873 // --- GridCell methods ------------------------------------- 874 875 /** 876 * Gets the number of columns this cell spans (e.g. the 877 * grid width). 878 * 879 * @return the number of columns 880 */ 881 public int getColumnCount() { 882 return 1; 883 } 884 885 /** 886 * Gets the number of rows this cell spans (that is, the 887 * grid height). 888 * 889 * @return the number of rows 890 */ 891 public int getRowCount() { 892 return 1; 893 } 894 895 896 /** 897 * Sets the grid location. 898 * 899 * @param row the row >= 0 900 * @param col the column >= 0 901 */ 902 public void setGridLocation(int row, int col) { 903 this.row = row; 904 this.col = col; 905 } 906 907 /** 908 * Gets the row of the grid location 909 */ 910 public int getGridRow() { 911 return row; 912 } 913 914 /** 915 * Gets the column of the grid location 916 */ 917 public int getGridColumn() { 918 return col; 919 } 920 921 int row; 922 int col; 923 } 924 925 /** 926 * <em> 927 * THIS IS NO LONGER USED, AND WILL BE REMOVED IN THE 928 * NEXT RELEASE. THE JCK SIGNATURE TEST THINKS THIS INTERFACE 929 * SHOULD EXIST 930 * </em> 931 */ 932 interface GridCell { 933 934 /** 935 * Sets the grid location. 936 * 937 * @param row the row >= 0 938 * @param col the column >= 0 939 */ 940 public void setGridLocation(int row, int col); 941 942 /** 943 * Gets the row of the grid location 944 */ 945 public int getGridRow(); 946 947 /** 948 * Gets the column of the grid location 949 */ 950 public int getGridColumn(); 951 952 /** 953 * Gets the number of columns this cell spans (e.g. the 954 * grid width). 955 * 956 * @return the number of columns 957 */ 958 public int getColumnCount(); 959 960 /** 961 * Gets the number of rows this cell spans (that is, the 962 * grid height). 963 * 964 * @return the number of rows 965 */ 966 public int getRowCount(); 967 968 } 969 970} 971