All Downloads are FREE. Search and download functionalities are using the official Maven repository.

source.ca.odell.glazedlists.swing.EventListModel Maven / Gradle / Ivy

There is a newer version: 1.9.1
Show newest version
/* Glazed Lists                                                 (c) 2003-2006 */
/* http://publicobject.com/glazedlists/                      publicobject.com,*/
/*                                                     O'Dell Engineering Ltd.*/
package ca.odell.glazedlists.swing;

import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.TransformedList;
import ca.odell.glazedlists.event.ListEvent;
import ca.odell.glazedlists.event.ListEventListener;

import javax.swing.*;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import java.util.ArrayList;
import java.util.List;

/**
 * An EventListModel adapts an EventList to the ListModel interface making it
 * appropriate for use with a {@link JList}. Each element of the list
 * corresponds to an element in the {@link ListModel}.
 *
 * 

The EventListModel class is not thread-safe. Unless * otherwise noted, all methods are only safe to be called from the event * dispatch thread. To do this programmatically, use * {@link SwingUtilities#invokeAndWait(Runnable)}. * * @see Bug 14 * @see Bug 146 * @see Bug 177 * @see Bug 228 * @see SwingUtilities#invokeAndWait(Runnable) * * @author Jesse Wilson */ public class EventListModel implements ListEventListener, ListModel { /** the proxy moves events to the Swing Event Dispatch thread */ private TransformedList swingSource; /** whom to notify of data changes */ private final List listeners = new ArrayList(); /** recycle the list data event to prevent unnecessary object creation */ protected final MutableListDataEvent listDataEvent = new MutableListDataEvent(this); /** * Creates a new model that contains all objects located in the given * source and reacts to any changes in the given * source. */ public EventListModel(EventList source) { // lock the source list for reading since we want to prevent writes // from occurring until we fully initialize this EventListModel source.getReadWriteLock().readLock().lock(); try { swingSource = GlazedListsSwing.swingThreadProxyList(source); swingSource.addListEventListener(this); } finally { source.getReadWriteLock().readLock().unlock(); } } /** * For implementing the ListEventListener interface. This sends changes * to the table which can repaint the table cells. Because this class uses * a EventThreadProxy, it is guaranteed that all natural * calls to this method use the Swing thread. * *

This always sends discrete changes for the complete size of the list. * It may be more efficient to implement a threshhold where a large list * of changes are grouped together as a single change. This is how the * ListTable accepts large change events. */ public void listChanged(ListEvent listChanges) { // build an "optimized" ListDataEvent describing the precise range of rows in the first block listChanges.nextBlock(); final int startIndex = listChanges.getBlockStartIndex(); final int endIndex = listChanges.getBlockEndIndex(); listDataEvent.setRange(startIndex, endIndex); final int changeType = listChanges.getType(); switch (changeType) { case ListEvent.INSERT: listDataEvent.setType(ListDataEvent.INTERVAL_ADDED); break; case ListEvent.DELETE: listDataEvent.setType(ListDataEvent.INTERVAL_REMOVED); break; case ListEvent.UPDATE: listDataEvent.setType(ListDataEvent.CONTENTS_CHANGED); break; } // if another block exists, fallback to using a generic "data changed" ListDataEvent if (listChanges.nextBlock()) { listDataEvent.setRange(0, Integer.MAX_VALUE); listDataEvent.setType(ListDataEvent.CONTENTS_CHANGED); } fireListDataEvent(listDataEvent); } /** * Retrieves the value at the specified location from the table. * * Before every get, we need to validate the row because there may be an * update waiting in the event queue. For example, it is possible that * the source list has been updated by a database thread. Such a change * may have been sent as notification, but after this request in the * event queue. In the case where a row is no longer available, null is * returned. The value returned is insignificant in this case because the * Event queue will very shortly be repainting (or removing) the row * anyway. * * @see ca.odell.glazedlists.swing.EventTableModel#getValueAt(int,int) ListTable */ public Object getElementAt(int index) { swingSource.getReadWriteLock().readLock().lock(); try { return swingSource.get(index); } finally { swingSource.getReadWriteLock().readLock().unlock(); } } /** * Gets the size of the list. */ public int getSize() { swingSource.getReadWriteLock().readLock().lock(); try { return swingSource.size(); } finally { swingSource.getReadWriteLock().readLock().unlock(); } } /** * Registers the specified ListDataListener to receive updates whenever * this list changes. * *

The specified ListDataListener must not save a * reference to the ListDataEvent beyond the end of the notification * method. This is because the ListDataEvent is re-used to increase * the performance of this implementation. */ public void addListDataListener(ListDataListener listDataListener) { listeners.add(listDataListener); } /** * Deregisters the specified ListDataListener from receiving updates * whenever this list changes. */ public void removeListDataListener(ListDataListener listDataListener) { listeners.remove(listDataListener); } /** * Notifies all ListDataListeners about one block of changes in the list. */ protected void fireListDataEvent(ListDataEvent listDataEvent) { // notify all listeners about the event for(int i = 0, n = listeners.size(); i < n; i++) { ListDataListener listDataListener = listeners.get(i); switch (listDataEvent.getType()) { case ListDataEvent.CONTENTS_CHANGED: listDataListener.contentsChanged(listDataEvent); break; case ListDataEvent.INTERVAL_ADDED: listDataListener.intervalAdded(listDataEvent); break; case ListDataEvent.INTERVAL_REMOVED: listDataListener.intervalRemoved(listDataEvent); break; } } } /** * Releases the resources consumed by this {@link EventListModel} so that it * may eventually be garbage collected. * *

An {@link EventListModel} will be garbage collected without a call to * {@link #dispose()}, but not before its source {@link EventList} is garbage * collected. By calling {@link #dispose()}, you allow the {@link EventListModel} * to be garbage collected before its source {@link EventList}. This is * necessary for situations where an {@link EventListModel} is short-lived but * its source {@link EventList} is long-lived. * *

Warning: It is an error * to call any method on an {@link EventListModel} after it has been disposed. * As such, this {@link EventListModel} should be detached from its * corresponding Component before it is disposed. */ public void dispose() { swingSource.removeListEventListener(this); swingSource.dispose(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy