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
|
2
|
public Table()
|
68
|
|
{
|
69
|
2
|
this(true);
|
70
|
|
}
|
71
|
|
|
72
|
2
|
public Table(boolean readOnly)
|
73
|
|
{
|
74
|
2
|
this.readOnly = readOnly;
|
75
|
|
}
|
76
|
|
|
77
|
|
private ChangeListener resetCreatedViewsListener = new ChangeListener()
|
78
|
|
{
|
79
|
29
|
public void stateChanged(ChangeEvent evt)
|
80
|
|
{
|
81
|
29
|
System.out.println("resetting");
|
82
|
29
|
resetCreatedViews();
|
83
|
|
}
|
84
|
|
};
|
85
|
|
|
86
|
2
|
public void setModel(TableModel model)
|
87
|
|
{
|
88
|
1
|
if (this.model != null) this.model.removeChangeListener(resetCreatedViewsListener);
|
89
|
2
|
this.model = model;
|
90
|
2
|
if (this.model != null) this.model.addChangeListener(resetCreatedViewsListener);
|
91
|
2
|
resetCreatedViews();
|
92
|
2
|
createTableColumns();
|
93
|
|
}
|
94
|
|
|
95
|
5
|
public List getRowViews(int row)
|
96
|
|
{
|
97
|
5
|
maybeCreateCellViews();
|
98
|
5
|
List cells = (List) views.get(row - getStartRow());
|
99
|
|
|
100
|
5
|
return cells;
|
101
|
|
}
|
102
|
|
|
103
|
2
|
public void setReadOnly(boolean readOnly)
|
104
|
|
{
|
105
|
2
|
this.readOnly = readOnly;
|
106
|
|
}
|
107
|
|
|
108
|
5
|
private void maybeCreateCellViews()
|
109
|
|
{
|
110
|
5
|
if (views == null)
|
111
|
|
{
|
112
|
4
|
logger.debug("recreating table cell views");
|
113
|
4
|
createCellViews();
|
114
|
|
}
|
115
|
|
}
|
116
|
|
|
117
|
4
|
private void createCellViews()
|
118
|
|
{
|
119
|
4
|
int capacity = getEndRow() - getStartRow() + 1;
|
120
|
4
|
views = new ArrayList(capacity);
|
121
|
4
|
int end = getEndRow();
|
122
|
4
|
for (int row = getStartRow(); row <= end; row++)
|
123
|
|
{
|
124
|
9
|
int columns = getColumnCount();
|
125
|
9
|
Collection cells = new ArrayList(columns);
|
126
|
|
|
127
|
9
|
for (int columnIndex = 0; columnIndex < columns; columnIndex++)
|
128
|
|
{
|
129
|
15
|
TableColumn column = getTableColumn(columnIndex);
|
130
|
|
|
131
|
15
|
if (column.isVisible())
|
132
|
|
{
|
133
|
15
|
View view = column.getCellView(this, row, columnIndex);
|
134
|
15
|
view.setContainer(this);
|
135
|
15
|
cells.add(view);
|
136
|
|
}
|
137
|
|
}
|
138
|
9
|
views.add(cells);
|
139
|
|
}
|
140
|
|
}
|
141
|
|
|
142
|
5
|
public View getCellView(int row, int column)
|
143
|
|
{
|
144
|
5
|
return (View) getRowViews(row).get(column);
|
145
|
|
}
|
146
|
|
|
147
|
15
|
private TableColumn getTableColumn(int column)
|
148
|
|
{
|
149
|
15
|
return (TableColumn) tableColumns.get(column);
|
150
|
|
}
|
151
|
|
|
152
|
0
|
public ValueModel getCellModel(int row, int column)
|
153
|
|
{
|
154
|
0
|
TableColumn tableColumn = getTableColumn(column);
|
155
|
0
|
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
|
39
|
public void resetCreatedViews()
|
162
|
|
{
|
163
|
39
|
views = null;
|
164
|
|
}
|
165
|
|
|
166
|
0
|
public List getChildren()
|
167
|
|
{
|
168
|
0
|
List all = super.getChildren();
|
169
|
0
|
for (Iterator iterator = views.iterator(); iterator.hasNext();)
|
170
|
|
{
|
171
|
0
|
List cells = (List) iterator.next();
|
172
|
0
|
all.addAll(cells);
|
173
|
|
}
|
174
|
0
|
return all;
|
175
|
|
}
|
176
|
|
|
177
|
2
|
private TableCellViewFactory createDefaultTableCellViewFactory()
|
178
|
|
{
|
179
|
2
|
if (readOnly)
|
180
|
2
|
return new ReadOnlyCellViewFactory();
|
181
|
|
else
|
182
|
0
|
return new DefaultTableCellViewFactory();
|
183
|
|
}
|
184
|
|
|
185
|
3
|
public TableCellViewFactory getViewFactory()
|
186
|
|
{
|
187
|
2
|
if (viewFactory == null) viewFactory = createDefaultTableCellViewFactory();
|
188
|
3
|
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
|
2
|
private void createTableColumns()
|
197
|
|
{
|
198
|
2
|
int columns = getTableModel().getColumnCount();
|
199
|
2
|
tableColumns = new ArrayList(columns);
|
200
|
2
|
for (int column = 0; column < columns; column++)
|
201
|
|
{
|
202
|
3
|
TableColumn tableColumn = new TableColumn(getTableModel(), column);
|
203
|
3
|
tableColumn.setViewFactory(getViewFactory());
|
204
|
3
|
addTableColumn(tableColumn);
|
205
|
|
}
|
206
|
|
}
|
207
|
|
|
208
|
0
|
public void setTableCellViewFactory(TableCellViewFactory viewFactory)
|
209
|
|
{
|
210
|
0
|
this.viewFactory = viewFactory;
|
211
|
|
}
|
212
|
|
|
213
|
6
|
public int getRowCount()
|
214
|
|
{
|
215
|
6
|
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
|
9
|
public int getColumnCount()
|
223
|
|
{
|
224
|
9
|
return tableColumns == null ? 0 : tableColumns.size();
|
225
|
|
}
|
226
|
|
|
227
|
24
|
public TableModel getTableModel()
|
228
|
|
{
|
229
|
1
|
if (model == null) model = createDefaultTableModel();
|
230
|
24
|
return model;
|
231
|
|
}
|
232
|
|
|
233
|
1
|
protected TableModel createDefaultTableModel()
|
234
|
|
{
|
235
|
1
|
return new DefaultTableModel();
|
236
|
|
}
|
237
|
|
|
238
|
0
|
public String getColumnName(int column)
|
239
|
|
{
|
240
|
0
|
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
|
5
|
public void setRange(int start, int end)
|
249
|
|
{
|
250
|
5
|
resetCreatedViews();
|
251
|
5
|
if (end <= start)
|
252
|
|
{
|
253
|
0
|
start = 0;
|
254
|
0
|
end = -1;
|
255
|
|
}
|
256
|
5
|
model.setRange(start, end);
|
257
|
|
}
|
258
|
|
|
259
|
|
/**
|
260
|
|
* Insert a new column at the specified index.
|
261
|
|
*/
|
262
|
0
|
public void addTableColumn(int index, TableColumn column)
|
263
|
|
{
|
264
|
0
|
resetCreatedViews();
|
265
|
0
|
tableColumns.add(index, column);
|
266
|
0
|
column.addChangeListener(resetCreatedViewsListener);
|
267
|
|
}
|
268
|
|
|
269
|
|
/**
|
270
|
|
* Add a new column at the end.
|
271
|
|
*/
|
272
|
3
|
public void addTableColumn(TableColumn column)
|
273
|
|
{
|
274
|
0
|
if (tableColumns == null) tableColumns = new ArrayList();
|
275
|
3
|
resetCreatedViews();
|
276
|
3
|
tableColumns.add(column);
|
277
|
3
|
column.addChangeListener(resetCreatedViewsListener);
|
278
|
|
}
|
279
|
|
|
280
|
0
|
public void setColumnCellViewFactory(int column, TableCellViewFactory columnFactory)
|
281
|
|
{
|
282
|
0
|
if (tableColumns == null) tableColumns = new ArrayList();
|
283
|
0
|
resetCreatedViews();
|
284
|
0
|
TableColumn tableColumn = getTableColumn(column);
|
285
|
0
|
tableColumn.setViewFactory(columnFactory);
|
286
|
|
}
|
287
|
|
|
288
|
21
|
public int getStartRow()
|
289
|
|
{
|
290
|
21
|
return model.getStart();
|
291
|
|
}
|
292
|
|
|
293
|
12
|
public int getEndRow()
|
294
|
|
{
|
295
|
12
|
return model.getEnd();
|
296
|
|
}
|
297
|
|
|
298
|
|
private int scrollStart = 0;
|
299
|
|
private int scrollEnd = 0;
|
300
|
|
private int scrollNumberOfRows = 0;
|
301
|
|
|
302
|
1
|
public void scrollToStart()
|
303
|
|
{
|
304
|
1
|
scrollStart = 0;
|
305
|
1
|
scrollEnd = scrollNumberOfRows - 1;
|
306
|
1
|
setRange(scrollStart, scrollEnd);
|
307
|
|
}
|
308
|
|
|
309
|
2
|
public void scrollForward()
|
310
|
|
{
|
311
|
2
|
scrollStart += scrollNumberOfRows;
|
312
|
2
|
scrollEnd += scrollNumberOfRows;
|
313
|
2
|
setRange(scrollStart, scrollEnd);
|
314
|
|
}
|
315
|
|
|
316
|
2
|
public void scrollBackward()
|
317
|
|
{
|
318
|
2
|
scrollStart -= scrollNumberOfRows;
|
319
|
2
|
scrollEnd -= scrollNumberOfRows;
|
320
|
2
|
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
|
1
|
public void setNumberOfScrollRows(int numberOfRows)
|
328
|
|
{
|
329
|
1
|
this.scrollNumberOfRows = numberOfRows;
|
330
|
1
|
scrollToStart();
|
331
|
|
}
|
332
|
|
|
333
|
0
|
public void render(RenderContext context) throws IOException
|
334
|
|
{
|
335
|
0
|
context.startTag("table id=\"" + uniqueId(context) + "\" border=\"" + (isDebugTables() ? "1" : "0") + "\"");
|
336
|
|
|
337
|
|
//int modelColumns = getTableModel().getColumnCount();
|
338
|
|
|
339
|
0
|
context.startTag("tr");
|
340
|
0
|
for (int columnIndex = 0; columnIndex < getColumnCount(); columnIndex++)
|
341
|
|
{
|
342
|
0
|
TableColumn column = getTableColumn(columnIndex);
|
343
|
0
|
if (column.isVisible())
|
344
|
|
{
|
345
|
0
|
String name = column.getColumnName();
|
346
|
0
|
context.startTag("th");
|
347
|
0
|
if (name != null)
|
348
|
0
|
context.println(name);
|
349
|
|
else
|
350
|
0
|
context.println(" ");
|
351
|
0
|
context.endTag("th");
|
352
|
|
}
|
353
|
|
}
|
354
|
0
|
context.endTag("tr");
|
355
|
|
|
356
|
0
|
int endRow = getEndRow();
|
357
|
0
|
for (int row = getStartRow(); row <= endRow; row++)
|
358
|
|
{
|
359
|
0
|
context.startTag("tr");
|
360
|
0
|
Iterator iterator = getRowViews(row).iterator();
|
361
|
0
|
while (iterator.hasNext())
|
362
|
|
{
|
363
|
0
|
View cell = (View) iterator.next();
|
364
|
0
|
context.startTag("td");
|
365
|
0
|
cell.render(context);
|
366
|
0
|
context.endTag("td");
|
367
|
|
}
|
368
|
0
|
context.endTag("tr");
|
369
|
|
}
|
370
|
0
|
context.endTag("table");
|
371
|
|
}
|
372
|
|
}
|
373
|
|
|