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 }