/** * Changes current selection if newer is different or <code>force</code> param is <code>true * </code>. * * <p>This method ensures listeners are invoked just once. * * @param <Q> the type of the rows. * @param table the table. * @param viewIndexes the indexes to select. <code>-1</code> indexes are ignored. * @param force whether to force selection change in spite of new selection is the same as * previous. */ public static <Q> void changeSelection( final JTable table, final List<Integer> viewIndexes, final Boolean force) { Assert.notNull(table, TableUtils.TABLE); Assert.notNull(viewIndexes, "viewIndexes"); Assert.notNull(force, "force"); // Replace current selection (do it in the event dispatcher thread to avoid race conditions) SwingUtils.runInEventDispatcherThread( new Runnable() { /** {@inheritDoc} */ @Override public void run() { Boolean proceed = force; if (!proceed) { final List<Integer> currentViewIndexes = TableUtils.getSelectedViewIndexes(table); proceed |= !CollectionUtils.isEqualCollection(currentViewIndexes, viewIndexes); } // PRE-CONDITION: new selection is different from previous or force is true if (proceed) { // INVARIANT: Change selection taking care of setting "valueIsAdjusting": // otherwise multiple events will be raised final ListSelectionModel listSelectionModel = table.getSelectionModel(); listSelectionModel.setValueIsAdjusting(Boolean.TRUE); listSelectionModel.clearSelection(); for (Integer viewIndex : viewIndexes) { if (viewIndex >= 0) { table.addRowSelectionInterval(viewIndex, viewIndex); } } // POST-CONDITION: listeners are notified listSelectionModel.setValueIsAdjusting(Boolean.FALSE); } } }); }
/** * Show the given entities in a table. * * <p>When dealing with <code>EventList</code> performance is important. This method ensures table * model events are launched just once at the end. * * @param <Q> the type of the rows. * @param tableModel the table model. * @param entities the entities to be shown. * @param attach whether to attach new entities to currents. If <code>false</code> currents are * replaced. * @return <code>true</code> if success and <code>false</code> in other case (i.e.:user rejected * change). */ @SuppressWarnings("unchecked") public static <Q> Boolean showEntities( GlazedTableModel tableModel, final List<Q> entities, final Boolean attach) { Assert.notNull(tableModel, TableUtils.TABLE_MODEL); Assert.notNull(entities, "entities"); Assert.notNull(attach, "attach"); final EventList<Q> eventList = TableUtils.getSource(tableModel); // Proceed only if ... final Boolean proceed; if (attach) { // ... must attach and entities are not contained in eventList or... proceed = !CollectionUtils.isSubCollection(entities, eventList); } else { // ... must replace and entities are not equals to eventList proceed = !CollectionUtils.isEqualCollection(entities, eventList); } if (proceed) { if (TableUtils.LOGGER.isDebugEnabled()) { TableUtils.LOGGER.debug("About to show entities " + entities); } // Avoid notifying in every single change, instead do it at the end final TableModelListener[] listeners = tableModel.getTableModelListeners(); for (final TableModelListener listener : listeners) { tableModel.removeTableModelListener(listener); } SwingUtils.runInEventDispatcherThread( new Runnable() { @Override public void run() { // Update the contents of the event list eventList.getReadWriteLock().writeLock().lock(); try { if (!attach) { eventList.clear(); } eventList.addAll(eventList.size(), CollectionUtils.subtract(entities, eventList)); } finally { // Since Swing is multithread we need to lock before and unlock later // http://sites.google.com/site/glazedlists/documentation/faq eventList.getReadWriteLock().writeLock().unlock(); } } }); // Enable notifying: install listeners again for (final TableModelListener listener : listeners) { tableModel.addTableModelListener(listener); } // Since listeners were uninstalled notification should be explicit tableModel.fireTableDataChanged(); } return proceed; }