1 package snow.sortabletable; 2 3 4 import javax.swing.table.*; 5 import javax.swing.event.*; 6 import javax.swing.border.*; 7 import javax.swing.*; 8 import java.awt.event.*; 9 import java.awt.*; 10 import java.util.*; 11 import java.util.regex.*; 12 13 14 27 public class SortableTableModel extends AbstractTableModel 28 { 29 final Vector<Integer > sortedRowIndices = new Vector<Integer >(); 33 34 final Vector<Integer > foundBasicIndices = new Vector<Integer >(); 36 37 private FineGrainTableModel basicTableModel; 39 40 public final static Icon SORT_ASC = new SortDirectionSelfDrawingIcon(SortDirectionSelfDrawingIcon.Ascending); 41 public final static Icon SORT_DESC = new SortDirectionSelfDrawingIcon(SortDirectionSelfDrawingIcon.Descending); 42 43 44 45 JTable tableReference; 47 JTable tableRowHeadersReference; 48 int numberOfColumnAsRowHeaders = 0; 49 50 51 final private TreeSet<Integer > selectedColumns = new TreeSet<Integer >(); 54 55 private boolean columnVisibilitiesChangeEnable = true; 56 57 64 public SortableTableModel(FineGrainTableModel basicTableModel) 65 { 66 this(basicTableModel, 0, true); 67 } 68 69 public FineGrainTableModel getBasicTableModel() 70 { 71 return basicTableModel; 72 } 73 74 TableModelListener tableModelListener = null; 75 TableModelChangeListener tableModelChangeListener = null; 76 77 78 85 public SortableTableModel(FineGrainTableModel basicTableModel, 86 int sortedColumn, boolean ascending) 87 { 88 setBasicTableModel(basicTableModel, sortedColumn, ascending); 89 } 90 91 92 public void setBasicTableModel(FineGrainTableModel basicTableModel, 93 int sortedColumn, boolean ascending) 94 { 95 removeOldListeners(); 96 97 this.basicTableModel = basicTableModel; 98 this.sortedColumnInBasicModel = sortedColumn; 99 this.ascending = ascending; 100 101 basicTableModel.addTableModelListener(tableModelListener = new TableModelListener() 102 { 103 public void tableChanged(TableModelEvent e) 105 { 106 createRangeForSorting(); internalSort(); 109 internal_search(); 110 111 fireTableDataChanged(); 113 } 115 }); 116 117 basicTableModel.addModelChangeListener(tableModelChangeListener = new TableModelChangeListener() 118 { 119 public void tableModelWillChange(ChangeEvent e) 120 { 121 storeTableSelection(); 122 } 123 124 public void tableModelHasChanged(ChangeEvent e) 125 { 126 restoreTableSelections(); 127 } 128 }); 129 130 for(int i=0; i<basicTableModel.getColumnCount(); i++) 132 { 133 selectedColumns.add(i); 134 } 135 136 createRangeForSorting(); 138 sort(sortedColumn, ascending); 139 internal_search(); 140 141 142 restoreTableSelections(); 144 } 146 147 public void removeOldListeners() 148 { 149 if(basicTableModel!=null) 150 { 151 basicTableModel.removeTableModelListener(tableModelListener); 152 basicTableModel.removeModelChangeListener(tableModelChangeListener); 153 } 154 155 if(tableHeadClickMouseAdapter!=null) 156 { 157 tableRowHeadersReference.removeMouseListener(tableHeadClickMouseAdapter); 158 } 159 if(tableRefClickMouseAdapter!=null) 160 { 161 tableReference.removeMouseListener(tableRefClickMouseAdapter); 162 } 163 } 164 165 166 169 public synchronized void storeTableSelection() 170 { 171 if(!SwingUtilities.isEventDispatchThread()) 173 { 174 new Throwable ("Must be called from EDT !").printStackTrace(); 175 } 176 177 if(tableReference==null) return; 178 179 int[] sel = tableReference.getSelectedRows(); 181 basicTableModel.clearRowSelection(); 182 for(int i=0; i<sel.length; i++) 183 { 184 int indexThis = sel[i]; 185 int indexBase = getIndexInUnsortedFromTablePos(indexThis); 186 basicTableModel.setRowSelection(indexBase, true); 187 } 188 } 189 190 public synchronized void restoreTableSelections() 191 { 192 if(tableReference==null) return; 193 if(this.getRowCount()==0) return; 194 195 if(!SwingUtilities.isEventDispatchThread()) 196 { 197 new Throwable ("Must be called from EDT !").printStackTrace(); 198 } 199 200 tableReference.getSelectionModel().clearSelection(); 202 int[] sel = basicTableModel.getSelectedRows(); 203 for(int i=0; i<sel.length; i++) 204 { 205 int indexBase = sel[i]; 206 int pos = getIndexInFoundFromBasicIndex(indexBase); 207 208 if(pos>=0 && pos<tableReference.getRowCount()) 209 { 210 if(tableReference.getSelectionModel().getSelectionMode()==ListSelectionModel.SINGLE_SELECTION) 211 { 212 tableReference.setRowSelectionInterval(pos, pos); 213 } 214 else 215 { 216 tableReference.addRowSelectionInterval(pos, pos); 217 } 218 } 219 } 220 221 if(tableReference!=null) tableReference.revalidate(); if(tableRowHeadersReference!=null) tableRowHeadersReference.revalidate(); 224 } 225 226 227 229 private synchronized void createRangeForSorting() 230 { 231 if(sortedRowIndices.size()==basicTableModel.getRowCount()) return; 233 234 synchronized(this) 235 { 236 sortedRowIndices.removeAllElements(); 237 for(int i=0; i<basicTableModel.getRowCount(); i++) 238 { 239 sortedRowIndices.addElement(i); 240 } 241 } 242 } 243 244 245 247 public synchronized boolean isSearchActive() 248 { 249 if(search1.length()>0) return true; 250 if(search2!=null) return true; 251 return false; 252 } 253 254 String search1 = ""; 255 String search2 = null; 256 boolean useRegEx = false; 257 Pattern p1 = null; 258 Pattern p2 = null; 259 int searchColumn = -1; boolean andSearch = true; 261 262 public synchronized void search(String str1, String str2, boolean useRegEx) 263 { 264 if(!SwingUtilities.isEventDispatchThread()) 265 { 266 new Throwable ("Must be called from EDT !").printStackTrace(); 267 } 268 269 search1 = str1; 270 search2 = (str2!=null && str2.equals("") ? null : str2); 271 this.useRegEx = useRegEx; 272 p1=null; 273 p2=null; 274 searchColumn = -1; 275 andSearch = true; 276 277 if(useRegEx) 278 { 279 try 280 { 281 p1 = Pattern.compile(str1); 282 } 283 catch(Exception e){} 284 285 try 286 { 287 p2 = Pattern.compile(str1); 288 } 289 catch(Exception e){} 290 } 291 292 storeTableSelection(); 293 294 internal_search(); 295 fireTableDataChanged(); 296 297 restoreTableSelections(); 298 } 299 300 public synchronized void advancedSearch(String str1, String str2, boolean andSearch, boolean useRegEx, int column) 301 { 302 search1 = str1; 304 search2 = (str2!=null && str2.equals("") ? null : str2); 305 306 this.andSearch = andSearch; 307 this.searchColumn = column; 308 this.andSearch = andSearch; 309 this.useRegEx = useRegEx; 310 311 if(column==-1) 312 { 313 search(str1, str2, useRegEx); 314 return; 315 } 316 317 p1=null; 318 p2=null; 319 320 if(useRegEx) 321 { 322 try 323 { 324 p1 = Pattern.compile(str1); 325 } 326 catch(Exception e){} 327 328 try 329 { 330 p2 = Pattern.compile(str1); 331 } 332 catch(Exception e){} 333 } 334 335 storeTableSelection(); 336 337 internal_search(); 338 fireTableDataChanged(); 339 340 restoreTableSelections(); 341 342 } 343 344 348 private synchronized void internal_search() 349 { 350 synchronized(this) 351 { 352 foundBasicIndices.removeAllElements(); 353 for(int i=0; i<basicTableModel.getRowCount();i++) 354 { 355 int basicIndex = getIndexInUnsortedModel(i); 356 357 boolean hit = basicTableModel.hitForTextSearch(basicIndex, searchColumn, search1, p1); 358 359 if(!hit && andSearch) continue; 361 if(search2!=null && !search2.equals("")) 362 { 363 boolean hit2 = basicTableModel.hitForTextSearch(basicIndex, searchColumn, search2, p2); 364 if(!hit2 && andSearch) continue; 366 hit = hit || hit2; 367 } 368 369 if(hit) 370 { 371 foundBasicIndices.addElement(basicIndex); 372 } 373 } 374 } 375 } 376 377 378 381 public void installGUI(JTable tHead, JTable table) 382 { 383 tHead.setSelectionModel( table.getSelectionModel() ); 385 386 this.tableReference = table; 387 this.tableRowHeadersReference = tHead; 388 389 numberOfColumnAsRowHeaders = tHead.getColumnCount(); 390 391 setHeaders(); 393 installHeaderClickListeners(); 394 395 } 396 397 398 400 public void installGUI(JTable _tableReference) 401 { 402 this.tableReference = _tableReference; 403 setHeaders(); 405 installHeaderClickListeners(); 406 407 restoreTableSelections(); 410 411 int fontSize = UIManager.getFont("Label.font").getSize(); 412 for(int i=0; i<tableReference.getColumnCount(); i++) 413 { 414 int w = this.basicTableModel.getPreferredColumnWidth(i); 415 if(w>0) 416 { 417 tableReference.getColumnModel().getColumn(i).setPreferredWidth(w*fontSize); 418 } 419 } 420 } 421 422 423 public void setColumnVisibilityToggle(boolean enable) 424 { 425 columnVisibilitiesChangeEnable = enable; 426 } 427 428 429 432 public void setPreferredColumnSizesFromModel() 433 { 434 if(tableReference==null) return; 435 436 int fontSize = UIManager.getFont("Label.font").getSize(); 437 for(int i=0; i<tableReference.getColumnCount(); i++) 438 { 439 int pos = this.getColumnForViewIndex(i); 440 int w = this.basicTableModel.getPreferredColumnWidth(pos); 441 if(w>0) 442 { 443 tableReference.getColumnModel().getColumn(i).setPreferredWidth(w*fontSize); 444 } 445 } 446 447 } 448 449 450 MouseAdapter tableRefClickMouseAdapter = null; 451 MouseAdapter tableHeadClickMouseAdapter = null; 452 453 454 456 private void installHeaderClickListeners() 457 { 458 if(tableRefClickMouseAdapter!=null) 459 { 460 tableReference.removeMouseListener(tableRefClickMouseAdapter); 461 } 462 463 tableReference.getTableHeader().addMouseListener(tableRefClickMouseAdapter = new MouseAdapter() 465 { 466 467 @Override public void mouseReleased(MouseEvent e) 468 { 469 if(e.isPopupTrigger() && columnVisibilitiesChangeEnable) 470 { 471 showColumnSelectionPopup(e); 473 } 475 } 476 477 478 public void mousePressed(MouseEvent e) 479 { 480 if(e.isPopupTrigger() && columnVisibilitiesChangeEnable) 481 { 482 showColumnSelectionPopup(e); 484 } 486 } 487 488 public void mouseClicked(MouseEvent e) 489 { 490 492 int columnView = tableReference.getColumnModel().getColumnIndexAtX(e.getX()); 493 int columnModel = tableReference.convertColumnIndexToModel(columnView); 494 495 if (columnModel >= 0) 496 { 497 499 int columnIndexInModel = getModelIndexForClickedColumn(columnModel); 501 502 if (sortedColumnInBasicModel == columnIndexInModel + numberOfColumnAsRowHeaders) 504 { 505 ascending = !ascending; 506 } 507 else 508 { 509 ascending = true; 510 } 511 sortedColumnInBasicModel = columnIndexInModel + numberOfColumnAsRowHeaders; 512 514 storeTableSelection(); 515 516 internalSort(); 517 internal_search(); 518 519 fireTableDataChanged(); 520 521 restoreTableSelections(); 522 523 setHeaders(); if(tableReference!=null) 526 { 527 tableReference.getTableHeader().repaint(); } 529 if(tableRowHeadersReference!=null) 530 { 531 tableRowHeadersReference.getTableHeader().repaint(); } 533 } 535 } 536 }); 537 538 539 if(tableRowHeadersReference==null) return; 542 543 if(tableHeadClickMouseAdapter!=null) 544 { 545 tableRowHeadersReference.removeMouseListener(tableHeadClickMouseAdapter); 546 } 547 548 549 tableRowHeadersReference.getTableHeader().addMouseListener(tableHeadClickMouseAdapter = new MouseAdapter() 550 { 551 @Override public void mouseClicked(MouseEvent e) 552 { 553 int columnView = tableRowHeadersReference.getColumnModel().getColumnIndexAtX(e.getX()); 555 int columnModel = tableRowHeadersReference.convertColumnIndexToModel(columnView); 556 557 559 if (columnModel >= 0) 560 { 561 if (sortedColumnInBasicModel==columnModel) 563 { 564 ascending = !ascending; 565 } 566 else 567 { 568 ascending = true; 569 } 570 sortedColumnInBasicModel = columnModel; 571 573 576 storeTableSelection(); 577 578 internalSort(); 579 internal_search(); 580 581 fireTableDataChanged(); 582 583 restoreTableSelections(); 584 585 setHeaders(); 586 if(tableReference!=null) tableReference.getTableHeader().repaint(); if(tableRowHeadersReference!=null) tableRowHeadersReference.getTableHeader().repaint(); } 591 } 592 }); 593 } 594 596 public int[] getVisibleColumnsIndex() 597 { 598 synchronized(this) 599 { 600 int[] rep = new int[selectedColumns.size()]; 601 Integer [] sc = selectedColumns.toArray(new Integer [selectedColumns.size()]); 602 for(int i=0; i<selectedColumns.size(); i++) 603 { 604 rep[i] = sc[i]; 605 } 606 return rep; 607 } 608 } 609 610 private int getModelIndexForClickedColumn(int col) 611 { 612 int[] visibleIndex = getVisibleColumnsIndex(); 613 return getVisibleColumnsIndex()[col]; 614 } 615 616 public void setVisibleColumns(int[] cols) 617 { 618 synchronized(this) 619 { 620 selectedColumns.clear(); 621 for(int i=0; i<cols.length; i++) 622 { 623 if(cols[i]<this.basicTableModel.getColumnCount()) 624 { 625 selectedColumns.add(cols[i]); 626 } 627 } 628 } 629 storeTableSelection(); 630 fireTableStructureChanged(); restoreTableSelections(); 632 setHeaders(); 633 634 635 } 636 637 public boolean isColumnVisible(int column) 638 { 639 return selectedColumns.contains(column); 640 } 641 642 644 public void setColumnVisible(int column, boolean visible) 645 { 646 if(visible) 647 { 648 selectedColumns.add(column); 649 } 650 else 651 { 652 if(selectedColumns.size()>1) 654 { 655 selectedColumns.remove(column); 656 } 657 } 658 659 storeTableSelection(); 660 fireTableStructureChanged(); restoreTableSelections(); 662 setHeaders(); 663 } 664 665 private void showColumnSelectionPopup(MouseEvent e) 666 { 667 JPopupMenu popup = new JPopupMenu("View Columns"); 668 popup.add(new JLabel("View")); 669 popup.addSeparator(); 670 671 for(int i=numberOfColumnAsRowHeaders; i<this.basicTableModel.getColumnCount(); i++) 673 { 674 String name = basicTableModel.getColumnName(i); 675 final Integer index = new Integer (i); 676 final JCheckBoxMenuItem cb = new JCheckBoxMenuItem(name, selectedColumns.contains(index)); 677 popup.add(cb); 678 cb.addActionListener(new ActionListener() 679 { 680 public void actionPerformed(ActionEvent ae) 681 { 682 setColumnVisible(index, cb.isSelected()); 683 } 684 }); 685 } 686 687 688 popup.show(tableReference.getTableHeader(), e.getX(), e.getY()); 689 } 690 691 692 695 protected void setHeaders() 696 { 697 if(tableReference == null) return; 698 699 setHeadersForIndexTable(); 700 701 703 if (tableRowHeadersReference!=null) 705 { 706 for (int i=0; i<numberOfColumnAsRowHeaders; i++) 707 { 708 int indexModel = i; 709 int indexView = tableRowHeadersReference.convertColumnIndexToView(i); 710 TableColumn column = tableRowHeadersReference.getColumnModel().getColumn(indexView); 711 DefaultTableCellRenderer headerRenderer = new DefaultTableCellRenderer(); 712 headerRenderer.setBackground(UIManager.getColor("TableHeader.background")); 713 if(indexModel==sortedColumnInBasicModel) 714 { 715 if (ascending) headerRenderer.setIcon(getAscIcon(indexModel)); 716 else headerRenderer.setIcon(getDescIcon(indexModel)); 717 } 718 else 719 { 720 headerRenderer.setIcon(getNoSortIcon(indexModel)); 721 } 722 723 tableRowHeadersReference.getColumnModel().getColumn(indexView).setHeaderRenderer(headerRenderer); 724 725 tableRowHeadersReference.getTableHeader().repaint(); 727 } 728 } 729 730 732 for (int i=0; i<tableReference.getColumnCount(); i++) 733 { 734 int indexModel = i; 735 int indexView = tableReference.convertColumnIndexToView(i); 736 TableColumn column = tableReference.getColumnModel().getColumn(indexView); 737 738 DefaultTableCellRenderer headerRenderer = new DefaultTableCellRenderer(); 739 headerRenderer.setBackground(UIManager.getColor("TableHeader.background")); 740 741 int sortedColViewIndex = getColumnForViewIndex(i); 742 743 748 if(sortedColViewIndex+numberOfColumnAsRowHeaders == sortedColumnInBasicModel) 749 { 750 753 if (ascending) 754 { 755 headerRenderer.setIcon(getAscIcon(sortedColViewIndex)); 756 } 757 else 758 { 759 headerRenderer.setIcon(getDescIcon(sortedColViewIndex)); 760 } 761 } 762 else 763 { 764 headerRenderer.setIcon(getNoSortIcon(sortedColViewIndex)); 765 } 766 767 tableReference.getColumnModel().getColumn(indexView).setHeaderRenderer(headerRenderer); 768 tableReference.getTableHeader().repaint(); 770 } 771 } 772 773 774 private void setHeadersForIndexTable() 775 { 776 if(tableRowHeadersReference == null) return; 777 778 JTableHeader tableHeader = tableRowHeadersReference.getTableHeader(); 779 for (int i=0; i<tableRowHeadersReference.getColumnCount(); i++) 780 { 781 int indexModel = i; 782 int indexView = tableRowHeadersReference.convertColumnIndexToView(i); 783 784 TableColumn column = tableRowHeadersReference.getColumnModel().getColumn(indexView); 785 DefaultTableCellRenderer headerRenderer = new DefaultTableCellRenderer(); 786 headerRenderer.setBackground(UIManager.getColor("TableHeader.background")); 787 788 if(indexModel==sortedColumnInBasicModel) 789 { 790 if (ascending) 791 { 792 headerRenderer.setIcon(getAscIcon(indexModel)); 793 } 794 else 795 { 796 headerRenderer.setIcon(getDescIcon(indexModel)); 797 } 798 } 799 else 800 { 801 headerRenderer.setIcon(getNoSortIcon(indexModel)); 802 } 803 804 tableRowHeadersReference.getColumnModel().getColumn(indexView).setHeaderRenderer(headerRenderer); 805 } 807 } 808 809 int sortedColumnInBasicModel = 0; 811 boolean ascending = true; 812 813 public void sort(int column, boolean ascending) 814 { 815 synchronized(this) 816 { 817 this.sortedColumnInBasicModel = column; 818 this.ascending = ascending; 819 internalSort(); 820 821 fireTableDataChanged(); 823 } 824 } 825 826 828 public boolean getSortOrderIsAscending() { return ascending;} 829 830 832 public int getSortedColumn() { return sortedColumnInBasicModel; } 833 834 public void setSortedColumnAndOrder(int col, boolean ascending) 835 { 836 this.sortedColumnInBasicModel = col; 837 this.ascending = ascending; 838 } 839 840 private void internalSort() 841 { 842 Comparator<Integer > comp = new Comparator<Integer >() 843 { 844 public int compare(Integer ind1, Integer ind2) 845 { 846 if(ascending) 847 { 848 return basicTableModel.compareForColumnSort(ind1, ind2, sortedColumnInBasicModel); 849 } 850 else 851 { 852 return basicTableModel.compareForColumnSort(ind2, ind1, sortedColumnInBasicModel); 853 } 854 } 855 }; 856 Collections.sort(sortedRowIndices, comp); 858 } 860 861 865 866 869 private int getIndexInUnsortedModel(int pos) 870 { 871 if(pos==-1) return -1; 872 if(pos>=sortedRowIndices.size()) return -1; 873 874 return sortedRowIndices.elementAt(pos); 875 } 876 877 879 public int getIndexInUnsortedFromTablePos(int tablePos) 880 { 881 if(tablePos==-1) return -1; 882 if(tablePos>=foundBasicIndices.size()) 883 { 884 return -1; 886 } 887 888 return foundBasicIndices.elementAt(tablePos); 889 } 890 891 892 894 public int getIndexInFoundFromBasicIndex(int basicIndex) 895 { 896 if(basicIndex==-1) return -1; 897 if(basicIndex>=0 && basicIndex<basicTableModel.getRowCount()) 898 { 899 int pos = foundBasicIndices.indexOf(basicIndex); 900 return pos; 901 } 902 else 903 { 904 return -1; 905 } 906 } 907 908 public Object getValueAt(int row, int col) 909 { 910 int pos = getIndexInUnsortedFromTablePos(row); 911 return basicTableModel.getValueAt(pos, getColumnForViewIndex(col)); 912 } 913 914 public int getRowCount() 915 { 916 return foundBasicIndices.size(); 918 } 920 921 922 public int getColumnCount() 923 { 924 return selectedColumns.size(); 925 } 927 928 931 public int getColumnForViewIndex(int viewCol) 932 { 933 synchronized(this) 934 { 935 int pos = -1; 936 Iterator it = selectedColumns.iterator(); 938 while(it.hasNext()) 939 { 940 Integer ind = (Integer ) it.next(); 941 pos++; 942 if(pos==viewCol) 943 { 944 return ind; 945 } 946 } 947 return -1; 948 } 949 950 961 } 962 963 964 965 966 967 public boolean isCellEditable(int row, int col) 968 { 969 int pos = getIndexInUnsortedFromTablePos(row); 970 return basicTableModel.isCellEditable(pos, getColumnForViewIndex(col)); 971 } 972 973 public void setValueAt(Object val, int row, int col) 974 { 975 int pos = getIndexInUnsortedFromTablePos(row); 976 basicTableModel.setValueAt(val, pos, getColumnForViewIndex(col)); 977 } 978 979 public Class getColumnClass ( int column ) 980 { 981 return basicTableModel.getColumnClass(getColumnForViewIndex(column)); 982 } 983 984 public String getColumnName(int column) 985 { 986 return basicTableModel.getColumnName(getColumnForViewIndex(column)); 987 } 988 989 990 991 996 protected Icon getAscIcon( final int column ) { return(SORT_ASC); } 997 protected Icon getDescIcon( final int column ) { return(SORT_DESC); } 998 protected Icon getNoSortIcon( final int column ) { return(null); } 999 1000 1001 1002} | Popular Tags |