001    package org.springframework.richclient.widget.table.glazedlists;
002    
003    import ca.odell.glazedlists.*;
004    import ca.odell.glazedlists.event.ListEvent;
005    import ca.odell.glazedlists.event.ListEventListener;
006    import ca.odell.glazedlists.gui.TableFormat;
007    import ca.odell.glazedlists.gui.WritableTableFormat;
008    import ca.odell.glazedlists.swing.*;
009    import com.jgoodies.forms.layout.CellConstraints;
010    import com.jgoodies.forms.layout.FormLayout;
011    import com.jgoodies.forms.layout.Size;
012    import com.jgoodies.forms.layout.Sizes;
013    import org.jdesktop.swingx.JXTable;
014    import org.jdesktop.swingx.decorator.*;
015    import org.jdesktop.swingx.table.TableColumnExt;
016    import org.jdesktop.xswingx.JXSearchField;
017    import org.springframework.richclient.application.Application;
018    import org.springframework.richclient.command.AbstractCommand;
019    import org.springframework.richclient.command.ActionCommand;
020    import org.springframework.richclient.command.CommandGroup;
021    import org.springframework.richclient.command.config.CommandConfigurer;
022    import org.springframework.richclient.util.RcpSupport;
023    import org.springframework.richclient.util.ValueMonitor;
024    import org.springframework.richclient.widget.AbstractWidget;
025    import org.springframework.richclient.widget.table.TableCellRenderers;
026    import org.springframework.richclient.widget.table.TableDescription;
027    import org.springframework.richclient.widget.table.TableWidget;
028    
029    import javax.swing.*;
030    import javax.swing.border.Border;
031    import javax.swing.event.*;
032    import javax.swing.table.DefaultTableCellRenderer;
033    import javax.swing.table.TableCellEditor;
034    import javax.swing.table.TableCellRenderer;
035    import java.awt.*;
036    import java.awt.event.FocusAdapter;
037    import java.awt.event.FocusEvent;
038    import java.awt.event.ActionListener;
039    import java.awt.event.ActionEvent;
040    import java.beans.PropertyChangeEvent;
041    import java.beans.PropertyChangeListener;
042    import java.util.*;
043    import java.util.List;
044    
045    public final class GlazedListTableWidget extends AbstractWidget implements TableWidget
046    {
047        private JXTable theTable = new JXTable();
048    
049        private JScrollPane tableScroller;
050    
051        private ValueMonitor selectionMonitor = new ValueMonitor();
052    
053        private EventTableModel<Object> tableModel;
054    
055        private EventSelectionModel<Object> selectionModel;
056    
057        private EventList<Object> dataList;
058    
059        private EventList<Object> shownList;
060    
061        private SortedList<Object> sortedList;
062    
063        private JTextField textFilterField;
064    
065        private AbstractCommand[] navigationCommands;
066    
067        private CommandGroup navigationCommandGroup;
068    
069        private CommandGroup selectColumnCommandGroup;
070    
071        private CommandConfigurer commandConfigurer;
072    
073        private JLabel countLabel;
074    
075        static
076        {
077            UIManager.put("JXTable.column.horizontalScroll", RcpSupport.getMessage("JXTable.horizontalScroll.label"));
078            UIManager.put("JXTable.column.packAll", RcpSupport.getMessage("JXTable.packAll.label"));
079            UIManager.put("JXTable.column.packSelected", RcpSupport.getMessage("JXTable.packSelected.label"));
080        }
081    
082        /**
083         * CellEditorListener op de selectiekolom om de selectieListeners gezamelijk
084         * te triggeren .
085         */
086        private CellEditorListener userSelectionCellEditorListener = new CellEditorListener()
087        {
088    
089            public void editingStopped(ChangeEvent e)
090            {
091                fireUserSelectionChangedEvent();
092            }
093    
094            public void editingCanceled(ChangeEvent e)
095            {
096            }
097        };
098    
099        private Set dirtyRows = new HashSet();
100    
101        private CellEditorListener dirtyRowCellEditorListener = new CellEditorListener()
102        {
103    
104            public void editingCanceled(ChangeEvent e)
105            {
106            }
107    
108    
109            public void editingStopped(ChangeEvent e)
110            {
111                dirtyRows.add(getSelectedRows()[0]);
112            }
113        };
114    
115        /**
116         * De listeners geregistreerd op de selectiekolom, getriggerd door
117         * {@link #userSelectionCellEditorListener}.
118         */
119        private List<PropertyChangeListener> userSelectionListeners;
120    
121        public GlazedListTableWidget(List<? extends Object> rows, TableDescription tableDesc)
122        {
123            this(rows, tableDesc, tableDesc.getDefaultComparator());
124        }
125    
126        public GlazedListTableWidget(List<? extends Object> rows, TableDescription tableDesc,
127                                     Comparator comparator)
128        {
129            this(tableDesc.getDataType(), rows, GlazedListsSupport.makeTableFormat(tableDesc), GlazedListsSupport
130                    .makeFilterProperties(tableDesc), comparator, tableDesc.hasSelectColumn());
131            // Als de tablewidget met ons eigen TableDescription class is gemaakt
132            // kunnen we additionele dingen als width/resizable/renderer en editor
133            // zetten
134            // bedenking: zouden we tabledesc van een iterator voorzien om over de
135            // kolommen te lopen?
136            TableCellEditor columnEditor = null;
137            for (int i = 0; i < tableDesc.getColumnCount(); ++i)
138            {
139                TableColumnExt column = (TableColumnExt) theTable.getColumns(true).get(i);
140                int columnWidth = tableDesc.getMaxColumnWidth(i);
141                if (columnWidth > 0)
142                {
143                    column.setMaxWidth(columnWidth);
144                }
145                columnWidth = tableDesc.getMinColumnWidth(i);
146                if (columnWidth > 0)
147                {
148                    column.setMinWidth(columnWidth);
149                }
150                column.setResizable(tableDesc.isResizable(i));
151                column.setVisible(tableDesc.isVisible(i));
152                columnEditor = tableDesc.getColumnEditor(i);
153                if (columnEditor != null)
154                {
155                    if (tableDesc.isSelectColumn(i))
156                    {
157                        columnEditor.addCellEditorListener(userSelectionCellEditorListener);
158                    }
159                    else
160                    {
161                        columnEditor.addCellEditorListener(dirtyRowCellEditorListener);
162                    }
163                    column.setCellEditor(columnEditor);
164                }
165                if (tableDesc.getColumnRenderer(i) != null)
166                {
167                    TableCellRenderer renderer = tableDesc.getColumnRenderer(i);
168                    column.setCellRenderer(renderer);
169                    if (renderer instanceof DefaultTableCellRenderer)
170                    {
171                        int align = ((DefaultTableCellRenderer) renderer).getHorizontalAlignment();
172                        switch (align)
173                        {
174                            case SwingConstants.CENTER:
175                                column.setHeaderRenderer(wrapInSortArrowHeaderRenderer(TableCellRenderers.CENTER_ALIGNED_HEADER_RENDERER));
176                                break;
177                            case SwingConstants.RIGHT:
178                                column.setHeaderRenderer(wrapInSortArrowHeaderRenderer(TableCellRenderers.RIGHT_ALIGNED_HEADER_RENDERER));
179                                break;
180                            default:
181                                break;
182                        }
183                    }
184                }
185            }
186        }
187    
188        private TableCellRenderer wrapInSortArrowHeaderRenderer(TableCellRenderer renderer)
189        {
190            if (tableComparatorChooser != null)
191            {
192                return tableComparatorChooser.createSortArrowHeaderRenderer(renderer);
193            }
194            else
195            {
196                return renderer;
197            }
198        }
199    
200        public GlazedListTableWidget(Class dataType, List<? extends Object> rows, TableFormat format,
201                                     String[] filterProperties)
202        {
203            this(dataType, rows, format, filterProperties, null, false);
204        }
205    
206        public GlazedListTableWidget(Class dataType, List<? extends Object> rows, TableFormat format,
207                                     String[] filterProperties, Comparator comparator, boolean addHighlightSelectColumn)
208        {
209            theTable.setColumnControlVisible(true);
210            theTable.getSelectionMapper().setEnabled(false);
211            commandConfigurer = (CommandConfigurer) Application.services().getService(CommandConfigurer.class);
212            dataList = rows == null ? new BasicEventList<Object>() : GlazedLists.eventList(rows);
213    
214            sortedList = new SortedList<Object>(dataList, comparator);
215            this.shownList = sortedList;
216    
217            if (filterProperties != null)
218            {
219                textFilterField = new JXSearchField(RcpSupport.getMessage("glazedListTableWidget.textFilterField.prompt"));
220                textFilterField.addFocusListener(new FocusAdapter()
221                {
222                    @Override
223                    public void focusGained(FocusEvent e)
224                    {
225                        textFilterField.selectAll();
226                    }
227                });
228                shownList = new FilterList<Object>(shownList,
229                        new TextComponentMatcherEditor(textFilterField, GlazedLists.textFilterator(dataType,
230                                filterProperties)));
231            }
232    
233            selectionModel = new EventSelectionModel<Object>(shownList);
234            selectionModel.addListSelectionListener(new SelectionNavigationListener());
235            theTable.setSelectionModel(selectionModel);
236    
237            tableModel = new EventJXTableModel<Object>(shownList, format);
238            theTable.setModel(tableModel);
239    
240            if (addHighlightSelectColumn)
241            {
242                Highlighter selectHighlighter = new ColorHighlighter(HIGHLIGHTSELECTCOLUMN, new Color(0xF0, 0xF0, 0xE0), Color.BLACK);
243                setHighlighters(HighlighterFactory.createSimpleStriping(), selectHighlighter);
244                initializeSelectColumnCommands();
245            }
246            else
247            {
248                setHighlighters(HighlighterFactory.createSimpleStriping());
249            }  
250    
251            if (sortedList != null)
252            {
253                theTable.setSortable(false);
254                theTable.getTableHeader().setDefaultRenderer(TableCellRenderers.LEFT_ALIGNED_HEADER_RENDERER);
255                tableComparatorChooser = TableComparatorChooser
256                        .install(theTable, sortedList, TableComparatorChooser.MULTIPLE_COLUMN_MOUSE_WITH_UNDO);
257                // the following is a fix for the selection sort and navigation problem
258                tableComparatorChooser.addSortActionListener(new ActionListener()
259                {
260                    public void actionPerformed(ActionEvent e)
261                    {
262                        EventList<Object> selected = selectionModel.getSelected();
263                        int[] indexes = new int[selected.size()];
264                        int i = 0;
265                        for (Object o : selected)
266                        {
267                            indexes[i++] = shownList.indexOf(o);
268                        }
269                        selectionModel.clearSelection();
270                        for (int index : indexes)
271                        {
272                            selectionModel.addSelectionInterval(index, index);
273                        }
274                    }
275                });
276            }       
277            
278            theTable.setPreferredScrollableViewportSize(new Dimension(50, 50));
279            tableScroller = new JScrollPane(theTable);
280            theTable.setHorizontalScrollEnabled(true);
281            initializeNavigationCommands();
282        }
283    
284        /**
285         * Enable the row height to diverge from the default height.
286         * <p/>
287         * NOTE: this is experimental as there is a problem with glazedlists and jxtable.
288         * (see note on ExtendedJXTable above)
289         */
290        public void setRowHeightEnabled(boolean rowHeightEnabled)
291        {
292            theTable.setRowHeightEnabled(true);
293        }
294    
295        private class SelectionNavigationListener implements ListSelectionListener
296        {
297    
298            public void valueChanged(ListSelectionEvent e)
299            {
300                // enkel op einde van reeks selection veranderingen reageren.
301                if (!e.getValueIsAdjusting())
302                {
303                    if (selectionModel.getSelected().size() == 1)
304                    {
305                        selectionMonitor.setValue(selectionModel.getSelected().get(0));
306                    }
307                    else
308                    {
309                        Object[] selectedRows = selectionModel.getSelected().toArray();
310                        selectionMonitor.setValue(selectedRows.length > 0 ? selectedRows : null);
311                    }
312    
313                    int selectedIndex = selectionModel.getAnchorSelectionIndex();
314                    int lastIndex = shownList.size() - 1;
315                    boolean emptyList = (lastIndex == -1);
316                    boolean onFirst = (selectedIndex == 0);
317                    boolean onLast = (selectedIndex == lastIndex);
318    
319                    navigationCommands[NAVIGATE_FIRST].setEnabled(!emptyList && !onFirst);
320                    navigationCommands[NAVIGATE_PREVIOUS].setEnabled(!emptyList && !onFirst);
321                    navigationCommands[NAVIGATE_NEXT].setEnabled(!emptyList && !onLast);
322                    navigationCommands[NAVIGATE_LAST].setEnabled(!emptyList && !onLast);
323                }
324            }
325        }
326    
327        public static final HighlightPredicate HIGHLIGHTSELECTCOLUMN = new HighlightSelectColumn();
328    
329        private TableComparatorChooser tableComparatorChooser;
330    
331        static class HighlightSelectColumn implements HighlightPredicate
332        {
333    
334            public boolean isHighlighted(Component renderer, ComponentAdapter adapter)
335            {
336                Object selectedValue = adapter.getValueAt(adapter.row, 0);
337                return Boolean.TRUE.equals(selectedValue);
338            }
339        }
340    
341        public void setHighlighters(Highlighter... highlighters)
342        {
343            this.theTable.setHighlighters(highlighters);
344        }
345    
346        /**
347         * {@inheritDoc}
348         */
349        public boolean isEmpty()
350        {
351            return this.dataList.isEmpty();
352        }
353    
354        /**
355         * {@inheritDoc}
356         */
357        public int nrOfRows()
358        {
359            return this.tableModel.getRowCount();
360        }
361    
362        private void initializeNavigationCommands()
363        {
364            this.navigationCommands = new AbstractCommand[4];
365            this.navigationCommands[NAVIGATE_FIRST] = new ActionCommand(NAVIGATE_FIRSTROW_CMDID)
366            {
367    
368                @Override
369                protected void doExecuteCommand()
370                {
371                    selectionModel.setSelectionInterval(0, 0);
372                    scrollToSelectedRow();
373                }
374            };
375            this.navigationCommands[NAVIGATE_PREVIOUS] = new ActionCommand(NAVIGATE_PREVIOUSROW_CMDID)
376            {
377    
378                @Override
379                protected void doExecuteCommand()
380                {
381                    int newIndex = selectionModel.getAnchorSelectionIndex() - 1;
382                    newIndex = (newIndex < 0) ? 0 : newIndex;
383                    selectionModel.setSelectionInterval(newIndex, newIndex);
384                    scrollToSelectedRow();
385                }
386            };
387            this.navigationCommands[NAVIGATE_NEXT] = new ActionCommand(NAVIGATE_NEXTROW_CMDID)
388            {
389    
390                @Override
391                protected void doExecuteCommand()
392                {
393                    int newIndex = selectionModel.getAnchorSelectionIndex() + 1;
394                    int lastIndex = shownList.size() - 1;
395                    newIndex = (newIndex > lastIndex) ? lastIndex : newIndex;
396                    selectionModel.setSelectionInterval(newIndex, newIndex);
397                    scrollToSelectedRow();
398                }
399            };
400            this.navigationCommands[NAVIGATE_LAST] = new ActionCommand(NAVIGATE_LASTROW_CMDID)
401            {
402    
403                @Override
404                protected void doExecuteCommand()
405                {
406                    int lastIndex = shownList.size() - 1;
407                    selectionModel.setSelectionInterval(lastIndex, lastIndex);
408                    scrollToSelectedRow();
409                }
410            };
411    
412            for (int i = 0; i < this.navigationCommands.length; i++)
413            {
414                this.commandConfigurer.configure(this.navigationCommands[i]);
415                this.navigationCommands[i].setEnabled(false);
416            }
417            this.navigationCommandGroup = CommandGroup.createCommandGroup(this.navigationCommands);
418        }
419    
420        private void fireUserSelectionChangedEvent()
421        {
422            if (userSelectionListeners != null)
423            {
424                for (Iterator listeners = userSelectionListeners.iterator(); listeners.hasNext();)
425                {
426                    PropertyChangeListener listener = (PropertyChangeListener) listeners.next();
427                    listener.propertyChange(new PropertyChangeEvent(this, "selection", null, null));
428                }
429            }
430        }
431    
432        public void addUserSelectionListener(PropertyChangeListener listener)
433        {
434            if (userSelectionListeners == null)
435            {
436                userSelectionListeners = new ArrayList<PropertyChangeListener>();
437            }
438            userSelectionListeners.add(listener);
439        }
440    
441        private void initializeSelectColumnCommands()
442        {
443            final WritableTableFormat writableTableFormat = (WritableTableFormat) this.tableModel
444                    .getTableFormat();
445            AbstractCommand selectAll = new ActionCommand(SELECT_ALL_ID)
446            {
447    
448                @Override
449                protected void doExecuteCommand()
450                {
451                    shownList.getReadWriteLock().writeLock().lock();
452                    Iterator i = shownList.iterator();
453                    while (i.hasNext())
454                    {
455                        writableTableFormat.setColumnValue(i.next(), Boolean.TRUE, 0);
456                    }
457                    shownList.getReadWriteLock().writeLock().unlock();
458                    theTable.repaint();
459                    fireUserSelectionChangedEvent();
460                }
461            };
462            this.commandConfigurer.configure(selectAll);
463            AbstractCommand selectNone = new ActionCommand(SELECT_NONE_ID)
464            {
465    
466                @Override
467                protected void doExecuteCommand()
468                {
469                    shownList.getReadWriteLock().writeLock().lock();
470                    Iterator i = shownList.iterator();
471                    while (i.hasNext())
472                    {
473                        writableTableFormat.setColumnValue(i.next(), Boolean.FALSE, 0);
474                    }
475                    shownList.getReadWriteLock().writeLock().unlock();
476                    theTable.repaint();
477                    fireUserSelectionChangedEvent();
478                }
479            };
480            this.commandConfigurer.configure(selectNone);
481            AbstractCommand selectInverse = new ActionCommand(SELECT_INVERSE_ID)
482            {
483    
484                @Override
485                protected void doExecuteCommand()
486                {
487                    shownList.getReadWriteLock().writeLock().lock();
488                    Iterator i = shownList.iterator();
489                    while (i.hasNext())
490                    {
491                        Object rowObject = i.next();
492                        Object columnValue = writableTableFormat.getColumnValue(rowObject, 0);
493                        writableTableFormat.setColumnValue(rowObject, Boolean.TRUE.equals(columnValue)
494                                ? Boolean.FALSE
495                                : Boolean.TRUE, 0);
496                    }
497                    shownList.getReadWriteLock().writeLock().unlock();
498                    theTable.repaint();
499                    fireUserSelectionChangedEvent();
500                }
501            };
502            this.commandConfigurer.configure(selectInverse);
503            this.selectColumnCommandGroup = CommandGroup.createCommandGroup(new Object[]{selectAll, selectNone,
504                    selectInverse});
505        }
506    
507        public final void setRows(Collection newRows)
508        {
509            this.dataList.getReadWriteLock().writeLock().lock();
510            try
511            {
512                this.dirtyRows.clear();
513                theTable.clearSelection();
514                this.dataList.clear();
515                this.dataList.addAll(newRows);
516    
517                scrollToSelectedRow(); // new rows, scroll back to top
518            }
519            finally
520            {
521                this.dataList.getReadWriteLock().writeLock().unlock();
522            }
523        }
524    
525        public final List getRows()
526        {
527            return new ArrayList<Object>(this.dataList);
528        }
529    
530        public final List getVisibleRows()
531        {
532            return new ArrayList<Object>(this.shownList);
533        }
534    
535        public void addRowObject(Object newObject)
536        {
537            this.dataList.getReadWriteLock().writeLock().lock();
538            try
539            {
540                this.dataList.add(newObject);
541            }
542            finally
543            {
544                this.dataList.getReadWriteLock().writeLock().unlock();
545            }
546        }
547    
548        public void addRows(Collection rows)
549        {
550            this.dataList.getReadWriteLock().writeLock().lock();
551            try
552            {
553                this.dataList.addAll(rows);
554            }
555            finally
556            {
557                this.dataList.getReadWriteLock().writeLock().unlock();
558            }
559        }
560    
561        public void removeRowObject(Object objectToRemove)
562        {
563            this.dataList.getReadWriteLock().writeLock().lock();
564            try
565            {
566                dirtyRows.remove(objectToRemove);
567                this.dataList.remove(objectToRemove);
568            }
569            finally
570            {
571                this.dataList.getReadWriteLock().writeLock().unlock();
572            }
573        }
574    
575        public int selectRowObject(Object toPointTo, Observer originatingObserver)
576        {
577            int index = this.shownList.indexOf(toPointTo);
578            selectRowObject(index, originatingObserver);
579            return index;
580        }
581    
582        public void selectRowObject(final int index, final Observer originatingObserver)
583        {
584            Runnable doSelectRowObject = new Runnable()
585            {
586    
587                public void run()
588                {
589                    if (originatingObserver != null)
590                    {
591                        selectionMonitor.deleteObserver(originatingObserver);
592                    }
593    
594                    if ((index > -1) && (shownList.size() > index))
595                    {
596                        selectionModel.setSelectionInterval(index, index);
597                    }
598                    else
599                    {
600                        selectionModel.clearSelection();
601                    }
602                    scrollToSelectedRow();
603    
604                    if (originatingObserver != null)
605                    {
606                        selectionMonitor.addObserver(originatingObserver);
607                    }
608                }
609            };
610            if (SwingUtilities.isEventDispatchThread())
611            {
612                doSelectRowObject.run();
613            }
614            else
615            {
616                SwingUtilities.invokeLater(doSelectRowObject);
617            }
618    
619        }
620    
621        public void addSelection(final Object[] rows, final Observer originatingObserver)
622        {
623            Runnable doAddSelection = new Runnable()
624            {
625                public void run()
626                {
627                    if (originatingObserver != null)
628                    {
629                        selectionMonitor.deleteObserver(originatingObserver);
630                    }
631                    for (int i = 0; i < rows.length; i++)
632                    {
633                        int index = shownList.indexOf(rows[i]);
634                        selectionModel.addSelectionInterval(index, index);
635                    }
636                    if (originatingObserver != null)
637                    {
638                        selectionMonitor.addObserver(originatingObserver);
639                    }
640                }
641            };
642            if (SwingUtilities.isEventDispatchThread())
643            {
644                doAddSelection.run();
645            }
646            else
647            {
648                SwingUtilities.invokeLater(doAddSelection);
649            }
650        }
651    
652        public boolean hasSelection()
653        {
654            return !this.selectionModel.isSelectionEmpty();
655        }
656    
657        public synchronized void scrollToSelectedRow()
658        {
659            Runnable doScrollToSelectedRow = new Runnable()
660            {
661                public void run()
662                {
663                    if (theTable.isVisible())
664                    {
665                        int selectedRow = theTable.getSelectedRow();
666                        if (selectedRow != -1)
667                        {
668                            Rectangle cellRect = theTable.getCellRect(selectedRow, 0, true);
669                            Rectangle viewRect = tableScroller.getViewport().getViewRect();
670                            if (!viewRect.contains(cellRect))
671                            {
672                                if (cellRect.y < viewRect.y) // cell is above view (or cut above)
673                                {
674                                    tableScroller.getViewport().setViewPosition(cellRect.getLocation());
675                                }
676                                else // cell is below view (or cut below)
677                                {
678                                    tableScroller.getViewport().scrollRectToVisible(cellRect);
679                                }
680                            }
681                        }
682                        else
683                        {
684                            tableScroller.getViewport().setViewPosition(new Point(0, 0));
685                        }
686                    }
687                }
688            };
689            if (SwingUtilities.isEventDispatchThread())
690            {
691                doScrollToSelectedRow.run();
692            }
693            else
694            {
695                SwingUtilities.invokeLater(doScrollToSelectedRow);
696            }
697        }
698    
699        public void replaceRowObject(Object oldObject, Object newObject, Observer originatingObserver)
700        {
701            this.dataList.getReadWriteLock().writeLock().lock();
702            try
703            {
704                dirtyRows.remove(oldObject);
705                int index = this.dataList.indexOf(oldObject);
706                if (index != -1)
707                {
708                    boolean wasSelected = this.selectionModel.isSelectedIndex(this.shownList.indexOf(oldObject));
709    
710                    if (wasSelected && (originatingObserver != null))
711                    {
712                        this.selectionMonitor.deleteObserver(originatingObserver);
713                    }
714    
715                    this.dataList.set(index, newObject);
716    
717                    if (wasSelected)
718                    {
719                        int indexToSelect = this.shownList.indexOf(newObject);
720                        this.selectionModel.addSelectionInterval(indexToSelect, indexToSelect);
721                        if (originatingObserver != null)
722                        {
723                            this.selectionMonitor.addObserver(originatingObserver);
724                        }
725                    }
726                }
727            }
728            finally
729            {
730                this.dataList.getReadWriteLock().writeLock().unlock();
731            }
732        }
733    
734        public void replaceRows(final Collection oldObject, final Collection newObject)
735        {
736            Runnable doReplaceRows = new Runnable()
737            {
738                public void run()
739                {
740                    dataList.getReadWriteLock().writeLock().lock();
741                    try
742                    {
743                        dirtyRows.clear();
744                        dataList.removeAll(oldObject);
745                        dataList.addAll(newObject);
746                    }
747                    finally
748                    {
749                        dataList.getReadWriteLock().writeLock().unlock();
750                    }
751                }
752            };
753            if (SwingUtilities.isEventDispatchThread())
754            {
755                doReplaceRows.run();
756            }
757            else
758            {
759                SwingUtilities.invokeLater(doReplaceRows);
760            }
761        }
762    
763        public void unSelectAll()
764        {
765            Runnable doUnselectAll = new Runnable()
766            {
767                public void run()
768                {
769                    selectionModel.clearSelection();
770                }
771            };
772            if (SwingUtilities.isEventDispatchThread())
773            {
774                doUnselectAll.run();
775            }
776            else
777            {
778                SwingUtilities.invokeLater(doUnselectAll);
779            }
780        }
781    
782        public Object[] getSelectedRows()
783        {
784            return this.selectionModel.getSelected().toArray();
785        }
786    
787        public JComponent getComponent()
788        {
789            return this.tableScroller;
790        }
791    
792        public JTable getTable()
793        {
794            return this.theTable;
795        }
796    
797        public void addSelectionObserver(Observer observer)
798        {
799            this.selectionMonitor.addObserver(observer);
800        }
801    
802        public void removeSelectionObserver(Observer observer)
803        {
804            this.selectionMonitor.deleteObserver(observer);
805        }
806    
807        public void addTableModelListener(TableModelListener listener)
808        {
809            this.tableModel.addTableModelListener(listener);
810        }
811    
812        public void removeTableModelListener(TableModelListener listener)
813        {
814            this.tableModel.removeTableModelListener(listener);
815        }
816    
817        public void updateTable()
818        {
819            this.tableModel.fireTableDataChanged();
820        }
821    
822        public JTextField getTextFilterField()
823        {
824            return textFilterField;
825        }
826    
827        public AbstractCommand[] getNavigationCommands()
828        {
829            return navigationCommands;
830        }
831    
832        public JComponent getNavigationButtonBar()
833        {
834            return getNavigationButtonBar(Sizes.PREFERRED, BorderFactory.createEmptyBorder());
835        }
836    
837        public JComponent getNavigationButtonBar(Size size, Border border)
838        {
839            return this.navigationCommandGroup.createButtonBar(size, border);
840        }
841    
842        public CommandGroup getNavigationCommandGroup()
843        {
844            return this.navigationCommandGroup;
845        }
846    
847        public CommandGroup getSelectColumnCommandGroup()
848        {
849            return this.selectColumnCommandGroup;
850        }
851    
852        public JComponent getSelectButtonBar()
853        {
854            return this.selectColumnCommandGroup.createButtonBar(Sizes.PREFERRED, BorderFactory
855                    .createEmptyBorder());
856        }
857    
858        public JComponent getButtonBar()
859        {
860            if (this.selectColumnCommandGroup != null)
861            {
862                JPanel buttons = new JPanel(new FormLayout("fill:pref, 3dlu, fill:pref, 3dlu, fill:pref",
863                        "fill:pref:grow"));
864                CellConstraints cc = new CellConstraints();
865                buttons.add(getSelectButtonBar(), cc.xy(1, 1));
866                buttons.add(new JSeparator(SwingConstants.VERTICAL), cc.xy(3, 1));
867                buttons.add(getNavigationButtonBar(), cc.xy(5, 1));
868                return buttons;
869            }
870            return getNavigationButtonBar();
871        }
872    
873        public JLabel getListSummaryLabel()
874        {
875            if (countLabel == null)
876            {
877                countLabel = createCountLabel();
878            }
879            return countLabel;
880        }
881    
882        private JLabel createCountLabel()
883        {
884            final JLabel label = new JLabel("");
885    
886            setTextForListSummaryLabel(label);
887    
888            shownList.addListEventListener(new ListEventListener<Object>()
889            {
890                public void listChanged(ListEvent<Object> evt)
891                {
892                    if (!evt.isReordering())
893                    {
894                        setTextForListSummaryLabel(label);
895                    }
896                }
897            });
898    
899            theTable.getSelectionModel().addListSelectionListener(new ListSelectionListener()
900            {
901                public void valueChanged(ListSelectionEvent e)
902                {
903                    if (!e.getValueIsAdjusting())
904                    {
905                        setTextForListSummaryLabel(label);
906                    }
907                }
908    
909            });
910    
911            return label;
912        }
913    
914        private void setTextForListSummaryLabel(final JLabel label)
915        {
916            SwingUtilities.invokeLater(new Runnable()
917            {
918                public void run()
919                {
920                    Integer index = 0;
921                    Integer selectedCount = 0;
922                    Integer totalCount = shownList.size();
923    
924                    if (getSelectedRows() != null && getSelectedRows().length > 0)
925                    {
926                        index = shownList.indexOf(getSelectedRows()[0]);
927                        index++;
928                        selectedCount = getSelectedRows().length;
929                    }
930    
931                    label.setText(RcpSupport.getMessage("glazedListTableWidget", "listSummary", "label", new Object[]{index, selectedCount, totalCount}));
932                }
933            });
934        }
935    
936    
937        @Override
938        public void onAboutToShow()
939        {
940            super.onAboutToShow();
941            this.theTable.requestFocusInWindow();
942        }
943    
944        public Set getDirtyRows()
945        {
946            return dirtyRows;
947        }
948    
949    }