1
15
16 package gate.swing;
17
18 import java.awt.Component;
19 import java.awt.Dimension;
20 import java.awt.event.*;
21 import java.awt.event.MouseAdapter;
22 import java.awt.event.MouseEvent;
23 import java.util.*;
24 import java.util.ArrayList;
25 import java.util.List;
26 import javax.swing.*;
27 import javax.swing.Timer;
28 import javax.swing.event.TableModelEvent;
29 import javax.swing.event.TableModelListener;
30 import javax.swing.table.*;
31 import javax.swing.table.AbstractTableModel;
32 import javax.swing.table.TableModel;
33
34
47 public class XJTable extends JTable{
48
49 public XJTable(){
50 super();
51 }
52
53 public XJTable(TableModel model){
54 super();
55 setModel(model);
56 }
57
58 public void setModel(TableModel dataModel) {
59 sortingModel = new SortingModel(dataModel);
60 super.setModel(sortingModel);
61 newColumns();
62 }
63
64
67 protected void newColumns(){
68 columnData = new ArrayList(dataModel.getColumnCount());
69 for(int i = 0; i < dataModel.getColumnCount(); i++)
70 columnData.add(new ColumnData(i));
71 adjustSizes();
72 }
73
74
77 public void updateUI() {
78 super.updateUI();
79 getTableHeader().addMouseListener(new HeaderMouseListener());
80 adjustSizes();
81 }
82
83 public Dimension getPreferredSize(){
84 int width = 0;
85 for(int i = 0; i < getColumnModel().getColumnCount(); i++)
86 width += getColumnModel().getColumn(i).getPreferredWidth();
87 int height = 0;
88 for(int i = 0; i < getRowCount(); i++)
89 height += getRowHeight(i);
90 return new Dimension(width, height);
91 }
92
93 public Dimension getPreferredScrollableViewportSize(){
94 return getPreferredSize();
95 }
96
97
102 public void adjustSizes(){
103 Iterator colIter = columnData.iterator();
104 while(colIter.hasNext()){
105 ((ColumnData)colIter.next()).adjustColumnWidth();
106 }
107 repaint();
108 }
109
110
115 public int rowModelToView(int modelRow){
116 return sortingModel.sourceToTarget(modelRow);
117 }
118
119
122 public boolean isAscending() {
123 return ascending;
124 }
125
126
131 public boolean isColumnHidden(int columnIndex){
132 return ((ColumnData)columnData.get(columnIndex)).isHidden();
133 }
134
135
138 public void setAscending(boolean ascending) {
139 this.ascending = ascending;
140 }
141
146 public int rowViewToModel(int viewRow){
147 return sortingModel.targetToSource(viewRow);
148 }
149
150
156 public void setComparator(int column, Comparator comparator){
157 ((ColumnData)columnData.get(column)).comparator = comparator;
158 }
159
160
163 public boolean isSortable(){
164 return sortable;
165 }
166
169 public void setSortable(boolean sortable){
170 this.sortable = sortable;
171 }
172
175 public int getSortedColumn(){
176 return sortedColumn;
177 }
178
181 public void setSortedColumn(int sortColumn){
182 this.sortedColumn = sortColumn;
183 }
184
185
188 public int getTableRow(int modelRow){
189 return sortingModel.sourceToTarget(modelRow);
190 }
191
192
197 protected class SortingModel extends AbstractTableModel
198 implements TableModelListener{
199
200 public SortingModel(TableModel sourceModel){
201 init(sourceModel);
202 }
203
204 protected void init(TableModel sourceModel){
205 if(this.sourceModel != null)
206 this.sourceModel.removeTableModelListener(this);
207 this.sourceModel = sourceModel;
208 int size = sourceModel.getRowCount();
210 sourceToTarget = new int[size];
211 targetToSource = new int[size];
212 for(int i = 0; i < size; i++) {
213 sourceToTarget[i] = i;
214 targetToSource[i] = i;
215 }
216 sourceModel.addTableModelListener(this);
217 if(isSortable() && sortedColumn == -1) setSortedColumn(0);
218 }
219
220
223 public void tableChanged(TableModelEvent e){
224 int type = e.getType();
225 int firstRow = e.getFirstRow();
226 int lastRow = e.getLastRow();
227 int column = e.getColumn();
228
229
233 switch(type){
234 case TableModelEvent.UPDATE:
235 if(firstRow == TableModelEvent.HEADER_ROW){
236 init(sourceModel);
238 fireTableStructureChanged();
239 if(isSortable()) sort();
240 newColumns();
241 adjustSizes();
242 }else if(lastRow == Integer.MAX_VALUE){
243 init(sourceModel);
245 fireTableDataChanged();
246 if(isSortable()) sort();
247 adjustSizes();
248 }else{
249 if(isSortable() &&
252 (column == sortedColumn ||
253 column == TableModelEvent.ALL_COLUMNS)){
254 sort();
256 }else{
257 fireTableChanged(new TableModelEvent(this,
258 sourceToTarget(firstRow),
259 sourceToTarget(lastRow), column, type));
260
261 }
262 if(column == TableModelEvent.ALL_COLUMNS){
264 adjustSizes();
265 }else{
266 ((ColumnData)columnData.get(column)).adjustColumnWidth();
267 }
268 }
269 break;
270 case TableModelEvent.INSERT:
271 init(sourceModel);
273 if(firstRow == lastRow){
274 fireTableChanged(new TableModelEvent(this,
275 sourceToTarget(firstRow),
276 sourceToTarget(lastRow), column, type));
277 }else{
278 fireTableDataChanged();
280 }
281 if(isSortable()) sort();
282 if(column == TableModelEvent.ALL_COLUMNS) adjustSizes();
284 else ((ColumnData)columnData.get(column)).adjustColumnWidth();
285 break;
286 case TableModelEvent.DELETE:
287 init(sourceModel);
289 fireTableDataChanged();
290 }
292 }
293
294 public int getRowCount(){
295 return sourceModel.getRowCount();
296 }
297
298 public int getColumnCount(){
299 return sourceModel.getColumnCount();
300 }
301
302 public String getColumnName(int columnIndex){
303 return sourceModel.getColumnName(columnIndex);
304 }
305 public Class getColumnClass(int columnIndex){
306 return sourceModel.getColumnClass(columnIndex);
307 }
308
309 public boolean isCellEditable(int rowIndex, int columnIndex){
310 return sourceModel.isCellEditable(targetToSource(rowIndex),
311 columnIndex);
312 }
313 public void setValueAt(Object aValue, int rowIndex, int columnIndex){
314 sourceModel.setValueAt(aValue, targetToSource(rowIndex),
315 columnIndex);
316 }
317 public Object getValueAt(int row, int column){
318 return sourceModel.getValueAt(targetToSource(row), column);
319 }
320
321
326 public void sort(){
327 int[] rows = getSelectedRows();
329 for(int i = 0; i < rows.length; i++) rows[i] = rowViewToModel(rows[i]);
331 clearSelection();
332
333 List sourceData = new ArrayList(sourceModel.getRowCount());
334 for(int i = 0; i < sourceModel.getRowCount(); i++){
336 sourceData.add(sourceModel.getValueAt(i, sortedColumn));
337 }
338 Comparator comparator = ((ColumnData)columnData.
340 get(sortedColumn)).comparator;
341 if(comparator == null){
342 if(defaultComparator == null)
344 defaultComparator = new DefaultComparator();
345 comparator = defaultComparator;
346 }
347 for(int i = 0; i < sourceData.size() - 1; i++){
348 for(int j = i + 1; j < sourceData.size(); j++){
349 Object o1 = sourceData.get(targetToSource(i));
350 Object o2 = sourceData.get(targetToSource(j));
351 boolean swap = ascending ?
352 (comparator.compare(o1, o2) > 0) :
353 (comparator.compare(o1, o2) < 0);
354 if(swap){
355 int aux = targetToSource[i];
356 targetToSource[i] = targetToSource[j];
357 targetToSource[j] = aux;
358
359 sourceToTarget[targetToSource[i]] = i;
360 sourceToTarget[targetToSource[j]] = j;
361 }
362 }
363 }
364 fireTableRowsUpdated(0, sourceData.size() -1);
365 for(int i = 0; i < rows.length; i++){
368 rows[i] = rowModelToView(rows[i]);
369 getSelectionModel().addSelectionInterval(rows[i], rows[i]);
370 }
371 }
372
373
374
380 public int sourceToTarget(int index){
381 return sourceToTarget[index];
382 }
383
384
391 public int targetToSource(int index){
392 return targetToSource[index];
393 }
394
395
398 protected void buildTargetToSourceIndex(){
399 targetToSource = new int[sourceToTarget.length];
400 for(int i = 0; i < sourceToTarget.length; i++)
401 targetToSource[sourceToTarget[i]] = i;
402 }
403
404
407 protected int[] sourceToTarget;
408
409
412 protected int[] targetToSource;
413
414 protected TableModel sourceModel;
415 }
416
417 protected class HeaderMouseListener extends MouseAdapter{
418 public HeaderMouseListener(){
419 }
420
421 public void mouseClicked(MouseEvent e){
422 process(e);
423 }
424
425 public void mousePressed(MouseEvent e){
426 process(e);
427 }
428
429 public void mouseReleased(MouseEvent e){
430 process(e);
431 }
432
433 protected void process(MouseEvent e){
434 int viewColumn = columnModel.getColumnIndexAtX(e.getX());
435 final int column = convertColumnIndexToModel(viewColumn);
436 ColumnData cData = (ColumnData)columnData.get(column);
437 if((e.getID() == MouseEvent.MOUSE_PRESSED ||
438 e.getID() == MouseEvent.MOUSE_RELEASED) &&
439 e.isPopupTrigger()){
440 cData.popup.show(e.getComponent(), e.getX(), e.getY());
442 }else if(e.getID() == MouseEvent.MOUSE_CLICKED &&
443 e.getButton() == MouseEvent.BUTTON1){
444 if(e.getClickCount() >= 2){
446 if(singleClickTimer != null){
448 singleClickTimer.stop();
449 singleClickTimer = null;
450 }
451 cData.adjustColumnWidth();
452 }else {
453 singleClickTimer = new Timer(CLICK_DELAY, new ActionListener(){
455 public void actionPerformed(ActionEvent evt){
456 if(sortable && column != -1) {
458 ascending = (column == sortedColumn) ? !ascending : true;
459 sortedColumn = column;
460 sortingModel.sort();
461 }
462 }
463 });
464 singleClickTimer.setRepeats(false);
465 singleClickTimer.start();
466 }
467 }
468 }
469
473 private static final int CLICK_DELAY = 300;
474 protected Timer singleClickTimer;
475 }
476
477 protected class ColumnData{
478 public ColumnData(int column){
479 this.column = column;
480 popup = new JPopupMenu();
481 hideMenuItem = new JCheckBoxMenuItem("Hide", false);
482 popup.add(hideMenuItem);
483 autoSizeMenuItem = new JCheckBoxMenuItem("Autosize", true);
484 hidden = false;
486 initListeners();
487 }
488
489 protected void initListeners(){
490 hideMenuItem.addActionListener(new ActionListener(){
491 public void actionPerformed(ActionEvent evt){
492 TableColumn tCol = getColumnModel().getColumn(column);
493 if(hideMenuItem.isSelected()){
494 colWidth = tCol.getWidth();
496 colPreferredWidth = tCol.getPreferredWidth();
497 colMaxWidth = tCol.getMaxWidth();
498 tCol.setPreferredWidth(HIDDEN_WIDTH);
499 tCol.setMaxWidth(HIDDEN_WIDTH);
500 tCol.setWidth(HIDDEN_WIDTH);
501 }else{
502 tCol.setMaxWidth(colMaxWidth);
504 tCol.setPreferredWidth(colPreferredWidth);
505 tCol.setWidth(colWidth);
506 }
507 }
508 });
509
510 autoSizeMenuItem.addActionListener(new ActionListener(){
511 public void actionPerformed(ActionEvent evt){
512 if(autoSizeMenuItem.isSelected()){
513 adjustColumnWidth();
515 }
516 }
517 });
518
519 }
520
521 public boolean isHidden(){
522 return hideMenuItem.isSelected();
523 }
524
525 public void adjustColumnWidth(){
526 int viewColumn = convertColumnIndexToView(column);
527 TableColumn tCol = getColumnModel().getColumn(column);
528 Dimension dim;
529 int width, height;
530 TableCellRenderer renderer;
531 if(getTableHeader() != null){
533 renderer = tCol.getHeaderRenderer();
534 if(renderer == null) renderer = getTableHeader().getDefaultRenderer();
535 dim = renderer.getTableCellRendererComponent(XJTable.this,
536 tCol.getHeaderValue(), true, true ,0 , viewColumn).
537 getPreferredSize();
538 width = dim.width;
539 height = dim.height;
541 if(height + getRowMargin() > getTableHeader().getPreferredSize().height){
542 getTableHeader().setPreferredSize(
543 new Dimension(getTableHeader().getPreferredSize().width,
544 height));
545 }
546 int marginWidth = getColumnModel().getColumnMargin();
547 if(marginWidth > 0) width += marginWidth;
548 }else{
549 width = 0;
550 }
551 renderer = tCol.getCellRenderer();
552 if(renderer == null) renderer = getDefaultRenderer(getColumnClass(column));
553 for(int row = 0; row < getRowCount(); row ++){
554 if(renderer != null){
555 dim = renderer. getTableCellRendererComponent(XJTable.this,
556 getValueAt(row, column), false, false, row, viewColumn).
557 getPreferredSize();
558 width = Math.max(width, dim.width);
559 height = dim.height;
560 if((height + getRowMargin()) > getRowHeight(row)){
561 setRowHeight(row, height + getRowMargin());
562 }
563 }
564 }
565
566 int marginWidth = getColumnModel().getColumnMargin();
567 if(marginWidth > 0) width += marginWidth;
568 tCol.setPreferredWidth(width);
569 }
570
571 JCheckBoxMenuItem autoSizeMenuItem;
572 JCheckBoxMenuItem hideMenuItem;
573 JPopupMenu popup;
574 int column;
575 boolean hidden;
576 int colPreferredWidth;
577 int colMaxWidth;
578 int colWidth;
579 Comparator comparator;
580 private static final int HIDDEN_WIDTH = 5;
581 }
582
583
587 protected class DefaultComparator implements Comparator{
588
589 public int compare(Object o1, Object o2){
590 if (o1 == null && o2 == null) {
592 return 0;
593 } else if (o1 == null) { return -1;
595 } else if (o2 == null) {
596 return 1;
597 }
598 int result;
599 if(o1 instanceof Comparable){
600 try {
601 result = ((Comparable)o1).compareTo(o2);
602 } catch(ClassCastException cce) {
603 String s1 = o1.toString();
604 String s2 = o2.toString();
605 result = s1.compareTo(s2);
606 }
607 } else {
608 String s1 = o1.toString();
609 String s2 = o2.toString();
610 result = s1.compareTo(s2);
611 }
612
613 return result;
614 }
615 }
616 protected SortingModel sortingModel;
617 protected DefaultComparator defaultComparator;
618
619
622 protected int sortedColumn = -1;
623
624
627 protected boolean ascending = true;
628
631 protected boolean sortable = true;
632
633
636 protected List columnData;
637 }
638