001/* TableView.java -- A view impl for tables inside styled text 002 Copyright (C) 2006 Free Software Foundation, Inc. 003 004This file is part of GNU Classpath. 005 006GNU Classpath is free software; you can redistribute it and/or modify 007it under the terms of the GNU General Public License as published by 008the Free Software Foundation; either version 2, or (at your option) 009any later version. 010 011GNU Classpath is distributed in the hope that it will be useful, but 012WITHOUT ANY WARRANTY; without even the implied warranty of 013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014General Public License for more details. 015 016You should have received a copy of the GNU General Public License 017along with GNU Classpath; see the file COPYING. If not, write to the 018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 01902110-1301 USA. 020 021Linking this library statically or dynamically with other modules is 022making a combined work based on this library. Thus, the terms and 023conditions of the GNU General Public License cover the whole 024combination. 025 026As a special exception, the copyright holders of this library give you 027permission to link this library with independent modules to produce an 028executable, regardless of the license terms of these independent 029modules, and to copy and distribute the resulting executable under 030terms of your choice, provided that you also meet, for each linked 031independent module, the terms and conditions of the license of that 032module. An independent module is a module which is not derived from 033or based on this library. If you modify this library, you may extend 034this exception to your version of the library, but you are not 035obligated to do so. If you do not wish to do so, delete this 036exception statement from your version. */ 037 038 039package javax.swing.text; 040 041import java.awt.Rectangle; 042import java.awt.Shape; 043 044import javax.swing.SizeRequirements; 045import javax.swing.event.DocumentEvent; 046 047/** 048 * A {@link View} implementation for rendering tables inside styled text. 049 * Tables are rendered as vertical boxes (see {@link BoxView}). These boxes 050 * have a number of child views, which are the rows of the table. These are 051 * horizontal boxes containing the actuall cells of the table. These cells 052 * can be arbitrary view implementations and are fetched via the 053 * {@link ViewFactory} returned by {@link View#getViewFactory}. 054 * 055 * @author Roman Kennke (kennke@aicas.com) 056 */ 057public abstract class TableView 058 extends BoxView 059{ 060 061 /** 062 * A view implementation that renders a row of a <code>TableView</code>. 063 * This is implemented as a horizontal box that contains the actual cells 064 * of the table. 065 * 066 * @author Roman Kennke (kennke@aicas.com) 067 */ 068 public class TableRow 069 extends BoxView 070 { 071 /** 072 * Creates a new instance of <code>TableRow</code>. 073 * 074 * @param el the element for which to create a row view 075 */ 076 public TableRow(Element el) 077 { 078 super(el, X_AXIS); 079 } 080 081 /** 082 * Replaces some child views with a new set of child views. This is 083 * implemented to call the superclass behaviour and invalidates the row 084 * grid so that rows and columns will be recalculated. 085 * 086 * @param offset the start offset at which to replace views 087 * @param length the number of views to remove 088 * @param views the new set of views 089 */ 090 public void replace(int offset, int length, View[] views) 091 { 092 super.replace(offset, length, views); 093 int viewCount = getViewCount(); 094 if (columnRequirements == null 095 || viewCount > columnRequirements.length) 096 { 097 columnRequirements = new SizeRequirements[viewCount]; 098 for (int i = 0; i < columnRequirements.length; i++) 099 columnRequirements[i] = new SizeRequirements(); 100 } 101 if (columnOffsets == null || columnOffsets.length < viewCount) 102 columnOffsets = new int[viewCount]; 103 if (columnSpans == null || columnSpans.length < viewCount) 104 columnSpans = new int[viewCount]; 105 layoutChanged(X_AXIS); 106 } 107 108 /** 109 * Lays out the box's child views along the major axis. This is 110 * reimplemented so that the child views all have the width of their 111 * column. 112 * 113 * @param targetSpan the total span of the view 114 * @param axis the axis that is laid out 115 * @param offsets an array that holds the offsets of the child views after 116 * this method returned 117 * @param spans an array that holds the spans of the child views after this 118 * method returned 119 */ 120 protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, 121 int[] spans) 122 { 123 // Some sanity checks. If these preconditions are not met, then the 124 // following code will not work. Also, there must be something 125 // seriously wrong then. 126 assert(offsets.length == columnOffsets.length); 127 assert(spans.length == columnSpans.length); 128 assert(offsets.length == spans.length); 129 for (int i = 0; i < offsets.length; ++i) 130 { 131 offsets[i] = columnOffsets[i]; 132 spans[i] = columnSpans[i]; 133 } 134 } 135 136 /** 137 * Lays out the box's child views along the minor axis (the orthogonal axis 138 * to the major axis). This is reimplemented to call the super behaviour 139 * and then adjust the span of the child views that span multiple rows. 140 * 141 * @param targetSpan the total span of the view 142 * @param axis the axis that is laid out 143 * @param offsets an array that holds the offsets of the child views after 144 * this method returned 145 * @param spans an array that holds the spans of the child views after this 146 * method returned 147 */ 148 protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, 149 int[] spans) 150 { 151 // FIXME: Figure out how to fetch the row heights from the TableView's 152 // element. 153 super.layoutMinorAxis(targetSpan, axis, offsets, spans); 154 } 155 156 /** 157 * Determines the resizeability of this view along the specified axis. 158 * 159 * @param axis the axis of which to fetch the resizability 160 * 161 * @return the resize weight or <= 0 if this view is not resizable 162 * 163 * @throws IllegalArgumentException when an illegal axis is specified 164 */ 165 public int getResizeWeight(int axis) 166 { 167 // TODO: Figure out if this is ok. I would think so, but better test 168 // this. 169 return 0; 170 } 171 172 /** 173 * Returns the child view that represents the specified position in the 174 * model. This is reimplemented because in this view we do not necessarily 175 * have a one to one mapping of child elements to child views. 176 * 177 * @param pos the model position for which to query the view 178 * @param a the allocation of this view 179 * 180 * @return the view that corresponds to the specified model position or 181 * <code>null</code> if there is none 182 */ 183 protected View getViewAtPosition(int pos, Rectangle a) 184 { 185 // FIXME: Do not call super here. Instead walk through the child views 186 // and look for a range that contains the given position. 187 return super.getViewAtPosition(pos, a); 188 } 189 } 190 191 /** 192 * This class is deprecated and not used anymore. Table cells are 193 * rendered by an arbitrary <code>View</code> implementation. 194 * 195 * @author Roman Kennke (kennke@aicas.com) 196 * 197 * @deprecated Table cells are now rendered by an arbitrary <code>View</code> 198 * implementation. 199 */ 200 public class TableCell 201 extends BoxView 202 { 203 204 /** 205 * The row number of this cell. 206 */ 207 private int row; 208 209 /** 210 * The column number of this cell. 211 */ 212 private int column; 213 214 /** 215 * Creates a new instance. 216 * 217 * @param el the element 218 * 219 * @deprecated Table cells are now rendered by an arbitrary 220 * <code>View</code> implementation. 221 */ 222 public TableCell(Element el) 223 { 224 super(el, X_AXIS); 225 } 226 227 /** 228 * Returns the number of columns that this cell spans. 229 * 230 * @return the number of columns that this cell spans 231 * 232 * @deprecated Table cells are now rendered by an arbitrary 233 * <code>View</code> implementation. 234 */ 235 public int getColumnCount() 236 { 237 // TODO: Figure out if this is right. However, this is not so important 238 // since this class isn't used anyway (except maybe be application code 239 // that still uses this deprecated class). 240 return 1; 241 } 242 243 /** 244 * Returns the number of rows that this cell spans. 245 * 246 * @return the number of rows that this cell spans 247 * 248 * @deprecated Table cells are now rendered by an arbitrary 249 * <code>View</code> implementation. 250 */ 251 public int getRowCount() 252 { 253 // TODO: Figure out if this is right. However, this is not so important 254 // since this class isn't used anyway (except maybe be application code 255 // that still uses this deprecated class). 256 return 1; 257 } 258 259 /** 260 * Sets the grid location of this table cell. 261 * 262 * @param r the row of this cell 263 * @param c the column of this cell 264 * 265 * @deprecated Table cells are now rendered by an arbitrary 266 * <code>View</code> implementation. 267 */ 268 public void setGridLocation(int r, int c) 269 { 270 row = r; 271 column = c; 272 } 273 274 /** 275 * Returns the row number of this cell. 276 * 277 * @return the row number of this cell 278 * 279 * @deprecated Table cells are now rendered by an arbitrary 280 * <code>View</code> implementation. 281 */ 282 public int getGridRow() 283 { 284 return row; 285 } 286 287 /** 288 * Returns the column number of this cell. 289 * 290 * @return the column number of this cell 291 * 292 * @deprecated Table cells are now rendered by an arbitrary 293 * <code>View</code> implementation. 294 */ 295 public int getGridColumn() 296 { 297 return column; 298 } 299 } 300 301 /** 302 * The offsets of the columns of this table. Package private to avoid 303 * synthetic accessor methods. 304 */ 305 int[] columnOffsets; 306 307 /** 308 * The spans of the columns of this table. Package private to avoid 309 * synthetic accessor methods. 310 */ 311 int[] columnSpans; 312 313 /** 314 * The size requirements of the columns. 315 */ 316 SizeRequirements[] columnRequirements = new SizeRequirements[0]; 317 318 /** 319 * Creates a new instance of <code>TableView</code>. 320 * 321 * @param el the element for which to create a table view 322 */ 323 public TableView(Element el) 324 { 325 super(el, Y_AXIS); 326 } 327 328 /** 329 * Replaces a number of child views with a set of new child views. This is 330 * implemented to call the superclass behaviour and invalidate the layout. 331 * 332 * @param offset the offset at which to replace child views 333 * @param length the number of child views to remove 334 * @param views the new set of views 335 */ 336 public void replace(int offset, int length, View[] views) 337 { 338 super.replace(offset, length, views); 339 layoutChanged(Y_AXIS); 340 } 341 342 /** 343 * Creates a view for a table row. 344 * 345 * @param el the element that represents the table row 346 * 347 * @return a view for rendering the table row 348 */ 349 protected TableRow createTableRow(Element el) 350 { 351 return new TableRow(el); 352 } 353 354 /** 355 * Creates a view for a table cell. This method is deprecated and not used 356 * anymore. 357 * 358 * @param el the element that represents the table cell 359 * 360 * @return a view for rendering the table cell 361 * 362 * @deprecated Table cells are now rendered by an arbitrary 363 * <code>View</code> implementation. 364 */ 365 protected TableCell createTableCell(Element el) 366 { 367 return new TableCell(el); 368 } 369 370 protected void forwardUpdate(DocumentEvent.ElementChange ec, DocumentEvent e, 371 Shape a, ViewFactory vf) 372 { 373 // TODO: Figure out what to do here. 374 } 375 376 /** 377 * Lays out the columns to fit within the specified target span. 378 * 379 * @param targetSpan the total span for the columns 380 * @param offsets an array that holds the offsets of the columns when this 381 * method returns 382 * @param spans an array that holds the spans of the columns when this method 383 * returns 384 * @param reqs the size requirements for each column 385 */ 386 protected void layoutColumns(int targetSpan, int[] offsets, int spans[], 387 SizeRequirements[] reqs) 388 { 389 updateColumnRequirements(); 390 SizeRequirements r = calculateMinorAxisRequirements(X_AXIS, null); 391 SizeRequirements.calculateTiledPositions(targetSpan, r, columnRequirements, 392 offsets, spans); 393 } 394 395 /** 396 * Lays out the child views along the minor axis of the table (that is the 397 * horizontal axis). This is implemented to call {@link #layoutColumns} to 398 * layout the column layout of this table, and then forward to the superclass 399 * to actually lay out the rows. 400 * 401 * @param targetSpan the available span along the minor (horizontal) axis 402 * @param axis the axis 403 * @param offsets an array that holds the offsets of the columns when this 404 * method returns 405 * @param spans an array that holds the spans of the columns when this method 406 * returns 407 */ 408 protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, 409 int[] spans) 410 { 411 // TODO: Prepare size requirements for the columns. 412 layoutColumns(targetSpan, columnOffsets, columnSpans, columnRequirements); 413 super.layoutMinorAxis(targetSpan, axis, offsets, spans); 414 } 415 416 /** 417 * Calculates the requirements of this view for the minor (== horizontal) 418 * axis. 419 * 420 * This is reimplemented to calculate the requirements as the sum of the 421 * size requirements of the columns. 422 * 423 * @param axis the axis 424 * @param req the size requirements object to use, if <code>null</code> a new 425 * one will be created 426 */ 427 protected SizeRequirements calculateMinorAxisRequirements(int axis, 428 SizeRequirements req) 429 { 430 // TODO: Maybe prepare columnRequirements. 431 SizeRequirements res = req; 432 if (res == null) 433 res = new SizeRequirements(); 434 else 435 { 436 res.alignment = 0.5f; 437 res.maximum = 0; 438 res.minimum = 0; 439 res.preferred = 0; 440 } 441 442 for (int i = 0; i < columnRequirements.length; ++i) 443 { 444 res.minimum += columnRequirements[i].minimum; 445 res.preferred += columnRequirements[i].preferred; 446 res.maximum += columnRequirements[i].maximum; 447 // TODO: Do we have to handle alignment somehow? 448 } 449 return res; 450 } 451 452 /** 453 * Returns the child view that represents the specified position in the 454 * model. This is reimplemented because in this view we do not necessarily 455 * have a one to one mapping of child elements to child views. 456 * 457 * @param pos the model position for which to query the view 458 * @param a the allocation of this view 459 * 460 * @return the view that corresponds to the specified model position or 461 * <code>null</code> if there is none 462 */ 463 protected View getViewAtPosition(int pos, Rectangle a) 464 { 465 // FIXME: Do not call super here. Instead walk through the child views 466 // and look for a range that contains the given position. 467 return super.getViewAtPosition(pos, a); 468 } 469 470 /** 471 * Updates the column requirements. 472 */ 473 private void updateColumnRequirements() 474 { 475 int rowCount = getViewCount(); 476 for (int r = 0; r < rowCount; ++r) 477 { 478 TableRow row = (TableRow) getView(r); 479 int columnCount = row.getViewCount(); 480 for (int c = 0; c < columnCount; ++c) 481 { 482 View cell = row.getView(c); 483 SizeRequirements cr = columnRequirements[c]; 484 cr.minimum = Math.max(cr.minimum, (int) cell.getMinimumSpan(X_AXIS)); 485 cr.preferred = Math.max(cr.preferred, 486 (int) cell.getPreferredSpan(X_AXIS)); 487 cr.maximum = Math.max(cr.maximum, (int) cell.getMaximumSpan(X_AXIS)); 488 } 489 } 490 } 491}