001    /* TableView.java -- A view impl for tables inside styled text
002       Copyright (C) 2006 Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package javax.swing.text;
040    
041    import java.awt.Rectangle;
042    import java.awt.Shape;
043    
044    import javax.swing.SizeRequirements;
045    import 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     */
057    public 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    }