1 /*
2 * Angkor Web Framework
3 *
4 * Distributable under LGPL license.
5 * See terms of license at gnu.org.
6 */
7 package com.tirsen.angkor.table;
8
9 import com.tirsen.angkor.Debug;
10 import com.tirsen.angkor.RenderContext;
11 import com.tirsen.angkor.View;
12 import com.tirsen.angkor.event.ChangeEvent;
13 import com.tirsen.angkor.event.ChangeListener;
14 import com.tirsen.angkor.widget.Container;
15 import com.tirsen.angkor.widget.ValueModel;
16 import org.apache.log4j.Category;
17
18 import java.io.IOException;
19 import java.util.ArrayList;
20 import java.util.Collection;
21 import java.util.Iterator;
22 import java.util.List;
23
24 /***
25 * The table should support the following features:
26 * Implemented ones:
27 * <li> Fetch values from the model.
28 * <li> A default cell-view-factory.
29 * <li> Cell-view-factories for each column overriding the default one.
30 * <li> Cell-view-factories for rows not getting values from the model.
31 * (for example a column for executing actions on the object on the row or for marking rows to remove)
32 * TODOs:
33 * <li> Row-view-factories for rows not getting values from the model.
34 * (for example a last line for adding new objects)
35 * <li> Inserting view-only view-factories before and after columns and rows.
36 *
37 * The implicit model and view-only row-view-factories and column-view-factories will probably
38 * be refactored into a TableColumn-class instead. The TableColumn-class has an index into the model
39 * if it is from the model and a view-factory for the cells. This way columns can be arranged in any order
40 * and still maintain a simple link to the model. This does not support the view-only rows so oftenly
41 * used on the web for creating a new row in the model, ie. a view-only row is not supported
42 * (in contrast to the view-only columns which are supported).
43 *
44 * @author $Author: tirsen $
45 * @version $Revision: 1.5 $
46 * <BR>
47 * $Id: Table.java,v 1.5 2002/10/09 21:37:37 tirsen Exp $
48 */
49 public class Table extends Container
50 {
51 private static final Category logger = Debug.getCategory();
52
53 private TableModel model;
54 private TableCellViewFactory viewFactory;
55 private boolean readOnly = true;
56
57 /***
58 * The created views are cached so that errors and other view-state can be displayed.
59 */
60 private List views;
61
62 /***
63 * A list of <code>TableColumn</code>s.
64 */
65 private List tableColumns;
66
67 public Table()
68 {
69 this(true);
70 }
71
72 public Table(boolean readOnly)
73 {
74 this.readOnly = readOnly;
75 }
76
77 private ChangeListener resetCreatedViewsListener = new ChangeListener()
78 {
79 public void stateChanged(ChangeEvent evt)
80 {
81 System.out.println("resetting");
82 resetCreatedViews();
83 }
84 };
85
86 public void setModel(TableModel model)
87 {
88 if (this.model != null) this.model.removeChangeListener(resetCreatedViewsListener);
89 this.model = model;
90 if (this.model != null) this.model.addChangeListener(resetCreatedViewsListener);
91 resetCreatedViews();
92 createTableColumns();
93 }
94
95 public List getRowViews(int row)
96 {
97 maybeCreateCellViews();
98 List cells = (List) views.get(row - getStartRow());
99
100 return cells;
101 }
102
103 public void setReadOnly(boolean readOnly)
104 {
105 this.readOnly = readOnly;
106 }
107
108 private void maybeCreateCellViews()
109 {
110 if (views == null)
111 {
112 logger.debug("recreating table cell views");
113 createCellViews();
114 }
115 }
116
117 private void createCellViews()
118 {
119 int capacity = getEndRow() - getStartRow() + 1;
120 views = new ArrayList(capacity);
121 int end = getEndRow();
122 for (int row = getStartRow(); row <= end; row++)
123 {
124 int columns = getColumnCount();
125 Collection cells = new ArrayList(columns);
126
127 for (int columnIndex = 0; columnIndex < columns; columnIndex++)
128 {
129 TableColumn column = getTableColumn(columnIndex);
130
131 if (column.isVisible())
132 {
133 View view = column.getCellView(this, row, columnIndex);
134 view.setContainer(this);
135 cells.add(view);
136 }
137 }
138 views.add(cells);
139 }
140 }
141
142 public View getCellView(int row, int column)
143 {
144 return (View) getRowViews(row).get(column);
145 }
146
147 private TableColumn getTableColumn(int column)
148 {
149 return (TableColumn) tableColumns.get(column);
150 }
151
152 public ValueModel getCellModel(int row, int column)
153 {
154 TableColumn tableColumn = getTableColumn(column);
155 return tableColumn.getCellModel(row);
156 }
157
158 /***
159 * Call this method when we have a suspicion that cell-views will need to be recreated.
160 */
161 public void resetCreatedViews()
162 {
163 views = null;
164 }
165
166 public List getChildren()
167 {
168 List all = super.getChildren();
169 for (Iterator iterator = views.iterator(); iterator.hasNext();)
170 {
171 List cells = (List) iterator.next();
172 all.addAll(cells);
173 }
174 return all;
175 }
176
177 private TableCellViewFactory createDefaultTableCellViewFactory()
178 {
179 if (readOnly)
180 return new ReadOnlyCellViewFactory();
181 else
182 return new DefaultTableCellViewFactory();
183 }
184
185 public TableCellViewFactory getViewFactory()
186 {
187 if (viewFactory == null) viewFactory = createDefaultTableCellViewFactory();
188 return viewFactory;
189 }
190
191 /***
192 * Creates all TableColumns based upon the TableModel.
193 * These can be modified <em>after</em> setting the model.
194 * When the model is set the table-columns are recreated.
195 */
196 private void createTableColumns()
197 {
198 int columns = getTableModel().getColumnCount();
199 tableColumns = new ArrayList(columns);
200 for (int column = 0; column < columns; column++)
201 {
202 TableColumn tableColumn = new TableColumn(getTableModel(), column);
203 tableColumn.setViewFactory(getViewFactory());
204 addTableColumn(tableColumn);
205 }
206 }
207
208 public void setTableCellViewFactory(TableCellViewFactory viewFactory)
209 {
210 this.viewFactory = viewFactory;
211 }
212
213 public int getRowCount()
214 {
215 return getTableModel().getRowCount();
216 }
217
218 /***
219 * Returns the number of columns table.
220 * Note that this is necessarily not the same number of columns as in the model.
221 */
222 public int getColumnCount()
223 {
224 return tableColumns == null ? 0 : tableColumns.size();
225 }
226
227 public TableModel getTableModel()
228 {
229 if (model == null) model = createDefaultTableModel();
230 return model;
231 }
232
233 protected TableModel createDefaultTableModel()
234 {
235 return new DefaultTableModel();
236 }
237
238 public String getColumnName(int column)
239 {
240 return getTableModel().getColumnName(column);
241 }
242
243 /***
244 * If any number is set to a negative number it is subtracted from the number of rows.
245 * As a special case of this setting end to -1 sets the range to end at the last row.
246 * If end is lower or equal to start sets the range to show all.
247 */
248 public void setRange(int start, int end)
249 {
250 resetCreatedViews();
251 if (end <= start)
252 {
253 start = 0;
254 end = -1;
255 }
256 model.setRange(start, end);
257 }
258
259 /***
260 * Insert a new column at the specified index.
261 */
262 public void addTableColumn(int index, TableColumn column)
263 {
264 resetCreatedViews();
265 tableColumns.add(index, column);
266 column.addChangeListener(resetCreatedViewsListener);
267 }
268
269 /***
270 * Add a new column at the end.
271 */
272 public void addTableColumn(TableColumn column)
273 {
274 if (tableColumns == null) tableColumns = new ArrayList();
275 resetCreatedViews();
276 tableColumns.add(column);
277 column.addChangeListener(resetCreatedViewsListener);
278 }
279
280 public void setColumnCellViewFactory(int column, TableCellViewFactory columnFactory)
281 {
282 if (tableColumns == null) tableColumns = new ArrayList();
283 resetCreatedViews();
284 TableColumn tableColumn = getTableColumn(column);
285 tableColumn.setViewFactory(columnFactory);
286 }
287
288 public int getStartRow()
289 {
290 return model.getStart();
291 }
292
293 public int getEndRow()
294 {
295 return model.getEnd();
296 }
297
298 private int scrollStart = 0;
299 private int scrollEnd = 0;
300 private int scrollNumberOfRows = 0;
301
302 public void scrollToStart()
303 {
304 scrollStart = 0;
305 scrollEnd = scrollNumberOfRows - 1;
306 setRange(scrollStart, scrollEnd);
307 }
308
309 public void scrollForward()
310 {
311 scrollStart += scrollNumberOfRows;
312 scrollEnd += scrollNumberOfRows;
313 setRange(scrollStart, scrollEnd);
314 }
315
316 public void scrollBackward()
317 {
318 scrollStart -= scrollNumberOfRows;
319 scrollEnd -= scrollNumberOfRows;
320 setRange(scrollStart, scrollEnd);
321 }
322
323 /***
324 * Sets the maximum number of rows for scrolling. If set to 0 disables scrolling (ie. shows all).
325 * A call to this method automatically scrolls to the start of the table.
326 */
327 public void setNumberOfScrollRows(int numberOfRows)
328 {
329 this.scrollNumberOfRows = numberOfRows;
330 scrollToStart();
331 }
332
333 public void render(RenderContext context) throws IOException
334 {
335 context.startTag("table id=\"" + uniqueId(context) + "\" border=\"" + (isDebugTables() ? "1" : "0") + "\"");
336
337 //int modelColumns = getTableModel().getColumnCount();
338
339 context.startTag("tr");
340 for (int columnIndex = 0; columnIndex < getColumnCount(); columnIndex++)
341 {
342 TableColumn column = getTableColumn(columnIndex);
343 if (column.isVisible())
344 {
345 String name = column.getColumnName();
346 context.startTag("th");
347 if (name != null)
348 context.println(name);
349 else
350 context.println(" ");
351 context.endTag("th");
352 }
353 }
354 context.endTag("tr");
355
356 int endRow = getEndRow();
357 for (int row = getStartRow(); row <= endRow; row++)
358 {
359 context.startTag("tr");
360 Iterator iterator = getRowViews(row).iterator();
361 while (iterator.hasNext())
362 {
363 View cell = (View) iterator.next();
364 context.startTag("td");
365 cell.render(context);
366 context.endTag("td");
367 }
368 context.endTag("tr");
369 }
370 context.endTag("table");
371 }
372 }
This page was automatically generated by Maven