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 &lt;= 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}