001: /* ====================================================================
002: * The QueryForm License, Version 1.1
003: *
004: * Copyright (c) 1998 - 2003 David F. Glasser. All rights
005: * reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * 1. Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: *
014: * 2. Redistributions in binary form must reproduce the above copyright
015: * notice, this list of conditions and the following disclaimer in
016: * the documentation and/or other materials provided with the
017: * distribution.
018: *
019: * 3. The end-user documentation included with the redistribution,
020: * if any, must include the following acknowledgment:
021: * "This product includes software developed by
022: * David F. Glasser."
023: * Alternately, this acknowledgment may appear in the software itself,
024: * if and wherever such third-party acknowledgments normally appear.
025: *
026: * 4. The names "QueryForm" and "David F. Glasser" must
027: * not be used to endorse or promote products derived from this
028: * software without prior written permission. For written
029: * permission, please contact dglasser@pobox.com.
030: *
031: * 5. Products derived from this software may not be called "QueryForm",
032: * nor may "QueryForm" appear in their name, without prior written
033: * permission of David F. Glasser.
034: *
035: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038: * DISCLAIMED. IN NO EVENT SHALL DAVID F. GLASSER, THE APACHE SOFTWARE
039: * FOUNDATION OR ITS CONTRIBUTORS, OR ANY AUTHORS OR DISTRIBUTORS
040: * OF THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
041: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
042: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
043: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
044: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
045: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
046: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
047: * SUCH DAMAGE.
048: * ====================================================================
049: *
050: * This product includes software developed by the
051: * Apache Software Foundation (http://www.apache.org/).
052: *
053: * ====================================================================
054: *
055: * $Source: /cvsroot/qform/qform/src/org/glasser/swing/table/ListTableModel.java,v $
056: * $Revision: 1.4 $
057: * $Author: dglasser $
058: * $Date: 2005/04/12 03:11:10 $
059: *
060: * --------------------------------------------------------------------
061: */
062: package org.glasser.swing.table;
063:
064: import javax.swing.*;
065: import javax.swing.table.*;
066: import javax.swing.event.*;
067: import java.util.*;
068: import java.awt.event.*;
069: import java.beans.*;
070: import org.glasser.util.*;
071:
072: public class ListTableModel extends AbstractTableModel {
073:
074: protected int[] columnMappings = null;
075:
076: private ColumnManager columnManager = null;
077:
078: private ColumnManagerComparator comparator = null;
079:
080: /**
081: * This indicates whether the last sort applied to the dataList was in
082: * descending order or not.
083: */
084: protected boolean descendingSort = false;
085:
086: /**
087: * Index of the last column on which this table was sorted.
088: */
089: protected int sortColumn = -1;
090:
091: /**
092: * This list holds the data for the table, and it is assumed that each
093: * element in this list is analogous to one row in the table. The ColumnManager
094: * knows, when given an object representing a row, how to get the value
095: * for a particular column within that row.
096: */
097: protected List dataList = null;
098:
099: protected SmartEventListenerList listeners = new SmartEventListenerList();
100:
101: public void setColumnManager(ColumnManager columnManager) {
102: this .columnManager = columnManager;
103: if (columnManager != null) {
104: comparator = new ColumnManagerComparator(columnManager);
105: } else {
106: comparator = null;
107: }
108: }
109:
110: public ColumnManager getColumnManager() {
111: return columnManager;
112: }
113:
114: public ListTableModel(ColumnManager columnManager, List data) {
115: super ();
116: this .columnManager = columnManager;
117: if (columnManager != null) {
118: comparator = new ColumnManagerComparator(columnManager);
119: } else {
120: comparator = null;
121: }
122: setDataList(data);
123:
124: }
125:
126: public ListTableModel() {
127: super ();
128: dataList = new Vector(0);
129: }
130:
131: public int getColumnCount() {
132: if (columnManager != null) {
133: if (columnMappings != null)
134: return columnMappings.length;
135: else
136: return columnManager.getColumnCount();
137: } else {
138: return 0;
139: }
140: }
141:
142: public String getColumnName(int columnIndex) {
143: if (columnManager != null) {
144: return columnManager
145: .getColumnName(getMappedColumnIndex(columnIndex));
146: } else {
147: return null;
148: }
149:
150: }
151:
152: public List getDataList() {
153: return dataList;
154: }
155:
156: public synchronized void setDataList(List newData) {
157: if (newData != null) {
158: this .dataList = newData;
159: fireTableDataChanged();
160: } else {
161: int rows = getRowCount();
162: this .dataList = new Vector(0);
163: if (rows > 0) {
164: fireTableRowsDeleted(0, rows - 1);
165: }
166: }
167: }
168:
169: public int getRowCount() {
170: if (dataList != null) {
171: return dataList.size();
172: } else {
173: return 0;
174: }
175: }
176:
177: public Object getObjectAtRow(int row) {
178: if (row < 0 || row >= dataList.size())
179: return null;
180: return dataList.get(row);
181: }
182:
183: public void addObject(Object obj) {
184: if (obj == null)
185: return;
186: if (dataList == null)
187: dataList = new Vector();
188: dataList.add(obj);
189: int newRowIndex = dataList.size() - 1;
190: fireTableRowsInserted(newRowIndex, newRowIndex);
191: fireDataObjectInserted(obj);
192: }
193:
194: public synchronized void replaceObject(Object oldObject,
195: Object newObject) {
196: if (oldObject == null)
197: return;
198: int index = dataList.indexOf(oldObject);
199: if (index < 0 || index >= dataList.size())
200: return;
201: dataList.set(index, newObject);
202: fireTableDataChanged();
203: fireDataObjectDeleted(oldObject);
204: fireDataObjectInserted(newObject);
205: }
206:
207: public void removeObject(Object obj) {
208: int rowToRemove = dataList.indexOf(obj);
209: if (rowToRemove < 0 || rowToRemove > (getRowCount() - 1))
210: return;
211: dataList.remove(obj);
212: fireTableRowsDeleted(rowToRemove, rowToRemove);
213: fireDataObjectDeleted(obj);
214:
215: }
216:
217: public void removeObjectAtRow(int rowToRemove) {
218: if (rowToRemove < 0 || rowToRemove > (getRowCount() - 1))
219: return;
220: Object obj = dataList.get(rowToRemove);
221: if (obj == null)
222: return;
223: dataList.remove(rowToRemove);
224: fireTableRowsDeleted(rowToRemove, rowToRemove);
225: fireDataObjectDeleted(obj);
226: }
227:
228: public boolean isCellEditable(int row, int column) {
229: if (columnManager != null) {
230: return columnManager.isCellEditable(row,
231: getMappedColumnIndex(column), getObjectAtRow(row));
232: } else {
233: return false;
234: }
235:
236: }
237:
238: /**
239: * Swaps the indicated table row with the one immediately below it. If
240: * it is the bottom row in the table, or out of bounds, nothing happens.
241: */
242: public void moveRowDown(int row) {
243: if (dataList == null || row < 0 || row >= (dataList.size() - 1))
244: return;
245:
246: int upperRow = row;
247: int lowerRow = row + 1;
248:
249: Object upperObj = getObjectAtRow(upperRow);
250: Object lowerObj = getObjectAtRow(lowerRow);
251:
252: dataList.set(upperRow, lowerObj);
253: dataList.set(lowerRow, upperObj);
254:
255: this .fireTableRowsUpdated(upperRow, lowerRow);
256: }
257:
258: /**
259: * Swaps the indicated table row with the one immediately above it. If
260: * it is the top row in the table, or out of bounds, nothing happens.
261: */
262: public void moveRowUp(int row) {
263: if (dataList == null || row < 0 || row >= (dataList.size() - 1))
264: return;
265:
266: int upperRow = row - 1;
267: int lowerRow = row;
268:
269: Object upperObj = getObjectAtRow(upperRow);
270: Object lowerObj = getObjectAtRow(lowerRow);
271:
272: dataList.set(upperRow, lowerObj);
273: dataList.set(lowerRow, upperObj);
274:
275: this .fireTableRowsUpdated(upperRow, lowerRow);
276: }
277:
278: /**
279: * Inserts the given object into the data list at the given row.
280: *
281: * @throws UnsupportedOperationException if the underlying List does not
282: * support the addAll() operation.
283: */
284: public void insertObjectAtRow(Object obj, int row) {
285: if (obj == null || row < 0 || dataList == null
286: || row > dataList.size())
287: return;
288: ArrayList a = new ArrayList(1);
289: a.add(obj);
290: dataList.addAll(row, a);
291: fireTableRowsInserted(row, row);
292: fireDataObjectInserted(obj);
293: }
294:
295: public java.lang.Class getColumnClass(int column) {
296: if (columnManager != null) {
297: return columnManager
298: .getColumnClass(getMappedColumnIndex(column));
299: } else {
300: return java.lang.Object.class;
301: }
302: }
303:
304: public Object getValueAt(int rowIndex, int columnIndex) {
305: if (columnManager != null) {
306: return columnManager.getValueAt(rowIndex,
307: getMappedColumnIndex(columnIndex),
308: getObjectAtRow(rowIndex));
309: } else {
310: return null;
311: }
312: }
313:
314: public void fireTableDataChanged() {
315: super .fireTableDataChanged();
316: }
317:
318: public void setColumnMappings(int[] columnMappings) {
319: if (columnMappings != null) {
320: this .columnMappings = (int[]) columnMappings.clone();
321: } else {
322: this .columnMappings = null;
323: }
324: }
325:
326: public int[] getColumnMappings() {
327: if (columnMappings == null)
328: return null;
329: return (int[]) columnMappings.clone();
330: }
331:
332: public int getMappedColumnIndex(int columnIndex) {
333: if (columnMappings == null)
334: return columnIndex;
335: return columnMappings[columnIndex];
336: }
337:
338: public void addListTableModelListener(
339: ListTableModelListener listener) {
340: listeners.add(
341: org.glasser.swing.table.ListTableModelListener.class,
342: listener);
343: }
344:
345: public void removeListTableModelListener(
346: ListTableModelListener listener) {
347: listeners.remove(
348: org.glasser.swing.table.ListTableModelListener.class,
349: listener);
350: }
351:
352: private void fireDataObjectInserted(Object rowObject) {
353: ListTableModelEvent event = new ListTableModelEvent(this ,
354: rowObject, ListTableModelEvent.INSERT);
355: listeners.fireSmartEvent(event);
356: }
357:
358: private void fireDataObjectDeleted(Object rowObject) {
359: ListTableModelEvent event = new ListTableModelEvent(this ,
360: rowObject, ListTableModelEvent.DELETE);
361: listeners.fireSmartEvent(event);
362: }
363:
364: private void fireDataObjectUpdated(Object rowObject) {
365: ListTableModelEvent event = new ListTableModelEvent(this ,
366: rowObject, ListTableModelEvent.UPDATE);
367: listeners.fireSmartEvent(event);
368: }
369:
370: /**
371: * Sorts the model's contents on the given column.
372: *
373: * @param columnIndex the zero-based index of the column on which the data
374: * will be sorted.
375: *
376: * @param sortDescending if true, the data is sorted in descending order, otherwise
377: * it will be sorted in ascending order.
378: */
379: public void sort(int columnIndex, boolean sortDescending) {
380: if (columnManager == null || dataList == null)
381: return;
382: comparator.setSortColumn(columnIndex);
383: comparator.setSortDescending(sortDescending);
384: Collections.sort(dataList, comparator);
385: this .fireTableDataChanged();
386: }
387:
388: /**
389: * Sorts the buffer on the indicated column, and returns true if the sort was in
390: * descending order, or false if it was in ascending order. The order
391: * will usually be ascending, unless the same column is sorted multiple times
392: * consecutively, in which case the sort order is reversed each time.
393: */
394: public boolean sort(int columnIndex) {
395: boolean desc = false;
396:
397: // if the column to be sorted is the same as the last column
398: // that was sorted, then reverse the last sort order.
399: if (this .sortColumn == columnIndex)
400: desc = !descendingSort;
401:
402: sort(columnIndex, desc);
403:
404: return desc;
405:
406: }
407:
408: public void setValueAt(Object newCellValue, int rowIndex,
409: int columnIndex) {
410: Object rowObject = dataList.get(rowIndex);
411: columnManager.setValueAt(newCellValue, rowIndex, columnIndex,
412: rowObject);
413: fireTableDataChanged();
414: fireDataObjectUpdated(rowObject);
415: }
416:
417: /**
418: * This method is used to set a particular Comparator that will be used when
419: * sorting on a particular column. It is especially useful for sorting String columns
420: * in case-insensitive order, by passing String.CASE_INSENSITIVE_ORDER to this method.
421: *
422: * @throws RuntimeException, if the ColumnManager for this ListTableModel is set to null.
423: */
424: public void setColumnComparator(int columnIndex,
425: Comparator columnComparator) {
426: if (columnManager == null) {
427: throw new RuntimeException(
428: "ListTableModel.setColumnComparator() cannot be called until a ColumnManager has been set.");
429: }
430: comparator.setColumnComparator(columnIndex, columnComparator);
431: }
432:
433: }
|