001: package snow.sortabletable;
002:
003: import javax.swing.table.*;
004: import java.util.*;
005: import javax.swing.event.*;
006: import javax.swing.*;
007: import java.awt.event.*;
008: import java.awt.*;
009: import java.util.regex.*;
010:
011: /** Wraps a basic model and allow sorting of his columns
012: the basic model remains unchanged. Only this model is sorted !
013: Features :
014: + The selection is maintained after table updates.
015: + Can installGUI() without corrupting model/view separation
016:
017: usage:
018: 1) make a FineGrain table model (like AbstractTableModel...)
019: 2) make this class, pass 1)
020: 3) make a table with this model 2)
021: 4) call installGUI() with the table 3) to allow sorting
022: */
023: public class SortableTableModel extends AbstractTableModel {
024: // these are the indices in the unsirted model corresponding to the position in this
025: // sorted model. element i is for position i in this table
026: // WITHOUT SEARCH HITS
027: final Vector<Integer> sortedRowIndices = new Vector<Integer>();
028:
029: // i-th element is the index in the basic model of the ith found element
030: final Vector<Integer> foundBasicIndices = new Vector<Integer>();
031:
032: // this is the unsorted model
033: private FineGrainTableModel basicTableModel;
034:
035: public final static Icon SORT_ASC = new SortDirectionSelfDrawingIcon(
036: SortDirectionSelfDrawingIcon.Ascending);
037: public final static Icon SORT_DESC = new SortDirectionSelfDrawingIcon(
038: SortDirectionSelfDrawingIcon.Descending);
039:
040: // used to remember selection when table change
041: JTable tableReference;
042: JTable tableRowHeadersReference;
043: int numberOfColumnAsRowHeaders = 0;
044:
045: // contain the basic model column indices to show (selected = visible)
046: // 0, 1, 3, 4 for example if column2 is not visible
047: final private Set<Integer> selectedColumns = new TreeSet<Integer>();
048:
049: private boolean columnVisibilitiesChangeEnable = true;
050:
051: private boolean multiSearch = false;
052: private Query[] queries = null;
053:
054: /** wraps a sortable feature around your basic table model
055:
056: Easy usage:
057: 1) just create your model, pass it here
058: 2) use this model as tablemodel for your jtable.
059: 3) After that, call installGUI and pass your JTable as argument.
060: */
061: public SortableTableModel(FineGrainTableModel basicTableModel) {
062: this (basicTableModel, 0, true);
063: }
064:
065: public FineGrainTableModel getBasicTableModel() {
066: return basicTableModel;
067: }
068:
069: TableModelListener tableModelListener = null;
070: TableModelChangeListener tableModelChangeListener = null;
071:
072: /** wraps a sortable feature around your basic table model
073:
074: Easy usage:
075: 1) just create your model, pass it here
076: 2) use this model as tablemodel for your jtable.
077: 3) After that, call installGUI and pass your JTable as argument.
078: */
079: public SortableTableModel(FineGrainTableModel basicTableModel,
080: int sortedColumn, boolean ascending) {
081: setBasicTableModel(basicTableModel, sortedColumn, ascending);
082: }
083:
084: public void setBasicTableModel(FineGrainTableModel basicTableModel,
085: int sortedColumn, boolean ascending) {
086: removeOldListeners();
087:
088: this .basicTableModel = basicTableModel;
089: this .sortedColumnInBasicModel = sortedColumn;
090: this .ascending = ascending;
091:
092: tableModelListener = new TableModelListener() {
093: // called after the table has changed
094: public void tableChanged(TableModelEvent e) {
095: //System.out.println("received tableChanged event from unsortedModel");
096: createRangeForSorting(); // range 0,...,n-1
097: internalSort();
098: internal_multisearch();
099:
100: // pass the event in same EDT for this listener
101: fireTableDataChanged();
102: //tableChanged(e); // index mismatch... should convert them... ###
103: }
104: };
105: basicTableModel.addTableModelListener(tableModelListener);
106:
107: tableModelChangeListener = new TableModelChangeListener() {
108: public void tableModelWillChange(ChangeEvent e) {
109: storeTableSelection();
110: }
111:
112: public void tableModelHasChanged(ChangeEvent e) {
113: restoreTableSelections();
114: }
115: };
116: basicTableModel
117: .addModelChangeListener(tableModelChangeListener);
118:
119: // all columns are selected
120: for (int i = 0; i < basicTableModel.getColumnCount(); i++) {
121: selectedColumns.add(i);
122: }
123:
124: // initial sort
125: createRangeForSorting();
126: sort(sortedColumn, ascending);
127: internal_multisearch();
128:
129: restoreTableSelections(); // ??? not now, but when table installed !!
130:
131: } // Constructor
132:
133: public void removeOldListeners() {
134: if (basicTableModel != null) {
135: basicTableModel
136: .removeTableModelListener(tableModelListener);
137: basicTableModel
138: .removeModelChangeListener(tableModelChangeListener);
139: }
140:
141: if (tableHeadClickMouseAdapter != null) {
142: tableRowHeadersReference
143: .removeMouseListener(tableHeadClickMouseAdapter);
144: }
145: if (tableRefClickMouseAdapter != null) {
146: tableReference
147: .removeMouseListener(tableRefClickMouseAdapter);
148: }
149: }
150:
151: /** free resource, listeners and so on...
152: */
153: public synchronized void terminate() {
154: removeOldListeners();
155:
156: if (basicTableModel != null) {
157: this .basicTableModel.terminate();
158: basicTableModel = null;
159: }
160: }
161:
162: /** only call this when you terminate the table,
163: to keep selection stored in model (if implemented)
164: */
165: public synchronized void storeTableSelection() {
166: //System.out.println("Store sel");
167: if (!SwingUtilities.isEventDispatchThread()) {
168: new Throwable("Must be called from EDT !")
169: .printStackTrace();
170: }
171:
172: if (tableReference == null)
173: return;
174:
175: // store the selection (store indices of the basic model
176: int[] sel = tableReference.getSelectedRows();
177: basicTableModel.clearRowSelection();
178: for (int i = 0; i < sel.length; i++) {
179: int indexThis = sel[i];
180: int indexBase = getIndexInUnsortedFromTablePos(indexThis);
181: basicTableModel.setRowSelection(indexBase, true);
182: }
183: }
184:
185: private synchronized void restoreTableSelections() {
186: if (tableReference == null)
187: return;
188: if (this .getRowCount() == 0)
189: return;
190: if (!SwingUtilities.isEventDispatchThread()) {
191: new Throwable("Must be called from EDT !")
192: .printStackTrace();
193: }
194:
195: // restore selection
196: tableReference.getSelectionModel().clearSelection();
197: int[] sel = basicTableModel.getSelectedRows();
198: for (int i = 0; i < sel.length; i++) {
199: int indexBase = sel[i];
200: int pos = getIndexInFoundFromBasicIndex(indexBase);
201:
202: if (pos >= 0 && pos < tableReference.getRowCount()) {
203: if (tableReference.getSelectionModel()
204: .getSelectionMode() == ListSelectionModel.SINGLE_SELECTION) {
205: tableReference.setRowSelectionInterval(pos, pos);
206: } else {
207: tableReference.addRowSelectionInterval(pos, pos);
208: }
209: }
210: }
211:
212: // ### needed to display correctly !!!
213: if (tableReference != null)
214: tableReference.revalidate(); // .repaint();
215: if (tableRowHeadersReference != null)
216: tableRowHeadersReference.revalidate();
217: }
218:
219: /** This just creates a range 0.. size-1 used to sort.
220: */
221: private synchronized void createRangeForSorting() {
222: // nothing to do if the size is ok
223: if (sortedRowIndices.size() == basicTableModel.getRowCount())
224: return;
225:
226: synchronized (this ) {
227: sortedRowIndices.removeAllElements();
228: for (int i = 0; i < basicTableModel.getRowCount(); i++) {
229: sortedRowIndices.addElement(i);
230: }
231: }
232: }
233:
234: /** @return true if the search is active.
235: *
236: public synchronized boolean isSearchActive()
237: {
238: if(search1.length()>0) return true;
239: if(search2!=null) return true;
240: return false;
241: }
242: /*
243: String search1 = "";
244: String search2 = null;
245: boolean useRegEx = false;
246: Pattern p1 = null;
247: Pattern p2 = null;
248: int searchColumn = -1; // -1 => all
249: boolean andSearch = true;
250: */
251: public synchronized void search(String str1, String str2,
252: boolean useRegEx) {
253: if (!SwingUtilities.isEventDispatchThread()) {
254: new Throwable("Must be called from EDT !")
255: .printStackTrace();
256: }
257:
258: Query q1 = new Query(-1, str1, Query.Combine.And,
259: (useRegEx ? Query.Comparison.RegEx
260: : Query.Comparison.Contains));
261: Query q2 = new Query(-1, str2, Query.Combine.And,
262: (useRegEx ? Query.Comparison.RegEx
263: : Query.Comparison.Contains));
264:
265: this .multiSearch(new Query[] { q1, q2 });
266: }
267:
268: public synchronized void advancedSearch(String str1, String str2,
269: boolean andSearch, boolean useRegEx, int column) {
270: Query q1 = new Query(column, str1, Query.Combine.And,
271: (useRegEx ? Query.Comparison.RegEx
272: : Query.Comparison.Contains));
273: Query q2 = new Query(column, str2,
274: (andSearch ? Query.Combine.And : Query.Combine.Or),
275: (useRegEx ? Query.Comparison.RegEx
276: : Query.Comparison.Contains));
277:
278: this .multiSearch(new Query[] { q1, q2 });
279: }
280:
281: /** SPECIAL CASE: Table with row index.
282: the two tables have the same selection model.
283: */
284: public void installGUI(JTable tHead, JTable table) {
285: // synchronize the selections => simply use the same models !!
286: tHead.setSelectionModel(table.getSelectionModel());
287:
288: this .tableReference = table;
289: this .tableRowHeadersReference = tHead;
290:
291: numberOfColumnAsRowHeaders = tHead.getColumnCount();
292:
293: // we just install header renderers
294: setHeaders();
295: installHeaderClickListeners();
296:
297: }
298:
299: /** used to render headers...
300: */
301: public void installGUI(JTable _tableReference) {
302: this .tableReference = _tableReference;
303: setHeaders(); // calls setHeadersForIndexTable()
304:
305: installHeaderClickListeners();
306:
307: // this is the first sel, from model, when the three methods
308: // are implemented
309: EventQueue.invokeLater(new Runnable() {
310: public void run() {
311: restoreTableSelections();
312:
313: int fontSize = UIManager.getFont("Label.font")
314: .getSize();
315: for (int i = 0; i < tableReference.getColumnCount(); i++) {
316: int w = basicTableModel.getPreferredColumnWidth(i);
317: if (w > 0) {
318: tableReference.getColumnModel().getColumn(i)
319: .setPreferredWidth(w * fontSize);
320: }
321: }
322:
323: }
324: });
325: }
326:
327: public void setColumnVisibilityToggle(boolean enable) {
328: columnVisibilitiesChangeEnable = enable;
329: }
330:
331: /** actually only for tables without header
332: Must be caled after installGUI !
333: */
334: public void setPreferredColumnSizesFromModel() {
335: if (tableReference == null)
336: return;
337:
338: int fontSize = UIManager.getFont("Label.font").getSize();
339: for (int i = 0; i < tableReference.getColumnCount(); i++) {
340: int pos = this .getColumnForViewIndex(i);
341: int w = this .basicTableModel.getPreferredColumnWidth(pos);
342: if (w > 0) {
343: tableReference.getColumnModel().getColumn(i)
344: .setPreferredWidth(w * fontSize);
345: }
346: }
347:
348: }
349:
350: MouseAdapter tableRefClickMouseAdapter = null;
351: MouseAdapter tableHeadClickMouseAdapter = null;
352:
353: /** Listen to click on the table headers used to toggle sorting.
354: */
355: private void installHeaderClickListeners() {
356: if (tableRefClickMouseAdapter != null) {
357: tableReference
358: .removeMouseListener(tableRefClickMouseAdapter);
359: }
360:
361: // listen to click on the table headers used to toggle sorting
362: tableRefClickMouseAdapter = new MouseAdapter() {
363: @Override
364: public void mouseReleased(MouseEvent e) {
365: if (e.isPopupTrigger()
366: && columnVisibilitiesChangeEnable) {
367: // on windows
368: showColumnSelectionPopup(e);
369: //e.consume();
370: }
371: }
372:
373: @Override
374: public void mousePressed(MouseEvent e) {
375: if (e.isPopupTrigger()
376: && columnVisibilitiesChangeEnable) {
377: // on linux
378: showColumnSelectionPopup(e);
379: //e.consume();
380: }
381: }
382:
383: @Override
384: public void mouseClicked(MouseEvent e) {
385: // [EDT]
386:
387: int columnView = tableReference.getColumnModel()
388: .getColumnIndexAtX(e.getX());
389: int columnModel = tableReference
390: .convertColumnIndexToModel(columnView);
391:
392: if (columnModel >= 0) {
393: //TableColumn tc = tableReference.getColumnModel().getColumn(columnModel);
394:
395: // convert to model taking in account the columns that are not visible
396: int columnIndexInModel = getModelIndexForClickedColumn(columnModel);
397:
398: // if this is the column already selected, invert the order
399: if (sortedColumnInBasicModel == columnIndexInModel
400: + numberOfColumnAsRowHeaders) {
401: ascending = !ascending;
402: } else {
403: ascending = true;
404: }
405: sortedColumnInBasicModel = columnIndexInModel
406: + numberOfColumnAsRowHeaders;
407: //System.out.println("Sorted column in basic model = " + sortedColumnInBasicModel);
408:
409: storeTableSelection();
410:
411: internalSort();
412: internal_multisearch();
413:
414: fireTableDataChanged();
415:
416: restoreTableSelections();
417:
418: setHeaders(); // calls setHeadersForIndexTable()
419: // strange... when not invoked, is sometimes not repainted
420: if (tableReference != null) {
421: tableReference.getTableHeader().repaint(); // we're in the EDT... ok
422: }
423: if (tableRowHeadersReference != null) {
424: tableRowHeadersReference.getTableHeader()
425: .repaint(); // we're in the EDT... ok
426: }
427: //tableHeader.invalidate();
428: }
429: }
430: };
431: tableReference.getTableHeader().addMouseListener(
432: tableRefClickMouseAdapter);
433:
434: // index table
435: //
436: if (tableRowHeadersReference == null)
437: return;
438:
439: if (tableHeadClickMouseAdapter != null) {
440: tableRowHeadersReference
441: .removeMouseListener(tableHeadClickMouseAdapter);
442: }
443:
444: tableRowHeadersReference.getTableHeader().addMouseListener(
445: tableHeadClickMouseAdapter = new MouseAdapter() {
446: @Override
447: public void mouseClicked(MouseEvent e) {
448: //final int column = tableReference.columnAtPoint(new Point(e.getX(), e.getY()));
449: int columnView = tableRowHeadersReference
450: .getColumnModel().getColumnIndexAtX(
451: e.getX());
452: int columnModel = tableRowHeadersReference
453: .convertColumnIndexToModel(columnView);
454:
455: //System.out.println("Click column "+columnModel);
456:
457: if (columnModel >= 0) {
458: //TableColumn tc = tableRowHeadersReference.getColumnModel().getColumn(columnModel);
459: if (sortedColumnInBasicModel == columnModel) {
460: ascending = !ascending;
461: } else {
462: ascending = true;
463: }
464: sortedColumnInBasicModel = columnModel;
465: //System.out.println("Sort column "+sortedColumn);
466:
467: //setHeadersForMainTable();
468: //tableHeader.repaint();
469:
470: storeTableSelection();
471:
472: internalSort();
473: internal_multisearch();
474:
475: fireTableDataChanged();
476:
477: restoreTableSelections();
478:
479: setHeaders();
480: // strange... when not invoked, is sometimes not repainted
481: if (tableReference != null)
482: tableReference.getTableHeader()
483: .repaint(); // we're in the EDT... ok
484: if (tableRowHeadersReference != null)
485: tableRowHeadersReference
486: .getTableHeader().repaint(); // we're in the EDT... ok
487: //tableHeader.invalidate();
488: }
489: }
490: });
491: }
492:
493: /** @return {0,1,3} for example if column 2 is not visible
494: */
495: public int[] getVisibleColumnsIndex() {
496: synchronized (this ) {
497: int[] rep = new int[selectedColumns.size()];
498: Integer[] sc = selectedColumns
499: .toArray(new Integer[selectedColumns.size()]);
500: for (int i = 0; i < selectedColumns.size(); i++) {
501: rep[i] = sc[i].intValue();
502: }
503: return rep;
504: }
505: }
506:
507: private int getModelIndexForClickedColumn(int col) {
508: int[] visibleIndex = getVisibleColumnsIndex();
509: return getVisibleColumnsIndex()[col];
510: }
511:
512: public void setVisibleColumns(int[] cols) {
513: synchronized (this ) {
514: selectedColumns.clear();
515: for (int i = 0; i < cols.length; i++) {
516: if (cols[i] < this .basicTableModel.getColumnCount()) {
517: selectedColumns.add(new Integer(cols[i]));
518: }
519: }
520: }
521: storeTableSelection();
522: fireTableStructureChanged(); // structure has changed, give a TableModelEvent with ROW_HEADERS row as argument
523: restoreTableSelections();
524: setHeaders();
525:
526: }
527:
528: public boolean isColumnVisible(int column) {
529: return selectedColumns.contains(new Integer(column));
530: }
531:
532: /** @param column in the base model
533: */
534: public void setColumnVisible(int column, boolean visible) {
535: if (visible) {
536: selectedColumns.add(column);
537: } else {
538: // avoid zero columns !!!
539: if (selectedColumns.size() > 1) {
540: selectedColumns.remove(column);
541: }
542: }
543:
544: storeTableSelection();
545: fireTableStructureChanged(); // structure has changed, give a TableModelEvent with ROW_HEADERS row as argument
546: restoreTableSelections();
547: setHeaders();
548:
549: // [Feb2007]
550: this .setPreferredColumnSizesFromModel();
551: }
552:
553: /** Must be called in the edt ! */
554: public void setAllColumnsVisible() {
555: for (int i = 0; i < this .getBasicTableModel().getColumnCount(); i++) {
556: selectedColumns.add(i);
557: }
558: storeTableSelection();
559: fireTableStructureChanged(); // structure has changed, give a TableModelEvent with ROW_HEADERS row as argument
560: restoreTableSelections();
561: setHeaders();
562: }
563:
564: private void showColumnSelectionPopup(MouseEvent e) {
565: JPopupMenu popup = new JPopupMenu("View Columns");
566: popup.add(new JLabel(" View"));
567: popup.addSeparator();
568:
569: // numberOfColumnAsRowHeaders first col are always visible
570: for (int i = numberOfColumnAsRowHeaders; i < this .basicTableModel
571: .getColumnCount(); i++) {
572: String name = basicTableModel.getColumnName(i);
573: final Integer index = i;
574: final JCheckBoxMenuItem cb = new JCheckBoxMenuItem(name,
575: selectedColumns.contains(index));
576: popup.add(cb);
577: cb.addActionListener(new ActionListener() {
578: public void actionPerformed(ActionEvent ae) {
579: setColumnVisible(index, cb.isSelected());
580: }
581: });
582: }
583:
584: popup.show(tableReference.getTableHeader(), e.getX(), e.getY());
585: }
586:
587: /** set the headers with sort icons.
588: [Called from EDT]
589: */
590: protected void setHeaders() {
591: if (tableReference == null)
592: return;
593:
594: setHeadersForIndexTable();
595:
596: //System.out.println("SC="+sortedColumn);
597:
598: // for the index table
599: if (tableRowHeadersReference != null) {
600: for (int i = 0; i < numberOfColumnAsRowHeaders; i++) {
601: int indexModel = i;
602: int indexView = tableRowHeadersReference
603: .convertColumnIndexToView(i);
604: //TableColumn column = tableRowHeadersReference.getColumnModel().getColumn(indexView);
605: DefaultTableCellRenderer headerRenderer = new DefaultTableCellRenderer();
606: headerRenderer.setBackground(UIManager
607: .getColor("TableHeader.background"));
608: if (indexModel == sortedColumnInBasicModel) {
609: if (ascending)
610: headerRenderer.setIcon(getAscIcon(indexModel));
611: else
612: headerRenderer.setIcon(getDescIcon(indexModel));
613: } else {
614: headerRenderer.setIcon(getNoSortIcon(indexModel));
615: }
616:
617: tableRowHeadersReference.getColumnModel().getColumn(
618: indexView).setHeaderRenderer(headerRenderer);
619:
620: // ??? needed, otherwise, does not correctly display.
621: tableRowHeadersReference.getTableHeader().repaint();
622: }
623: }
624:
625: // table
626:
627: for (int i = 0; i < tableReference.getColumnCount(); i++) {
628: //int indexModel = i;
629: int indexView = tableReference.convertColumnIndexToView(i);
630: //TableColumn column = tableReference.getColumnModel().getColumn(indexView);
631:
632: DefaultTableCellRenderer headerRenderer = new DefaultTableCellRenderer();
633: headerRenderer.setBackground(UIManager
634: .getColor("TableHeader.background"));
635:
636: int sortedColViewIndex = getColumnForViewIndex(i);
637:
638: if (sortedColViewIndex + numberOfColumnAsRowHeaders == sortedColumnInBasicModel) {
639: if (ascending) {
640: headerRenderer
641: .setIcon(getAscIcon(sortedColViewIndex));
642: } else {
643: headerRenderer
644: .setIcon(getDescIcon(sortedColViewIndex));
645: }
646: } else {
647: headerRenderer
648: .setIcon(getNoSortIcon(sortedColViewIndex));
649: }
650:
651: tableReference.getColumnModel().getColumn(indexView)
652: .setHeaderRenderer(headerRenderer);
653: // ??? needed, otherwise, does not correctly display.
654: tableReference.getTableHeader().repaint();
655: }
656: }
657:
658: private void setHeadersForIndexTable() {
659: if (tableRowHeadersReference == null)
660: return;
661:
662: //JTableHeader tableHeader = tableRowHeadersReference.getTableHeader();
663: for (int i = 0; i < tableRowHeadersReference.getColumnCount(); i++) {
664: int indexModel = i;
665: int indexView = tableRowHeadersReference
666: .convertColumnIndexToView(i);
667:
668: //TableColumn column = tableRowHeadersReference.getColumnModel().getColumn(indexView);
669: DefaultTableCellRenderer headerRenderer = new DefaultTableCellRenderer();
670: headerRenderer.setBackground(UIManager
671: .getColor("TableHeader.background"));
672:
673: if (indexModel == sortedColumnInBasicModel) {
674: if (ascending) {
675: headerRenderer.setIcon(getAscIcon(indexModel));
676: } else {
677: headerRenderer.setIcon(getDescIcon(indexModel));
678: }
679: } else {
680: headerRenderer.setIcon(getNoSortIcon(indexModel));
681: }
682:
683: tableRowHeadersReference.getColumnModel().getColumn(
684: indexView).setHeaderRenderer(headerRenderer);
685: //tableReference.repaint();
686: }
687: }
688:
689: // keep in mind what sorting is wanted, because each update must re-sort
690: int sortedColumnInBasicModel = 0;
691: boolean ascending = true;
692:
693: public void sort(int column, boolean ascending) {
694: synchronized (this ) {
695: this .sortedColumnInBasicModel = column;
696: this .ascending = ascending;
697: internalSort();
698:
699: // pass the event in same EDT for this listener
700: fireTableDataChanged();
701: }
702: }
703:
704: /** @return true if the actual sorting order is ascending (default).
705: */
706: public boolean getSortOrderIsAscending() {
707: return ascending;
708: }
709:
710: /** @return index in the basic tabel model.
711: */
712: public int getSortedColumn() {
713: return sortedColumnInBasicModel;
714: }
715:
716: public void setSortedColumnAndOrder(int col, boolean ascending) {
717: this .sortedColumnInBasicModel = col;
718: this .ascending = ascending;
719: }
720:
721: private void internalSort() {
722: Comparator<Integer> comp = new Comparator<Integer>() {
723: public int compare(Integer ind1, Integer ind2) {
724: //int ind1 = ((Integer) i1).intValue();
725: //int ind2 = ((Integer) i2).intValue();
726:
727: if (ascending) {
728: return basicTableModel.compareForColumnSort(ind1,
729: ind2, sortedColumnInBasicModel);
730: } else {
731: return basicTableModel.compareForColumnSort(ind2,
732: ind1, sortedColumnInBasicModel);
733: }
734: }
735: };
736: //System.out.println("Sort start");
737: Collections.sort(sortedRowIndices, comp);
738: //System.out.println("Sort end");
739: }
740:
741: //
742: // TableModel
743: //
744:
745: /** @param pos id the index in this table,
746: @return the position of the element in the unsorted model
747: */
748: private int getIndexInUnsortedModel(int pos) {
749: if (pos == -1)
750: return -1;
751: if (pos >= sortedRowIndices.size())
752: return -1;
753:
754: return sortedRowIndices.get(pos);
755: }
756:
757: /** positions in unsorted
758: */
759: public int getIndexInUnsortedFromTablePos(int tablePos) {
760: if (tablePos == -1)
761: return -1;
762: if (tablePos >= foundBasicIndices.size()) {
763: // System.out.println("tp="+tablePos+", fi="+foundBasicIndices.size());
764: return -1;
765: }
766:
767: return foundBasicIndices.get(tablePos);
768: }
769:
770: /** get the view index corresponding to the one in the basicIndex
771: */
772: public int getIndexInFoundFromBasicIndex(int basicIndex) {
773: if (basicIndex == -1)
774: return -1;
775: if (basicIndex >= 0
776: && basicIndex < basicTableModel.getRowCount()) {
777: int pos = foundBasicIndices.indexOf(Integer
778: .valueOf(basicIndex));
779: return pos;
780: } else {
781: return -1;
782: }
783: }
784:
785: @Override
786: public Object getValueAt(int row, int col) {
787: int pos = getIndexInUnsortedFromTablePos(row);
788: return basicTableModel.getValueAt(pos,
789: getColumnForViewIndex(col));
790: }
791:
792: public int getRowCount() {
793: //System.out.println("grc="+foundBasicIndices.size());
794: return foundBasicIndices.size();
795: //# return unsortedTableModel.getRowCount();
796: }
797:
798: public int getColumnCount() {
799: return selectedColumns.size();
800: //[Old] return basicTableModel.getColumnCount();
801: }
802:
803: /** if visible columns are {0,1,3}, view=2, return 3
804: BE CAREFUL: used in getColumName and getColumnClass !
805: */
806: public int getColumnForViewIndex(int viewCol) {
807: synchronized (this ) {
808: int pos = -1;
809: // iterate over all visible columns
810: Iterator it = selectedColumns.iterator();
811: while (it.hasNext()) {
812: Integer ind = (Integer) it.next();
813: pos++;
814: if (pos == viewCol) {
815: return ind.intValue();
816: }
817: }
818: return -1;
819: }
820: }
821:
822: @Override
823: public boolean isCellEditable(int row, int col) {
824: int pos = getIndexInUnsortedFromTablePos(row);
825: return basicTableModel.isCellEditable(pos,
826: getColumnForViewIndex(col));
827: }
828:
829: @Override
830: public void setValueAt(Object val, int row, int col) {
831: int pos = getIndexInUnsortedFromTablePos(row);
832: basicTableModel
833: .setValueAt(val, pos, getColumnForViewIndex(col));
834: }
835:
836: @Override
837: public Class getColumnClass(int column) {
838: return basicTableModel
839: .getColumnClass(getColumnForViewIndex(column));
840: }
841:
842: @Override
843: public String getColumnName(int column) {
844: return basicTableModel
845: .getColumnName(getColumnForViewIndex(column));
846: }
847:
848: /** can be used for completion...
849: */
850: public Set<String> getDifferentColumnValues(int col) {
851: Set<String> set = new HashSet<String>();
852: int colMod = this .getModelIndexForClickedColumn(col);
853: for (int i = 0; i < this .getBasicTableModel().getRowCount(); i++) {
854: set.add(""
855: + this .getBasicTableModel().getValueAt(i, colMod));
856: }
857:
858: return set;
859: }
860:
861: /** {value, count}
862: */
863: public Map<String, Integer> getDifferentColumnValuesStat(int col) {
864: Map<String, Integer> set = new HashMap<String, Integer>();
865: int colMod = this .getModelIndexForClickedColumn(col);
866: for (int i = 0; i < this .getBasicTableModel().getRowCount(); i++) {
867: String val = ""
868: + this .getBasicTableModel().getValueAt(i, colMod);
869: if (!set.containsKey(val)) {
870: set.put(val, 1);
871: } else {
872: set.put(val, set.get(val) + 1);
873: }
874: }
875: return set;
876: }
877:
878: public void multiSearch(final Query[] _queries) {
879: multiSearch = true;
880: queries = _queries;
881: storeTableSelection();
882:
883: internal_multisearch();
884: fireTableDataChanged();
885:
886: restoreTableSelections();
887: }
888:
889: private void internal_multisearch() {
890: synchronized (this ) {
891: foundBasicIndices.clear();
892: for (int i = 0; i < basicTableModel.getRowCount(); i++) {
893: int basicIndex = getIndexInUnsortedModel(i);
894:
895: if (basicTableModel.hitForTextSearch(basicIndex,
896: queries)) {
897: foundBasicIndices.add(basicIndex);
898: }
899: }
900: }
901: }
902:
903: /**
904: * returns the ascending/descending sort icons.
905: * this method may be overwritten by subclasses
906: * in order to provide their own icons
907: */
908: protected Icon getAscIcon(final int column) {
909: return (SORT_ASC);
910: }
911:
912: protected Icon getDescIcon(final int column) {
913: return (SORT_DESC);
914: }
915:
916: protected Icon getNoSortIcon(final int column) {
917: return (null);
918: }
919:
920: /** important: this returns a list of indices in the basic model in ascending order
921: * suitable for reverse iterating, for example when removing elements.
922: * silently ignore bad positions (-1).
923: * @param table uses the passed table to fetch the view selected rows.
924: */
925: public Vector<Integer> getSelectedRows_sortedBasicIndices(
926: JTable table) {
927: int[] sel = table.getSelectedRows();
928: Vector<Integer> ind = new Vector<Integer>();
929: for (int i = 0; i < sel.length; i++) {
930: int pos = getIndexInUnsortedFromTablePos(sel[i]);
931: if (pos >= 0)
932: ind.add(pos);
933: }
934: Collections.sort(ind);
935: return ind;
936: }
937:
938: }
|