001: package org.wingx;
002:
003: import org.wings.*;
004: import org.wings.table.*;
005: import org.wings.util.SStringBuilder;
006: import org.wings.event.SMouseListener;
007: import org.wings.event.SMouseEvent;
008: import org.wingx.table.*;
009: import org.wingx.plaf.css.XTableCG;
010:
011: import javax.swing.table.TableModel;
012: import java.util.*;
013:
014: public class XTable extends STable {
015: private static final String MESSAGE_PREFIX = "org.wingx.XTable.";
016: private static final SIcon ICON_TRANS = new SResourceIcon(
017: "org/wings/icons/transdot.gif");
018: private static final SIcon ICON_NONE = new SResourceIcon(
019: "org/wingx/table/images/sort_none.png");
020: private static final SIcon ICON_ASCENDING = new SResourceIcon(
021: "org/wingx/table/images/sort_up.png");
022: private static final SIcon ICON_DESCENDING = new SResourceIcon(
023: "org/wingx/table/images/sort_down.png");
024: public static final SIcon ICON_REFRESH = new SResourceIcon(
025: "org/wingx/table/images/table_refresh.png");
026: public static final SIcon ICON_RESET = new SResourceIcon(
027: "org/wingx/table/images/table_clear_filter.png");
028:
029: private Map column2Listeners = new HashMap();
030: protected boolean filterVisible = true;
031: protected boolean resetFilter = false;
032: protected int delayedSortColumn = -1;
033: private EditableTableCellRenderer filterRenderer;
034: private boolean refresh;
035: private SMouseListener linkMouseListener;
036: private SStringBuilder nameBuffer = new SStringBuilder();
037: private String noDataFoundLabel = "- - -";
038: private String noDataAvailableLabel = "- - -";
039:
040: public XTable() {
041: }
042:
043: public XTable(TableModel model, STableColumnModel columnModel) {
044: super (model, columnModel);
045: }
046:
047: public XTable(TableModel tableModel) {
048: super (tableModel);
049: }
050:
051: public EditableTableCellRenderer getFilterRenderer() {
052: return filterRenderer;
053: }
054:
055: public void setFilterRenderer(
056: EditableTableCellRenderer filterRenderer) {
057: this .filterRenderer = filterRenderer;
058: }
059:
060: protected void nameFilterComponent(final SComponent component,
061: final int col, final int num) {
062: nameBuffer.setLength(0);
063: nameBuffer.append(this .getName()).append("_f_").append(col)
064: .append("_").append(num);
065: component.setNameRaw(nameBuffer.toString());
066: }
067:
068: // private int nthVisibleColumn(int n) {
069: // n += 1;
070: // int c = 0;
071: // for (Iterator iterator = columnModel.getColumns().iterator(); iterator.hasNext();) {
072: // STableColumn column = (STableColumn)iterator.next();
073: // if (!column.isHidden())
074: // n --;
075: //
076: // if (n == 0)
077: // return c;
078: //
079: // c ++;
080: // }
081: // throw new RuntimeException("There is no " + n + "'th visible column.");
082: // }
083:
084: protected boolean filtersDifferent(Object filter, Object value) {
085: if (filter instanceof Object[] && value instanceof Object[]) {
086: return !arraysEqual((Object[]) filter, (Object[]) value);
087: } else {
088: return SComponent.isDifferent(filter, value);
089: }
090: }
091:
092: /**
093: * Arrays.equals() doesn't work when the array elements are arrays
094: * and so on...
095: */
096: private boolean arraysEqual(Object[] arr1, Object[] arr2) {
097: if (arr1.length != arr2.length) {
098: return false;
099: }
100: for (int i = 0; i < arr1.length; i++) {
101: Object o1 = arr1[i];
102: Object o2 = arr2[i];
103: if (o1 == o2) {
104: continue;
105: }
106: if (o1 instanceof Object[] && o2 instanceof Object[]) {
107: boolean b = arraysEqual((Object[]) o1, (Object[]) o2);
108: if (b) {
109: continue;
110: } else {
111: return false;
112: }
113: }
114: if (o1 == null || o2 == null || !o1.equals(o2)) {
115: return false;
116: }
117: }
118: return true;
119: }
120:
121: public void processLowLevelEvent(String action, String[] values) {
122: if (model instanceof FilterableTableModel
123: && action.indexOf("_f_") != -1) {
124: FilterableTableModel filterableTableModel = (FilterableTableModel) getModel();
125:
126: StringTokenizer tokens = new StringTokenizer(action, "_");
127: tokens.nextToken(); // tableName
128: tokens.nextToken(); // f
129: int col = Integer.parseInt(tokens.nextToken()); // col
130:
131: EditableTableCellRenderer editableTableCellRenderer = getFilterRenderer(col);
132: editableTableCellRenderer.getLowLevelEventListener(this ,
133: -1, col).processLowLevelEvent(action, values);
134: Object value = editableTableCellRenderer.getValue();
135:
136: Object filter = filterableTableModel
137: .getFilter(convertColumnIndexToModel(col));
138: if (filtersDifferent(filter, value)) {
139: filterableTableModel.setFilter(
140: convertColumnIndexToModel(col), value);
141: SForm.addArmedComponent(this );
142: refresh = true;
143: }
144: } else if (isEditing() && action.indexOf("_e_") != -1
145: && cellEditorComponent != null) {
146: cellEditorComponent.processLowLevelEvent(action, values);
147: } else if (action.indexOf("_") != -1) {
148: StringTokenizer tokens = new StringTokenizer(action, "_");
149: tokens.nextToken(); // tableName
150: int row = Integer.parseInt(tokens.nextToken()); // row
151: int col = Integer.parseInt(tokens.nextToken()); // col
152:
153: STableCellRenderer cellRenderer = getCellRenderer(row, col);
154: if (cellRenderer instanceof EditableTableCellRenderer) {
155: EditableTableCellRenderer editableCellRenderer = (EditableTableCellRenderer) cellRenderer;
156: editableCellRenderer.getLowLevelEventListener(this ,
157: row, col).processLowLevelEvent(action, values);
158: Object value = editableCellRenderer.getValue();
159: getModel().setValueAt(value, row,
160: convertColumnIndexToModel(col));
161: }
162: } else {
163: for (int i = 0; i < values.length; i++) {
164: String value = values[i];
165: char modus = value.charAt(0);
166: try {
167: // editor event
168: switch (modus) {
169: case 'o':
170: delayedSortColumn = Integer.parseInt(value
171: .substring(1));
172: SForm.addArmedComponent(this );
173: break;
174: case 'c':
175: resetFilter = true;
176: break;
177: case 'r':
178: refresh = true;
179: break;
180: }
181: } catch (NumberFormatException ex) {
182: // ignored
183: }
184: }
185: super .processLowLevelEvent(action, values);
186: }
187: }
188:
189: public void fireFinalEvents() {
190: super .fireFinalEvents();
191: /*
192: * check for sort
193: */
194: if (delayedSortColumn != -1) {
195: SortableTableModel sortableModel = (SortableTableModel) model;
196: int order = sortableModel.getSort(delayedSortColumn);
197: order += 1;
198: order %= 3;
199: sortableModel.setSort(delayedSortColumn, order);
200: delayedSortColumn = -1;
201: refresh = true;
202: }
203: /*
204: * check for filter reset
205: */
206: if (resetFilter) {
207: resetFilter = false;
208: resetFilter();
209: refresh = true;
210: }
211:
212: if (refresh) {
213: refresh = false;
214: refresh();
215: }
216: }
217:
218: /**
219: * refresh the table
220: */
221: public void refresh() {
222: if (getModel() instanceof RefreshableModel)
223: ((RefreshableModel) getModel()).refresh();
224: }
225:
226: public void resetFilter() {
227: FilterableTableModel filterableTableModel = (FilterableTableModel) getModel();
228: for (int i = 0; i < filterableTableModel.getColumnCount(); i++)
229: filterableTableModel.setFilter(i, null);
230: }
231:
232: public void updateCG() {
233: setCG(new XTableCG());
234: }
235:
236: public String getToggleSortParameter(int col) {
237: return "o" + col;
238: }
239:
240: public String getRefreshParameter() {
241: return "r";
242: }
243:
244: public String getResetParameter() {
245: return "c";
246: }
247:
248: /**
249: * Returns the header renderer for the given header cell.
250: * @param col Table column
251: * @return The header renderer for the given header cell.
252: */
253: public EditableTableCellRenderer getFilterRenderer(int col) {
254: STableColumnModel columnModel = getColumnModel();
255: if (columnModel != null) {
256: STableColumn column = columnModel.getColumn(col);
257: if (column != null) {
258: STableCellRenderer renderer = column instanceof XTableColumn ? ((XTableColumn) column)
259: .getFilterRenderer()
260: : column.getCellRenderer();
261: if (renderer instanceof EditableTableCellRenderer)
262: return (EditableTableCellRenderer) renderer;
263: }
264: }
265: return getFilterRenderer();
266: }
267:
268: /**
269: * Prepares and returns the renderer to render the column filter
270: * @param col Column number to render. Starts with <code>0</code>. May be <code>-1</code> for row selection column.
271: * @return The renderer to render the column filter
272: */
273: public SComponent prepareFilterRenderer(
274: EditableTableCellRenderer filterRenderer, int col) {
275: Object filterValue = col >= 0 ? ((FilterableTableModel) model)
276: .getFilter(convertColumnIndexToModel(col)) : null;
277: SComponent component = filterRenderer
278: .getTableCellRendererComponent(this , filterValue,
279: false, -1, col);
280: nameFilterComponent(component, col);
281: return component;
282: }
283:
284: protected void nameFilterComponent(final SComponent component,
285: final int col) {
286: nameBuffer.setLength(0);
287: nameBuffer.append(this .getName()).append("_f_");
288: nameBuffer.append(col);
289: component.setNameRaw(nameBuffer.toString());
290: }
291:
292: public final void removeClickListener(
293: final XTableClickListener listener) {
294: for (Iterator iterator = column2Listeners.entrySet().iterator(); iterator
295: .hasNext();) {
296: Map.Entry colAndListeners = (Map.Entry) iterator.next();
297: List l = (List) colAndListeners.getValue();
298: if (l == null) {
299: continue;
300: }
301: l.remove(listener);
302: }
303: }
304:
305: public void addClickListener(int index, XTableClickListener listener) {
306: List l = (List) column2Listeners.get(Integer.valueOf(index));
307: if (l == null) {
308: l = new ArrayList();
309: column2Listeners.put(Integer.valueOf(index), l);
310: }
311: l.add(listener);
312: if (linkMouseListener == null) {
313: linkMouseListener = new SMouseListener() {
314: public void mouseClicked(SMouseEvent e) {
315: SPoint point = e.getPoint();
316: int col = XTable.this .columnAtPoint(point);
317: List listeners = (List) column2Listeners
318: .get(Integer.valueOf(col));
319: if (listeners == null) {
320: return;
321: }
322: for (Iterator iterator = listeners.iterator(); iterator
323: .hasNext();) {
324: XTableClickListener listener = (XTableClickListener) iterator
325: .next();
326: listener.clickOccured(XTable.this
327: .rowAtPoint(point), col);
328: }
329: }
330: };
331: addMouseListener(linkMouseListener);
332: }
333: }
334:
335: public boolean isCellEditable(int row, int col) {
336: if (isClickListenerSet(col)) {
337: return true;
338: }
339: return super .isCellEditable(row, col);
340: }
341:
342: public boolean isColumnSortable(int col) {
343: STableColumn column = getColumnModel().getColumn(col);
344: if (column instanceof XTableColumn) {
345: XTableColumn xTableColumn = (XTableColumn) column;
346: return xTableColumn.isSortable();
347: }
348: return false;
349: }
350:
351: public boolean isFilterVisible() {
352: return filterVisible;
353: }
354:
355: public void setFilterVisible(boolean filterVisible) {
356: this .filterVisible = filterVisible;
357: }
358:
359: private boolean isClickListenerSet(int col) {
360: List l = (List) column2Listeners.get(Integer.valueOf(col));
361: return (l != null && !l.isEmpty());
362: }
363:
364: public SComponent prepareRenderer(STableCellRenderer r, int row,
365: int col) {
366: SComponent component = super .prepareRenderer(r, row, col);
367: if (isClickListenerSet(col)) {
368: if (!(component.getStyle().indexOf(" link ") > 0)) {
369: component.setStyle(component.getStyle() + " link ");
370: }
371: } else if (component.getStyle() != null) {
372: component.setStyle(component.getStyle().replaceAll(
373: " link ", " "));
374: }
375: return component;
376: }
377:
378: public boolean editCellAt(int row, int column, EventObject eo) {
379: if (isClickListenerSet(column)) {
380: return false;
381: }
382: return super .editCellAt(row, column, eo);
383: }
384:
385: public static class HeaderRenderer extends
386: SDefaultTableCellRenderer {
387: public HeaderRenderer() {
388: setHorizontalTextPosition(SConstants.LEFT);
389: setPreferredSize(SDimension.FULLWIDTH);
390: }
391:
392: public SComponent getTableCellRendererComponent(STable table,
393: Object value, boolean selected, int row, final int col) {
394: if (table.getModel() instanceof SortableTableModel) {
395: SortableTableModel sortableTableModel = (SortableTableModel) table
396: .getModel();
397:
398: setIcon(null);
399: setText(value != null ? value.toString() : null);
400:
401: if (!((XTable) table).isColumnSortable(col)) {
402: setIcon(null);
403: } else
404: switch (sortableTableModel.getSort(col)) {
405: case SortableTableModel.SORT_NONE:
406: setIcon(ICON_NONE);
407: break;
408: case SortableTableModel.SORT_ASCENDING:
409: setIcon(ICON_ASCENDING);
410: break;
411: case SortableTableModel.SORT_DESCENDING:
412: setIcon(ICON_DESCENDING);
413: break;
414: }
415:
416: return this ;
417: } else
418: return super .getTableCellRendererComponent(table,
419: value, selected, row, col);
420: }
421: }
422:
423: public void createDefaultColumnsFromModel() {
424: TableModel tm = getModel();
425:
426: if (tm != null) {
427: STableColumnModel columnModel = getColumnModel();
428: while (columnModel.getColumnCount() > 0)
429: columnModel.removeColumn(columnModel.getColumn(0));
430:
431: for (int i = 0; i < tm.getColumnCount(); i++) {
432: XTableColumn column = new XTableColumn(i);
433: String columnName = tm.getColumnName(i);
434: column.setHeaderValue(columnName);
435: this .columnModel.addColumn(column);
436: }
437: }
438: }
439:
440: protected STableColumnModel createDefaultColumnModel() {
441: return new XDefaultTableColumnModel();
442: }
443:
444: public String getNoDataFoundLabel() {
445: return noDataFoundLabel;
446: }
447:
448: public void setNoDataFoundLabel(String noDataFoundLabel) {
449: this .noDataFoundLabel = noDataFoundLabel;
450: }
451:
452: public String getNoDataAvailableLabel() {
453: return noDataAvailableLabel;
454: }
455:
456: public void setNoDataAvailableLabel(String noDataAvailableLabel) {
457: this.noDataAvailableLabel = noDataAvailableLabel;
458: }
459:
460: }
|