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    }