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 import gate.util.ObjectComparator;
34
35
48 public class XJTable extends JTable{
49
50 public XJTable(){
51 super();
52 }
53
54 public XJTable(TableModel model){
55 super();
56 setModel(model);
57 }
58
59 public void setModel(TableModel dataModel) {
60 sortingModel = new SortingModel(dataModel);
61 super.setModel(sortingModel);
62 newColumns();
63 }
64
65
68 protected void newColumns(){
69 columnData = new ArrayList(dataModel.getColumnCount());
70 for(int i = 0; i < dataModel.getColumnCount(); i++)
71 columnData.add(new ColumnData(i));
72 adjustSizes();
73 }
74
75
78 public void updateUI() {
79 super.updateUI();
80 getTableHeader().addMouseListener(new HeaderMouseListener());
81 adjustSizes();
82 }
83
84 public Dimension getPreferredSize(){
85 int width = 0;
86 for(int i = 0; i < getColumnModel().getColumnCount(); i++)
87 width += getColumnModel().getColumn(i).getPreferredWidth();
88 int height = 0;
89 for(int i = 0; i < getRowCount(); i++)
90 height += getRowHeight(i);
91 return new Dimension(width, height);
92 }
93
94 public Dimension getPreferredScrollableViewportSize(){
95 return getPreferredSize();
96 }
97
98
103 public void adjustSizes(){
104 Iterator colIter = columnData.iterator();
105 while(colIter.hasNext()){
106 ((ColumnData)colIter.next()).adjustColumnWidth();
107 }
108 repaint();
109 }
110
111
116 public int rowModelToView(int modelRow){
117 return sortingModel.sourceToTarget(modelRow);
118 }
119
120
123 public boolean isAscending() {
124 return ascending;
125 }
126
127
132 public boolean isColumnHidden(int columnIndex){
133 return ((ColumnData)columnData.get(columnIndex)).isHidden();
134 }
135
136
139 public void setAscending(boolean ascending) {
140 this.ascending = ascending;
141 }
142
147 public int rowViewToModel(int viewRow){
148 return sortingModel.targetToSource(viewRow);
149 }
150
151
157 public void setComparator(int column, Comparator comparator){
158 ((ColumnData)columnData.get(column)).comparator = comparator;
159 }
160
161
164 public boolean isSortable(){
165 return sortable;
166 }
167
170 public void setSortable(boolean sortable){
171 this.sortable = sortable;
172 }
173
176 public int getSortedColumn(){
177 return sortedColumn;
178 }
179
182 public void setSortedColumn(int sortColumn){
183 this.sortedColumn = sortColumn;
184 }
185
186
189 public int getTableRow(int modelRow){
190 return sortingModel.sourceToTarget(modelRow);
191 }
192
193
198 protected class SortingModel extends AbstractTableModel
199 implements TableModelListener{
200
201 public SortingModel(TableModel sourceModel){
202 init(sourceModel);
203 }
204
205 protected void init(TableModel sourceModel){
206 if(this.sourceModel != null)
207 this.sourceModel.removeTableModelListener(this);
208 this.sourceModel = sourceModel;
209 int size = sourceModel.getRowCount();
211 sourceToTarget = new int[size];
212 targetToSource = new int[size];
213 for(int i = 0; i < size; i++) {
214 sourceToTarget[i] = i;
215 targetToSource[i] = i;
216 }
217 sourceModel.addTableModelListener(this);
218 if(isSortable() && sortedColumn == -1) setSortedColumn(0);
219 }
220
221
224 public void tableChanged(TableModelEvent e){
225 int type = e.getType();
226 int firstRow = e.getFirstRow();
227 int lastRow = e.getLastRow();
228 int column = e.getColumn();
229
230
234 switch(type){
235 case TableModelEvent.UPDATE:
236 if(firstRow == TableModelEvent.HEADER_ROW){
237 init(sourceModel);
239 fireTableStructureChanged();
240 if(isSortable()) sort();
241 newColumns();
242 adjustSizes();
243 }else if(lastRow == Integer.MAX_VALUE){
244 init(sourceModel);
246 fireTableDataChanged();
247 if(isSortable()) sort();
248 adjustSizes();
249 }else{
250 if(isSortable() &&
253 (column == sortedColumn ||
254 column == TableModelEvent.ALL_COLUMNS)){
255 sort();
257 }else{
258 fireTableChanged(new TableModelEvent(this,
259 sourceToTarget(firstRow),
260 sourceToTarget(lastRow), column, type));
261
262 }
263 if(column == TableModelEvent.ALL_COLUMNS){
265 adjustSizes();
266 }else{
267 ((ColumnData)columnData.get(column)).adjustColumnWidth();
268 }
269 }
270 break;
271 case TableModelEvent.INSERT:
272 init(sourceModel);
274 if(firstRow == lastRow){
275 fireTableChanged(new TableModelEvent(this,
276 sourceToTarget(firstRow),
277 sourceToTarget(lastRow), column, type));
278 }else{
279 fireTableDataChanged();
281 }
282 if(isSortable()) sort();
283 if(column == TableModelEvent.ALL_COLUMNS) adjustSizes();
285 else ((ColumnData)columnData.get(column)).adjustColumnWidth();
286 break;
287 case TableModelEvent.DELETE:
288 init(sourceModel);
290 fireTableDataChanged();
291 if(isSortable()) sort();
292 }
293 }
294
295 public int getRowCount(){
296 return sourceModel.getRowCount();
297 }
298
299 public int getColumnCount(){
300 return sourceModel.getColumnCount();
301 }
302
303 public String getColumnName(int columnIndex){
304 return sourceModel.getColumnName(columnIndex);
305 }
306 public Class getColumnClass(int columnIndex){
307 return sourceModel.getColumnClass(columnIndex);
308 }
309
310 public boolean isCellEditable(int rowIndex, int columnIndex){
311 return sourceModel.isCellEditable(targetToSource(rowIndex),
312 columnIndex);
313 }
314 public void setValueAt(Object aValue, int rowIndex, int columnIndex){
315 sourceModel.setValueAt(aValue, targetToSource(rowIndex),
316 columnIndex);
317 }
318 public Object getValueAt(int row, int column){
319 return sourceModel.getValueAt(targetToSource(row), column);
320 }
321
322
327 public void sort(){
328 int[] rows = getSelectedRows();
330 for(int i = 0; i < rows.length; i++) rows[i] = rowViewToModel(rows[i]);
332 clearSelection();
333
334 List sourceData = new ArrayList(sourceModel.getRowCount());
335 for(int i = 0; i < sourceModel.getRowCount(); i++){
337 sourceData.add(sourceModel.getValueAt(i, sortedColumn));
338 }
339 Comparator comparator = ((ColumnData)columnData.
341 get(sortedColumn)).comparator;
342 if(comparator == null){
343 if(defaultComparator == null)
345 defaultComparator = new ObjectComparator();
346 comparator = defaultComparator;
347 }
348 for(int i = 0; i < sourceData.size() - 1; i++){
349 for(int j = i + 1; j < sourceData.size(); j++){
350 Object o1 = sourceData.get(targetToSource(i));
351 Object o2 = sourceData.get(targetToSource(j));
352 boolean swap = ascending ?
353 (comparator.compare(o1, o2) > 0) :
354 (comparator.compare(o1, o2) < 0);
355 if(swap){
356 int aux = targetToSource[i];
357 targetToSource[i] = targetToSource[j];
358 targetToSource[j] = aux;
359
360 sourceToTarget[targetToSource[i]] = i;
361 sourceToTarget[targetToSource[j]] = j;
362 }
363 }
364 }
365 fireTableRowsUpdated(0, sourceData.size() -1);
366 for(int i = 0; i < rows.length; i++){
369 rows[i] = rowModelToView(rows[i]);
370 getSelectionModel().addSelectionInterval(rows[i], rows[i]);
371 }
372 }
373
374
375
381 public int sourceToTarget(int index){
382 return sourceToTarget[index];
383 }
384
385
392 public int targetToSource(int index){
393 return targetToSource[index];
394 }
395
396
399 protected void buildTargetToSourceIndex(){
400 targetToSource = new int[sourceToTarget.length];
401 for(int i = 0; i < sourceToTarget.length; i++)
402 targetToSource[sourceToTarget[i]] = i;
403 }
404
405
408 protected int[] sourceToTarget;
409
410
413 protected int[] targetToSource;
414
415 protected TableModel sourceModel;
416 }
417
418 protected class HeaderMouseListener extends MouseAdapter{
419 public HeaderMouseListener(){
420 }
421
422 public void mouseClicked(MouseEvent e){
423 process(e);
424 }
425
426 public void mousePressed(MouseEvent e){
427 process(e);
428 }
429
430 public void mouseReleased(MouseEvent e){
431 process(e);
432 }
433
434 protected void process(MouseEvent e){
435 int viewColumn = columnModel.getColumnIndexAtX(e.getX());
436 final int column = convertColumnIndexToModel(viewColumn);
437 ColumnData cData = (ColumnData)columnData.get(column);
438 if((e.getID() == MouseEvent.MOUSE_PRESSED ||
439 e.getID() == MouseEvent.MOUSE_RELEASED) &&
440 e.isPopupTrigger()){
441 cData.popup.show(e.getComponent(), e.getX(), e.getY());
443 }else if(e.getID() == MouseEvent.MOUSE_CLICKED &&
444 e.getButton() == MouseEvent.BUTTON1){
445 if(e.getClickCount() >= 2){
447 if(singleClickTimer != null){
449 singleClickTimer.stop();
450 singleClickTimer = null;
451 }
452 cData.adjustColumnWidth();
453 }else {
454 singleClickTimer = new Timer(CLICK_DELAY, new ActionListener(){
456 public void actionPerformed(ActionEvent evt){
457 if(sortable && column != -1) {
459 ascending = (column == sortedColumn) ? !ascending : true;
460 sortedColumn = column;
461 sortingModel.sort();
462 }
463 }
464 });
465 singleClickTimer.setRepeats(false);
466 singleClickTimer.start();
467 }
468 }
469 }
470
474 private static final int CLICK_DELAY = 300;
475 protected Timer singleClickTimer;
476 }
477
478 protected class ColumnData{
479 public ColumnData(int column){
480 this.column = column;
481 popup = new JPopupMenu();
482 hideMenuItem = new JCheckBoxMenuItem("Hide", false);
483 popup.add(hideMenuItem);
484 autoSizeMenuItem = new JCheckBoxMenuItem("Autosize", true);
485 hidden = false;
487 initListeners();
488 }
489
490 protected void initListeners(){
491 hideMenuItem.addActionListener(new ActionListener(){
492 public void actionPerformed(ActionEvent evt){
493 TableColumn tCol = getColumnModel().getColumn(column);
494 if(hideMenuItem.isSelected()){
495 colWidth = tCol.getWidth();
497 colPreferredWidth = tCol.getPreferredWidth();
498 colMaxWidth = tCol.getMaxWidth();
499 tCol.setPreferredWidth(HIDDEN_WIDTH);
500 tCol.setMaxWidth(HIDDEN_WIDTH);
501 tCol.setWidth(HIDDEN_WIDTH);
502 }else{
503 tCol.setMaxWidth(colMaxWidth);
505 tCol.setPreferredWidth(colPreferredWidth);
506 tCol.setWidth(colWidth);
507 }
508 }
509 });
510
511 autoSizeMenuItem.addActionListener(new ActionListener(){
512 public void actionPerformed(ActionEvent evt){
513 if(autoSizeMenuItem.isSelected()){
514 adjustColumnWidth();
516 }
517 }
518 });
519
520 }
521
522 public boolean isHidden(){
523 return hideMenuItem.isSelected();
524 }
525
526 public void adjustColumnWidth(){
527 int viewColumn = convertColumnIndexToView(column);
528 TableColumn tCol = getColumnModel().getColumn(column);
529 Dimension dim;
530 int width, height;
531 TableCellRenderer renderer;
532 if(getTableHeader() != null){
534 renderer = tCol.getHeaderRenderer();
535 if(renderer == null) renderer = getTableHeader().getDefaultRenderer();
536 dim = renderer.getTableCellRendererComponent(XJTable.this,
537 tCol.getHeaderValue(), true, true ,0 , viewColumn).
538 getPreferredSize();
539 width = dim.width;
540 height = dim.height;
542 if(height + getRowMargin() > getTableHeader().getPreferredSize().height){
543 getTableHeader().setPreferredSize(
544 new Dimension(getTableHeader().getPreferredSize().width,
545 height));
546 }
547 int marginWidth = getColumnModel().getColumnMargin();
548 if(marginWidth > 0) width += marginWidth;
549 }else{
550 width = 0;
551 }
552 renderer = tCol.getCellRenderer();
553 if(renderer == null) renderer = getDefaultRenderer(getColumnClass(column));
554 for(int row = 0; row < getRowCount(); row ++){
555 if(renderer != null){
556 dim = renderer. getTableCellRendererComponent(XJTable.this,
557 getValueAt(row, column), false, false, row, viewColumn).
558 getPreferredSize();
559 width = Math.max(width, dim.width);
560 height = dim.height;
561 if((height + getRowMargin()) > getRowHeight(row)){
562 setRowHeight(row, height + getRowMargin());
563 }
564 }
565 }
566
567 int marginWidth = getColumnModel().getColumnMargin();
568 if(marginWidth > 0) width += marginWidth;
569 tCol.setPreferredWidth(width);
570 }
571
572 JCheckBoxMenuItem autoSizeMenuItem;
573 JCheckBoxMenuItem hideMenuItem;
574 JPopupMenu popup;
575 int column;
576 boolean hidden;
577 int colPreferredWidth;
578 int colMaxWidth;
579 int colWidth;
580 Comparator comparator;
581 private static final int HIDDEN_WIDTH = 5;
582 }
583
584 protected SortingModel sortingModel;
585 protected ObjectComparator defaultComparator;
586
587
590 protected int sortedColumn = -1;
591
592
595 protected boolean ascending = true;
596
599 protected boolean sortable = true;
600
601
604 protected List columnData;
605 }
606