KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > google > gwt > user > client > ui > HTMLTable


1 /*
2  * Copyright 2007 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */

16
17 package com.google.gwt.user.client.ui;
18
19 import com.google.gwt.user.client.DOM;
20 import com.google.gwt.user.client.Element;
21 import com.google.gwt.user.client.Event;
22 import com.google.gwt.user.client.ui.HasHorizontalAlignment.HorizontalAlignmentConstant;
23 import com.google.gwt.user.client.ui.HasVerticalAlignment.VerticalAlignmentConstant;
24
25 import java.util.ArrayList JavaDoc;
26 import java.util.Iterator JavaDoc;
27 import java.util.NoSuchElementException JavaDoc;
28
29 /**
30  * HTMLTable contains the common table algorithms for
31  * {@link com.google.gwt.user.client.ui.Grid} and
32  * {@link com.google.gwt.user.client.ui.FlexTable}.
33  * <p>
34  * <img class='gallery' SRC='Table.png'/>
35  * </p>
36  */

37 public abstract class HTMLTable extends Panel implements SourcesTableEvents {
38   /**
39    * This class contains methods used to format a table's cells.
40    */

41   public class CellFormatter {
42     /**
43      * Adds a style to the specified cell.
44      *
45      * @param row the cell's row
46      * @param column the cell's column
47      * @param styleName the style name to be added
48      * @see UIObject#addStyleName(String)
49      */

50     public void addStyleName(int row, int column, String JavaDoc styleName) {
51       prepareCell(row, column);
52       Element td = getCellElement(bodyElem, row, column);
53       UIObject.setStyleName(td, styleName, true);
54     }
55
56     /**
57      * Gets the TD element representing the specified cell.
58      *
59      * @param row the row of the cell to be retrieved
60      * @param column the column of the cell to be retrieved
61      * @return the column's TD element
62      * @throws IndexOutOfBoundsException
63      */

64     public Element getElement(int row, int column) {
65       checkCellBounds(row, column);
66       return getCellElement(bodyElem, row, column);
67     }
68
69     /**
70      * Gets a style from a specified row.
71      *
72      * @param row the row of the cell which the style while be added to
73      * @param column the column of the cell which the style will be added to
74      * @see UIObject#getStyleName()
75      * @return returns the style name
76      * @throws IndexOutOfBoundsException
77      */

78     public String JavaDoc getStyleName(int row, int column) {
79       return DOM.getElementProperty(getElement(row, column), "className");
80     }
81
82     /**
83      * Determines whether or not this cell is visible.
84      *
85      * @param row the row of the cell whose visibility is to be set
86      * @param column the column of the cell whose visibility is to be set
87      * @return <code>true</code> if the object is visible
88      */

89     public boolean isVisible(int row, int column) {
90       Element e = getElement(row, column);
91       return UIObject.isVisible(e);
92     }
93
94     /**
95      * Removes a style from the specified cell.
96      *
97      * @param row the cell's row
98      * @param column the cell's column
99      * @param styleName the style name to be removed
100      * @see UIObject#removeStyleName(String)
101      * @throws IndexOutOfBoundsException
102      */

103     public void removeStyleName(int row, int column, String JavaDoc styleName) {
104       checkCellBounds(row, column);
105       Element td = getCellElement(bodyElem, row, column);
106       UIObject.setStyleName(td, styleName, false);
107     }
108
109     /**
110      * Sets the horizontal and vertical alignment of the specified cell's
111      * contents.
112      *
113      * @param row the row of the cell whose alignment is to be set
114      * @param column the cell whose alignment is to be set
115      * @param hAlign the cell's new horizontal alignment as specified in
116      * {@link HasHorizontalAlignment}
117      * @param vAlign the cell's new vertical alignment as specified in
118      * {@link HasVerticalAlignment}
119      * @throws IndexOutOfBoundsException
120      */

121     public void setAlignment(int row, int column,
122         HorizontalAlignmentConstant hAlign, VerticalAlignmentConstant vAlign) {
123       setHorizontalAlignment(row, column, hAlign);
124       setVerticalAlignment(row, column, vAlign);
125     }
126
127     /**
128      * Sets the height of the specified cell.
129      *
130      * @param row the row of the cell whose height is to be set
131      * @param column the cell whose height is to be set
132      * @param height the cell's new height, in CSS units
133      * @throws IndexOutOfBoundsException
134      */

135     public void setHeight(int row, int column, String JavaDoc height) {
136       prepareCell(row, column);
137       Element elem = getCellElement(bodyElem, row, column);
138       DOM.setElementProperty(elem, "height", height);
139     }
140
141     /**
142      * Sets the horizontal alignment of the specified cell.
143      *
144      * @param row the row of the cell whose alignment is to be set
145      * @param column the cell whose alignment is to be set
146      * @param align the cell's new horizontal alignment as specified in
147      * {@link HasHorizontalAlignment}.
148      * @throws IndexOutOfBoundsException
149      */

150     public void setHorizontalAlignment(int row, int column,
151         HorizontalAlignmentConstant align) {
152       prepareCell(row, column);
153       Element elem = getCellElement(bodyElem, row, column);
154       DOM.setElementProperty(elem, "align", align.getTextAlignString());
155     }
156
157     /**
158      * Sets the style name associated with the specified cell.
159      *
160      * @param row the row of the cell whose style name is to be set
161      * @param column the column of the cell whose style name is to be set
162      * @param styleName the new style name
163      * @see UIObject#setStyleName(String)
164      * @throws IndexOutOfBoundsException
165      */

166     public void setStyleName(int row, int column, String JavaDoc styleName) {
167       prepareCell(row, column);
168       Element elem = getCellElement(bodyElem, row, column);
169       // IE uses attribute "className", FireFox uses attribute "class", so
170
// avoiding the problem by using properties instead.
171
DOM.setElementProperty(elem, "className", styleName);
172     }
173
174     /**
175      * Sets the vertical alignment of the specified cell.
176      *
177      * @param row the row of the cell whose alignment is to be set
178      * @param column the cell whose alignment is to be set
179      * @param align the cell's new vertical alignment as specified in
180      * {@link HasVerticalAlignment}.
181      * @throws IndexOutOfBoundsException
182      */

183     public void setVerticalAlignment(int row, int column,
184         VerticalAlignmentConstant align) {
185       prepareCell(row, column);
186       DOM.setStyleAttribute(getCellElement(bodyElem, row, column),
187           "verticalAlign", align.getVerticalAlignString());
188     }
189
190     /**
191      * Sets whether this cell is visible via the display style property. The
192      * other cells in the row will all shift left to fill the cell's space. So,
193      * for example a table with (0,1,2) will become (1,2) if cell 1 is hidden.
194      *
195      * @param row the row of the cell whose visibility is to be set
196      * @param column the column of the cell whose visibility is to be set
197      * @param visible <code>true</code> to show the cell, <code>false</code>
198      * to hide it
199      */

200     public void setVisible(int row, int column, boolean visible) {
201       Element e = ensureElement(row, column);
202       UIObject.setVisible(e, visible);
203     }
204
205     /**
206      * Sets the width of the specified cell.
207      *
208      * @param row the row of the cell whose width is to be set
209      * @param column the cell whose width is to be set
210      * @param width the cell's new width, in CSS units
211      * @throws IndexOutOfBoundsException
212      */

213     public void setWidth(int row, int column, String JavaDoc width) {
214       // Give the subclass a chance to prepare the cell.
215
prepareCell(row, column);
216       DOM.setElementProperty(getCellElement(bodyElem, row, column), "width",
217           width);
218     }
219
220     /**
221      * Sets whether the specified cell will allow word wrapping of its contents.
222      *
223      * @param row the row of the cell whose word-wrap is to be set
224      * @param column the cell whose word-wrap is to be set
225      * @param wrap <code>false </code> to disable word wrapping in this cell
226      * @throws IndexOutOfBoundsException
227      */

228     public void setWordWrap(int row, int column, boolean wrap) {
229       prepareCell(row, column);
230       String JavaDoc wrapValue = wrap ? "" : "nowrap";
231       DOM.setStyleAttribute(getElement(row, column), "whiteSpace", wrapValue);
232     }
233
234     /**
235      * Gets the element associated with a cell. If it does not exist and the
236      * subtype allows creation of elements, creates it.
237      *
238      * @param row the cell's row
239      * @param column the cell's column
240      * @return the cell's element
241      * @throws IndexOutOfBoundsException
242      */

243     protected Element ensureElement(int row, int column) {
244       prepareCell(row, column);
245       return getCellElement(bodyElem, row, column);
246     }
247
248     /**
249      * Convenience methods to get an attribute on a cell.
250      *
251      * @param row cell's row
252      * @param column cell's column
253      * @param attr attribute to get
254      * @return the attribute's value
255      * @throws IndexOutOfBoundsException
256      */

257     protected String JavaDoc getAttr(int row, int column, String JavaDoc attr) {
258       Element elem = getElement(row, column);
259       return DOM.getElementAttribute(elem, attr);
260     }
261
262     /**
263      * Convenience methods to set an attribute on a cell.
264      *
265      * @param row cell's row
266      * @param column cell's column
267      * @param attrName attribute to set
268      * @param value value to set
269      * @throws IndexOutOfBoundsException
270      */

271     protected void setAttr(int row, int column, String JavaDoc attrName, String JavaDoc value) {
272       Element elem = ensureElement(row, column);
273       DOM.setElementAttribute(elem, attrName, value);
274     }
275
276     /**
277      * Native method to get a cell's element.
278      *
279      * @param table the table element
280      * @param row the row of the cell
281      * @param col the column of the cell
282      * @return the element
283      */

284
285     private native Element getCellElement(Element table, int row, int col) /*-{
286       var out = table.rows[row].cells[col];
287       return (out == null ? null : out);
288      }-*/
;
289
290     /**
291      * Gets the TD element representing the specified cell unsafely (meaning
292      * that it doesn't ensure that the row and column are valid).
293      *
294      * @param row the row of the cell to be retrieved
295      * @param column the column of the cell to be retrieved
296      * @return the column's TD element
297      */

298     private Element getRawElement(int row, int column) {
299       return getCellElement(bodyElem, row, column);
300     }
301   }
302
303   /**
304    * This class contains methods used to format a table's columns. It is limited
305    * by the support cross-browser HTML support for column formatting.
306    */

307   public class ColumnFormatter {
308     protected Element columnGroup;
309
310     /**
311      * Adds a style to the specified column.
312      *
313      * @param col the col to which the style while be added
314      * @param styleName the style name to be added
315      * @see UIObject#addStyleName(String)
316      * @throws IndexOutOfBoundsException
317      */

318     public void addStyleName(int col, String JavaDoc styleName) {
319       UIObject.setStyleName(ensureColumn(col), styleName, true);
320     }
321
322     /**
323      * Gets a style from a specified column.
324      *
325      * @param column the column to which the style while be added
326      * @see UIObject#getStyleName()
327      * @throws IndexOutOfBoundsException
328      * @return the style name
329      */

330     public String JavaDoc getStyleName(int column) {
331       return DOM.getElementProperty(ensureColumn(column), "className");
332     }
333
334     /**
335      * Removes a style from the specified column.
336      *
337      * @param column the column to which the style while be removed
338      * @param styleName the style name to be removed
339      * @see UIObject#removeStyleName(String)
340      * @throws IndexOutOfBoundsException
341      */

342     public void removeStyleName(int column, String JavaDoc styleName) {
343       UIObject.setStyleName(ensureColumn(column), styleName, false);
344     }
345
346     /**
347      * Sets the style name associated with the specified column.
348      *
349      * @param column the column whose style name is to be set
350      * @param styleName the new style name
351      * @see UIObject#setStyleName(String)
352      * @throws IndexOutOfBoundsException
353      */

354     public void setStyleName(int column, String JavaDoc styleName) {
355       UIObject.resetStyleName(ensureColumn(column), styleName);
356     }
357
358     /**
359      * Sets the width of the specified column.
360      *
361      * @param column the column of the cell whose width is to be set
362      * @param width the cell's new width, in percentage or pixel units
363      * @throws IndexOutOfBoundsException
364      */

365     public void setWidth(int column, String JavaDoc width) {
366       DOM.setElementProperty(ensureColumn(column), "width", width);
367     }
368
369     private Element ensureColumn(int col) {
370       prepareColumn(col);
371
372       if (columnGroup == null) {
373         columnGroup = DOM.createElement("colgroup");
374         DOM.insertChild(getElement(), columnGroup, 0);
375       }
376
377       int num = DOM.getChildCount(columnGroup);
378       if (num <= col) {
379         Element colElement = null;
380         for (int i = num; i <= col; i++) {
381           colElement = DOM.createElement("col");
382           DOM.appendChild(columnGroup, colElement);
383         }
384         return colElement;
385       }
386       return DOM.getChild(columnGroup, col);
387     }
388   }
389
390   /**
391    * This class contains methods used to format a table's rows.
392    */

393   public class RowFormatter {
394
395     /**
396      * Adds a style to the specified row.
397      *
398      * @param row the row to which the style while be added
399      * @param styleName the style name to be added
400      * @see UIObject#addStyleName(String)
401      * @throws IndexOutOfBoundsException
402      */

403     public void addStyleName(int row, String JavaDoc styleName) {
404       UIObject.setStyleName(ensureElement(row), styleName, true);
405     }
406
407     /**
408      * Gets the TR element representing the specified row.
409      *
410      * @param row the row whose TR element is to be retrieved
411      * @return the row's TR element
412      * @throws IndexOutOfBoundsException
413      */

414     public Element getElement(int row) {
415       checkRowBounds(row);
416       return getRow(bodyElem, row);
417     }
418
419     /**
420      * Gets a style from a specified row.
421      *
422      * @param row the row to which the style while be added
423      * @see UIObject#getStyleName()
424      * @throws IndexOutOfBoundsException
425      * @return the style name
426      */

427     public String JavaDoc getStyleName(int row) {
428       return DOM.getElementProperty(getElement(row), "className");
429     }
430
431     /**
432      * Determines whether or not this row is visible via the display style
433      * attribute.
434      *
435      * @param row the row whose visibility is to be set
436      * @return <code>true</code> if the row is visible
437      */

438     public boolean isVisible(int row) {
439       Element e = getElement(row);
440       return UIObject.isVisible(e);
441     }
442
443     /**
444      * Removes a style from the specified row.
445      *
446      * @param row the row to which the style while be removed
447      * @param styleName the style name to be removed
448      * @see UIObject#removeStyleName(String)
449      * @throws IndexOutOfBoundsException
450      */

451     public void removeStyleName(int row, String JavaDoc styleName) {
452       UIObject.setStyleName(ensureElement(row), styleName, false);
453     }
454
455     /**
456      * Sets the style name associated with the specified row.
457      *
458      * @param row the row whose style name is to be set
459      * @param styleName the new style name
460      * @see UIObject#setStyleName(String)
461      * @throws IndexOutOfBoundsException
462      */

463     public void setStyleName(int row, String JavaDoc styleName) {
464       UIObject.resetStyleName(ensureElement(row), styleName);
465     }
466
467     /**
468      * Sets the vertical alignment of the specified row.
469      *
470      * @param row the row whose alignment is to be set
471      * @param align the row's new vertical alignment as specified in
472      * {@link HasVerticalAlignment}
473      * @throws IndexOutOfBoundsException
474      */

475     public void setVerticalAlign(int row, VerticalAlignmentConstant align) {
476       DOM.setStyleAttribute(ensureElement(row), "verticalAlign",
477           align.getVerticalAlignString());
478     }
479
480     /**
481      * Sets whether this row is visible.
482      *
483      * @param row the row whose visibility is to be set
484      * @param visible <code>true</code> to show the row, <code>false</code>
485      * to hide it
486      */

487     public void setVisible(int row, boolean visible) {
488       Element e = ensureElement(row);
489       UIObject.setVisible(e, visible);
490     }
491
492     /**
493      * Ensure the TR element representing the specified row exists for
494      * subclasses that allow dynamic addition of elements.
495      *
496      * @param row the row whose TR element is to be retrieved
497      * @return the row's TR element
498      * @throws IndexOutOfBoundsException
499      */

500     protected Element ensureElement(int row) {
501       prepareRow(row);
502       return getRow(bodyElem, row);
503     }
504
505     protected native Element getRow(Element elem, int row)/*-{
506       return elem.rows[row];
507     }-*/
;
508
509     /**
510      * Convenience methods to set an attribute on a row.
511      *
512      * @param row cell's row
513      * @param attrName attribute to set
514      * @param value value to set
515      * @throws IndexOutOfBoundsException
516      */

517     protected void setAttr(int row, String JavaDoc attrName, String JavaDoc value) {
518       Element elem = ensureElement(row);
519       DOM.setElementAttribute(elem, attrName, value);
520     }
521   }
522
523   /**
524    * Creates a mapping from elements to their associated widgets.
525    */

526   private static class WidgetMapper {
527
528     private static class FreeNode {
529       int index;
530       FreeNode next;
531       public FreeNode(int index, FreeNode next) {
532         this.index = index;
533         this.next = next;
534       }
535     }
536
537     private static native void clearWidgetIndex(Element elem) /*-{
538       elem["__widgetID"] = null;
539     }-*/
;
540
541     private static native int getWidgetIndex(Element elem) /*-{
542       var index = elem["__widgetID"];
543       return (index == null) ? -1 : index;
544     }-*/
;
545
546     private static native void setWidgetIndex(Element elem, int index) /*-{
547       elem["__widgetID"] = index;
548     }-*/
;
549
550     private FreeNode freeList = null;
551
552     private ArrayList JavaDoc widgetList = new ArrayList JavaDoc();
553
554     /**
555      * Returns the widget associated with the given element.
556      *
557      * @param elem widget's element
558      * @return the widget
559      */

560     public Widget getWidget(Element elem) {
561       int index = getWidgetIndex(elem);
562       if (index < 0) {
563         return null;
564       }
565       return (Widget) widgetList.get(index);
566     }
567
568     /**
569      * Adds the Widget.
570      *
571      * @param widget widget to add
572      */

573     public void putWidget(Widget widget) {
574       int index;
575       if (freeList == null) {
576         index = widgetList.size();
577         widgetList.add(widget);
578       } else {
579         index = freeList.index;
580         widgetList.set(index, widget);
581         freeList = freeList.next;
582       }
583       setWidgetIndex(widget.getElement(), index);
584     }
585
586     /**
587      * Remove the widget associated with the given element.
588      *
589      * @param elem the widget's element
590      */

591     public void removeWidgetByElement(Element elem) {
592       int index = getWidgetIndex(elem);
593       removeImpl(elem, index);
594     }
595
596     /**
597      * Creates an iterator of widgets.
598      *
599      * @return the iterator
600      */

601     public Iterator JavaDoc widgetIterator() {
602       // TODO: look at using the WidgetIterators class!
603
return new Iterator JavaDoc() {
604         int lastIndex = -1;
605         int nextIndex = -1;
606         {
607           findNext();
608         }
609
610         public boolean hasNext() {
611           return nextIndex < widgetList.size();
612         }
613
614         public Object JavaDoc next() {
615           if (!hasNext()) {
616             throw new NoSuchElementException JavaDoc();
617           }
618           Object JavaDoc result = widgetList.get(nextIndex);
619           lastIndex = nextIndex;
620           findNext();
621           return result;
622         }
623
624         public void remove() {
625           if (lastIndex < 0) {
626             throw new IllegalStateException JavaDoc();
627           }
628           Widget w = (Widget) widgetList.get(lastIndex);
629           removeImpl(w.getElement(), lastIndex);
630           lastIndex = -1;
631         }
632
633         private void findNext() {
634           while (++nextIndex < widgetList.size()) {
635             if (widgetList.get(nextIndex) != null) {
636               return;
637             }
638           }
639         }
640       };
641     }
642
643     private void removeImpl(Element elem, int index) {
644       clearWidgetIndex(elem);
645       widgetList.set(index, null);
646       freeList = new FreeNode(index, freeList);
647     }
648   }
649
650   /**
651    * Table's body.
652    */

653   private final Element bodyElem;
654
655   /**
656    * Current cell formatter.
657    */

658   private CellFormatter cellFormatter;
659
660   /**
661    * Column Formatter.
662    */

663   private ColumnFormatter columnFormatter;
664
665   /**
666    * Current row formatter.
667    */

668   private RowFormatter rowFormatter;
669
670   /**
671    * Table element.
672    */

673   private final Element tableElem;
674
675   /**
676    * Current table listener.
677    */

678   private TableListenerCollection tableListeners;
679
680   private WidgetMapper widgetMap = new WidgetMapper();
681
682   /**
683    * Create a new empty HTML Table.
684    */

685   public HTMLTable() {
686     tableElem = DOM.createTable();
687     bodyElem = DOM.createTBody();
688     DOM.appendChild(tableElem, bodyElem);
689     setElement(tableElem);
690     sinkEvents(Event.ONCLICK);
691   }
692
693   /**
694    * Adds a listener to the current table.
695    *
696    * @param listener listener to add
697    */

698   public void addTableListener(TableListener listener) {
699     if (tableListeners == null) {
700       tableListeners = new TableListenerCollection();
701     }
702     tableListeners.add(listener);
703   }
704
705   /**
706    * Removes all widgets from this table, but does not remove other HTML or text
707    * contents of cells.
708    */

709   public void clear() {
710     for (int row = 0; row < getRowCount(); ++row) {
711       for (int col = 0; col < getCellCount(row); ++col) {
712         Widget child = getWidgetImpl(row, col);
713         if (child != null) {
714           remove(child);
715         }
716       }
717     }
718   }
719
720   /**
721    * Clears the given row and column. If it contains a Widget, it will be
722    * removed from the table. If not, its contents will simply be cleared.
723    *
724    * @param row the widget's column
725    * @param column the widget's column
726    * @return true if a widget was removed
727    * @throws IndexOutOfBoundsException
728    */

729   public boolean clearCell(int row, int column) {
730     Element td = getCellFormatter().getElement(row, column);
731     return internalClearCell(td, true);
732   }
733
734   /**
735    * Gets the number of cells in a given row.
736    *
737    * @param row the row whose cells are to be counted
738    * @return the number of cells present in the row
739    */

740   public abstract int getCellCount(int row);
741
742   /**
743    * Gets the {@link CellFormatter} associated with this table. Use casting to
744    * get subclass-specific functionality
745    *
746    * @return this table's cell formatter
747    */

748   public CellFormatter getCellFormatter() {
749     return cellFormatter;
750   }
751
752   /**
753    * Gets the amount of padding that is added around all cells.
754    *
755    * @return the cell padding, in pixels
756    */

757   public int getCellPadding() {
758     return DOM.getElementPropertyInt(tableElem, "cellPadding");
759   }
760
761   /**
762    * Gets the amount of spacing that is added around all cells.
763    *
764    * @return the cell spacing, in pixels
765    */

766   public int getCellSpacing() {
767     return DOM.getElementPropertyInt(tableElem, "cellSpacing");
768   }
769
770   /**
771    * Gets the column formatter.
772    *
773    * @return the column formatter
774    */

775   public ColumnFormatter getColumnFormatter() {
776     return columnFormatter;
777   }
778
779   /**
780    * Gets the HTML contents of the specified cell.
781    *
782    * @param row the cell's row
783    * @param column the cell's column
784    * @return the cell's HTML contents
785    * @throws IndexOutOfBoundsException
786    */

787   public String JavaDoc getHTML(int row, int column) {
788     return DOM.getInnerHTML(cellFormatter.getElement(row, column));
789   }
790
791   /**
792    * Gets the number of rows present in this table.
793    *
794    * @return the table's row count
795    */

796   public abstract int getRowCount();
797
798   /**
799    * Gets the RowFormatter associated with this table.
800    *
801    * @return the table's row formatter
802    */

803   public RowFormatter getRowFormatter() {
804     return rowFormatter;
805   }
806
807   /**
808    * Gets the text within the specified cell.
809    *
810    * @param row the cell's row
811    * @param column the cell's column
812    * @return the cell's text contents
813    * @throws IndexOutOfBoundsException
814    */

815   public String JavaDoc getText(int row, int column) {
816     checkCellBounds(row, column);
817     Element e = cellFormatter.getElement(row, column);
818     return DOM.getInnerText(e);
819   }
820
821   /**
822    * Gets the widget in the specified cell.
823    *
824    * @param row the cell's row
825    * @param column the cell's column
826    * @return the widget in the specified cell, or <code>null</code> if none is
827    * present
828    * @throws IndexOutOfBoundsException
829    */

830   public Widget getWidget(int row, int column) {
831     checkCellBounds(row, column);
832     return getWidgetImpl(row, column);
833   }
834
835   /**
836    * Determines whether the specified cell exists.
837    *
838    * @param row the cell's row
839    * @param column the cell's column
840    * @return <code>true</code> if the specified cell exists
841    */

842   public boolean isCellPresent(int row, int column) {
843     if ((row >= getRowCount()) && (row < 0)) {
844       return false;
845     }
846     if ((column < 0) || (column >= getCellCount(row))) {
847       return false;
848     } else {
849       return true;
850     }
851   }
852
853   /**
854    * Returns an iterator containing all the widgets in this table.
855    *
856    * @return the iterator
857    */

858   public Iterator JavaDoc iterator() {
859     return widgetMap.widgetIterator();
860   }
861
862   /**
863    * Method to process events generated from the browser.
864    *
865    * @param event the generated event
866    */

867   public void onBrowserEvent(Event event) {
868     switch (DOM.eventGetType(event)) {
869       case Event.ONCLICK: {
870         if (tableListeners != null) {
871           // Find out which cell was actually clicked.
872
Element td = getEventTargetCell(event);
873           if (td == null) {
874             return;
875           }
876           Element tr = DOM.getParent(td);
877           Element body = DOM.getParent(tr);
878           int row = DOM.getChildIndex(body, tr);
879           int column = DOM.getChildIndex(tr, td);
880           // Fire the event.
881
tableListeners.fireCellClicked(this, row, column);
882         }
883         break;
884       }
885       default: {
886         // Do nothing
887
}
888     }
889   }
890
891   /**
892    * Remove the specified widget from the table.
893    *
894    * @param widget widget to remove
895    * @return was the widget removed from the table.
896    */

897   public boolean remove(Widget widget) {
898     // Make sure the Widget is actually contained in this table.
899
if (widget.getParent() != this) {
900       return false;
901     }
902     widgetMap.removeWidgetByElement(widget.getElement());
903     disown(widget);
904     return true;
905   }
906
907   /**
908    * Removes the specified table listener.
909    *
910    * @param listener listener to remove
911    */

912   public void removeTableListener(TableListener listener) {
913     if (tableListeners != null) {
914       tableListeners.remove(listener);
915     }
916   }
917
918   /**
919    * Sets the width of the table's border. This border is displayed around all
920    * cells in the table.
921    *
922    * @param width the width of the border, in pixels
923    */

924   public void setBorderWidth(int width) {
925     DOM.setElementProperty(tableElem, "border", "" + width);
926   }
927
928   /**
929    * Sets the amount of padding to be added around all cells.
930    *
931    * @param padding the cell padding, in pixels
932    */

933   public void setCellPadding(int padding) {
934     DOM.setElementPropertyInt(tableElem, "cellPadding", padding);
935   }
936
937   /**
938    * Sets the amount of spacing to be added around all cells.
939    *
940    * @param spacing the cell spacing, in pixels
941    */

942   public void setCellSpacing(int spacing) {
943     DOM.setElementPropertyInt(tableElem, "cellSpacing", spacing);
944   }
945
946   /**
947    * Sets the HTML contents of the specified cell.
948    *
949    * @param row the cell's row
950    * @param column the cell's column
951    * @param html the cell's HTML contents
952    * @throws IndexOutOfBoundsException
953    */

954   public void setHTML(int row, int column, String JavaDoc html) {
955     prepareCell(row, column);
956     Element td = cleanCell(row, column, html == null);
957     if (html != null) {
958       DOM.setInnerHTML(td, html);
959     }
960   }
961
962   /**
963    * Sets the text within the specified cell.
964    *
965    * @param row the cell's row
966    * @param column cell's column
967    * @param text the cell's text contents
968    * @throws IndexOutOfBoundsException
969    */

970   public void setText(int row, int column, String JavaDoc text) {
971     prepareCell(row, column);
972     Element td;
973     td = cleanCell(row, column, text == null);
974     if (text != null) {
975       DOM.setInnerText(td, text);
976     }
977   }
978
979   /**
980    * Sets the widget within the specified cell.
981    * <p>
982    * Inherited implementations may either throw IndexOutOfBounds exception if
983    * the cell does not exist, or allocate a new cell to store the content.
984    * </p>
985    * <p>
986    * FlexTable will automatically allocate the cell at the correct location and
987    * then set the widget. Grid will set the widget if and only if the cell is
988    * within the Grid's bounding box.
989    * </p>
990    *
991    * @param widget The widget to be added
992    * @param row the cell's row
993    * @param column the cell's column
994    * @throws IndexOutOfBoundsException
995    */

996   public void setWidget(int row, int column, Widget widget) {
997     prepareCell(row, column);
998     if (widget != null) {
999       // Call this early to ensure that the table doesn't end up partially
1000
// constructed when an exception is thrown from adopt().
1001
widget.removeFromParent();
1002
1003      // Attach it to the cell's TD.
1004
Element td = cleanCell(row, column, true);
1005
1006      // Add the widget to the map.
1007
widgetMap.putWidget(widget);
1008
1009      // Set the widget's parent.
1010
adopt(widget, td);
1011    }
1012  }
1013
1014  /**
1015   * Bounds checks that the cell exists at the specified location.
1016   *
1017   * @param row cell's row
1018   * @param column cell's column
1019   * @throws IndexOutOfBoundsException
1020   */

1021  protected void checkCellBounds(int row, int column) {
1022    checkRowBounds(row);
1023    if (column < 0) {
1024      throw new IndexOutOfBoundsException JavaDoc("Column " + column
1025          + " must be non-negative: " + column);
1026    }
1027    int cellSize = getCellCount(row);
1028    if (cellSize <= column) {
1029      throw new IndexOutOfBoundsException JavaDoc("Column index: " + column
1030          + ", Column size: " + getCellCount(row));
1031    }
1032  }
1033
1034  /**
1035   * Checks that the row is within the correct bounds.
1036   *
1037   * @param row row index to check
1038   * @throws IndexOutOfBoundsException
1039   */

1040  protected void checkRowBounds(int row) {
1041    int rowSize = getRowCount();
1042    if ((row >= rowSize) || (row < 0)) {
1043      throw new IndexOutOfBoundsException JavaDoc("Row index: " + row + ", Row size: "
1044          + rowSize);
1045    }
1046  }
1047
1048  /**
1049   * Creates a new cell. Override this method if the cell should have initial
1050   * contents.
1051   *
1052   * @return the newly created TD
1053   */

1054  protected Element createCell() {
1055    return DOM.createTD();
1056  }
1057
1058  /**
1059   * Gets the table's TBODY element.
1060   *
1061   * @return the TBODY element
1062   */

1063  protected Element getBodyElement() {
1064    return bodyElem;
1065  }
1066
1067  /**
1068   * Directly ask the underlying DOM what the cell count on the given row is.
1069   *
1070   * @param tableBody the element
1071   * @param row the row
1072   * @return number of columns in the row
1073   */

1074  protected native int getDOMCellCount(Element tableBody, int row) /*-{
1075    return tableBody.rows[row].cells.length;
1076  }-*/
;
1077
1078  /**
1079   * Directly ask the underlying DOM what the cell count on the given row is.
1080   *
1081   * @param row the row
1082   * @return number of columns in the row
1083   */

1084  protected int getDOMCellCount(int row) {
1085    return getDOMCellCount(bodyElem, row);
1086  }
1087
1088  /**
1089   * Directly ask the underlying DOM what the row count is.
1090   *
1091   * @return Returns the number of rows in the table
1092   */

1093  protected int getDOMRowCount() {
1094    return getDOMRowCount(bodyElem);
1095  }
1096
1097  protected native int getDOMRowCount(Element elem) /*-{
1098    return elem.rows.length;
1099  }-*/
;
1100
1101  /**
1102   * Determines the TD associated with the specified event.
1103   *
1104   * @param event the event to be queried
1105   * @return the TD associated with the event, or <code>null</code> if none is
1106   * found.
1107   */

1108  protected Element getEventTargetCell(Event event) {
1109    Element td = DOM.eventGetTarget(event);
1110    for (; td != null; td = DOM.getParent(td)) {
1111      // If it's a TD, it might be the one we're looking for.
1112
if (DOM.getElementProperty(td, "tagName").equalsIgnoreCase("td")) {
1113        // Make sure it's directly a part of this table before returning
1114
// it.
1115
Element tr = DOM.getParent(td);
1116        Element body = DOM.getParent(tr);
1117        if (DOM.compare(body, bodyElem)) {
1118          return td;
1119        }
1120      }
1121      // If we run into this table's body, we're out of options.
1122
if (DOM.compare(td, bodyElem)) {
1123        return null;
1124      }
1125    }
1126    return null;
1127  }
1128
1129  /**
1130   * Inserts a new cell into the specified row.
1131   *
1132   * @param row the row into which the new cell will be inserted
1133   * @param column the column before which the cell will be inserted
1134   * @throws IndexOutOfBoundsException
1135   */

1136  protected void insertCell(int row, int column) {
1137    Element tr = rowFormatter.getRow(bodyElem, row);
1138    Element td = createCell();
1139    DOM.insertChild(tr, td, column);
1140  }
1141
1142  /**
1143   * Inserts a number of cells before the specified cell.
1144   *
1145   * @param row the row into which the new cells will be inserted
1146   * @param column the column before which the new cells will be inserted
1147   * @param count number of cells to be inserted
1148   * @throws IndexOutOfBoundsException
1149   */

1150  protected void insertCells(int row, int column, int count) {
1151    Element tr = rowFormatter.getRow(bodyElem, row);
1152    for (int i = column; i < column + count; i++) {
1153      Element td = createCell();
1154      DOM.insertChild(tr, td, i);
1155    }
1156  }
1157
1158  /**
1159   * Inserts a new row into the table.
1160   *
1161   * @param beforeRow the index before which the new row will be inserted
1162   * @return the index of the newly-created row
1163   * @throws IndexOutOfBoundsException
1164   */

1165  protected int insertRow(int beforeRow) {
1166    // Specifically allow the row count as an insert position.
1167
if (beforeRow != getRowCount()) {
1168      checkRowBounds(beforeRow);
1169    }
1170    Element tr = DOM.createTR();
1171    DOM.insertChild(bodyElem, tr, beforeRow);
1172    return beforeRow;
1173  }
1174
1175  /**
1176   * Does actual clearing, used by clearCell and cleanCell. All HTMLTable
1177   * methods should use internalClearCell rather than clearCell, as clearCell
1178   * may be overridden in subclasses to format an empty cell.
1179   *
1180   * @param td element to clear
1181   * @param clearInnerHTML should the cell's inner html be cleared?
1182   * @return returns whether a widget was cleared
1183   */

1184  protected boolean internalClearCell(Element td, boolean clearInnerHTML) {
1185    Element maybeChild = DOM.getFirstChild(td);
1186    Widget widget = null;
1187    if (maybeChild != null) {
1188      widget = widgetMap.getWidget(maybeChild);
1189    }
1190    if (widget != null) {
1191      // If there is a widget, remove it.
1192
remove(widget);
1193      return true;
1194    } else {
1195      // Otherwise, simply clear whatever text and/or HTML may be there.
1196
if (clearInnerHTML) {
1197        DOM.setInnerHTML(td, "");
1198      }
1199      return false;
1200    }
1201  }
1202
1203  /**
1204   * Subclasses must implement this method. It allows them to decide what to do
1205   * just before a cell is accessed. If the cell already exists, this method
1206   * must do nothing. Otherwise, a subclass must either ensure that the cell
1207   * exists or throw an {@link IndexOutOfBoundsException}.
1208   *
1209   * @param row the cell's row
1210   * @param column the cell's column
1211   */

1212  protected abstract void prepareCell(int row, int column);
1213
1214  /**
1215   * Subclasses can implement this method. It allows them to decide what to do
1216   * just before a column is accessed. For classes, such as
1217   * <code>FlexTable</code>, that do not have a concept of a global column
1218   * length can ignore this method.
1219   *
1220   * @param column the cell's column
1221   * @throws IndexOutOfBoundsException
1222   */

1223  protected void prepareColumn(int column) {
1224    // By default, do nothing.
1225
}
1226
1227  /**
1228   * Subclasses must implemea whaccessed. If the row already exists, this method
1229   * must do nothing. Otherwise, a subclass must either ensure that the row
1230   * exists or throw an {@link IndexOutOfBoundsException}.
1231   *
1232   * @param row the cell's row
1233   */

1234  protected abstract void prepareRow(int row);
1235
1236  /**
1237   * Removes the specified cell from the table.
1238   *
1239   * @param row the row of the cell to remove
1240   * @param column the column of cell to remove
1241   * @throws IndexOutOfBoundsException
1242   */

1243  protected void removeCell(int row, int column) {
1244    checkCellBounds(row, column);
1245    Element td = cleanCell(row, column, false);
1246    Element tr = rowFormatter.getRow(bodyElem, row);
1247    DOM.removeChild(tr, td);
1248  }
1249
1250  /**
1251   * Removes the specified row from the table.
1252   *
1253   * @param row the index of the row to be removed
1254   * @throws IndexOutOfBoundsException
1255   */

1256  protected void removeRow(int row) {
1257    int columnCount = getCellCount(row);
1258    for (int column = 0; column < columnCount; ++column) {
1259      cleanCell(row, column, false);
1260    }
1261    DOM.removeChild(bodyElem, rowFormatter.getRow(bodyElem, row));
1262  }
1263
1264  /**
1265   * Sets the table's CellFormatter.
1266   *
1267   * @param cellFormatter the table's cell formatter
1268   */

1269  protected void setCellFormatter(CellFormatter cellFormatter) {
1270    this.cellFormatter = cellFormatter;
1271  }
1272
1273  protected void setColumnFormatter(ColumnFormatter formatter) {
1274    columnFormatter = formatter;
1275  }
1276
1277  /**
1278   * Sets the table's RowFormatter.
1279   *
1280   * @param rowFormatter the table's row formatter
1281   */

1282  protected void setRowFormatter(RowFormatter rowFormatter) {
1283    this.rowFormatter = rowFormatter;
1284  }
1285
1286  /**
1287   * Removes any widgets, text, and HTML within the cell. This method assumes
1288   * that the requested cell already exists.
1289   *
1290   * @param row the cell's row
1291   * @param column the cell's column
1292   * @param clearInnerHTML should the cell's inner html be cleared?
1293   * @return element that has been cleaned
1294   */

1295  private Element cleanCell(int row, int column, boolean clearInnerHTML) {
1296    // Clear whatever is in the cell.
1297
Element td = getCellFormatter().getRawElement(row, column);
1298    internalClearCell(td, clearInnerHTML);
1299    return td;
1300  }
1301
1302  /**
1303   * Gets the Widget associated with the given cell.
1304   *
1305   * @param row the cell's row
1306   * @param column the cell's column
1307   * @return the widget
1308   */

1309  private Widget getWidgetImpl(int row, int column) {
1310    Element e = cellFormatter.getRawElement(row, column);
1311    Element child = DOM.getFirstChild(e);
1312    if (child == null) {
1313      return null;
1314    } else {
1315      return widgetMap.getWidget(child);
1316    }
1317  }
1318}
1319
Popular Tags