001 // License: GPL. For details, see LICENSE file.
002 package org.openstreetmap.josm.data.osm.event;
003
004 import java.util.Collection;
005 import java.util.List;
006 import java.util.concurrent.CopyOnWriteArrayList;
007
008 import javax.swing.SwingUtilities;
009
010 import org.openstreetmap.josm.data.SelectionChangedListener;
011 import org.openstreetmap.josm.data.osm.DataSet;
012 import org.openstreetmap.josm.data.osm.OsmPrimitive;
013 import org.openstreetmap.josm.data.osm.event.DatasetEventManager.FireMode;
014
015 /**
016 * Similar like {@link DatasetEventManager}, just for selection events. Because currently selection changed
017 * event are global, only FIRE_IN_EDT and FIRE_EDT_CONSOLIDATED modes are really useful
018 *
019 */
020 public class SelectionEventManager implements SelectionChangedListener {
021
022 private static final SelectionEventManager instance = new SelectionEventManager();
023
024 public static SelectionEventManager getInstance() {
025 return instance;
026 }
027
028 private static class ListenerInfo {
029 final SelectionChangedListener listener;
030
031 public ListenerInfo(SelectionChangedListener listener, boolean consolidate) {
032 this.listener = listener;
033 }
034
035 @Override
036 public int hashCode() {
037 return listener.hashCode();
038 }
039
040 @Override
041 public boolean equals(Object o) {
042 return o instanceof ListenerInfo && ((ListenerInfo)o).listener == listener;
043 }
044 }
045
046 private Collection<? extends OsmPrimitive> selection;
047 private final CopyOnWriteArrayList<ListenerInfo> inEDTListeners = new CopyOnWriteArrayList<ListenerInfo>();
048 private final CopyOnWriteArrayList<ListenerInfo> normalListeners = new CopyOnWriteArrayList<ListenerInfo>();
049
050 public SelectionEventManager() {
051 DataSet.addSelectionListener(this);
052 }
053
054 public void addSelectionListener(SelectionChangedListener listener, FireMode fireMode) {
055 if (fireMode == FireMode.IN_EDT)
056 throw new UnsupportedOperationException("IN_EDT mode not supported, you probably want to use IN_EDT_CONSOLIDATED.");
057 if (fireMode == FireMode.IN_EDT || fireMode == FireMode.IN_EDT_CONSOLIDATED) {
058 inEDTListeners.addIfAbsent(new ListenerInfo(listener, fireMode == FireMode.IN_EDT_CONSOLIDATED));
059 } else {
060 normalListeners.addIfAbsent(new ListenerInfo(listener, false));
061 }
062 }
063
064 public void removeSelectionListener(SelectionChangedListener listener) {
065 ListenerInfo searchListener = new ListenerInfo(listener, false);
066 inEDTListeners.remove(searchListener);
067 normalListeners.remove(searchListener);
068 }
069
070 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
071 fireEvents(normalListeners, newSelection);
072 selection = newSelection;
073 SwingUtilities.invokeLater(edtRunnable);
074 }
075
076 private void fireEvents(List<ListenerInfo> listeners, Collection<? extends OsmPrimitive> newSelection) {
077 for (ListenerInfo listener: listeners) {
078 listener.listener.selectionChanged(newSelection);
079 }
080 }
081
082 private final Runnable edtRunnable = new Runnable() {
083 public void run() {
084 if (selection != null) {
085 fireEvents(inEDTListeners, selection);
086 }
087 }
088 };
089
090 }