001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.data.osm.event; 003 004import java.util.Collection; 005import java.util.List; 006import java.util.concurrent.CopyOnWriteArrayList; 007 008import javax.swing.SwingUtilities; 009 010import org.openstreetmap.josm.data.SelectionChangedListener; 011import org.openstreetmap.josm.data.osm.DataSet; 012import org.openstreetmap.josm.data.osm.OsmPrimitive; 013import 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 */ 020public 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) { 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 /** 051 * Constructs a new {@code SelectionEventManager}. 052 */ 053 public SelectionEventManager() { 054 DataSet.addSelectionListener(this); 055 } 056 057 public void addSelectionListener(SelectionChangedListener listener, FireMode fireMode) { 058 if (fireMode == FireMode.IN_EDT) 059 throw new UnsupportedOperationException("IN_EDT mode not supported, you probably want to use IN_EDT_CONSOLIDATED."); 060 if (fireMode == FireMode.IN_EDT || fireMode == FireMode.IN_EDT_CONSOLIDATED) { 061 inEDTListeners.addIfAbsent(new ListenerInfo(listener)); 062 } else { 063 normalListeners.addIfAbsent(new ListenerInfo(listener)); 064 } 065 } 066 067 public void removeSelectionListener(SelectionChangedListener listener) { 068 ListenerInfo searchListener = new ListenerInfo(listener); 069 inEDTListeners.remove(searchListener); 070 normalListeners.remove(searchListener); 071 } 072 073 @Override 074 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) { 075 fireEvents(normalListeners, newSelection); 076 selection = newSelection; 077 SwingUtilities.invokeLater(edtRunnable); 078 } 079 080 private void fireEvents(List<ListenerInfo> listeners, Collection<? extends OsmPrimitive> newSelection) { 081 for (ListenerInfo listener: listeners) { 082 listener.listener.selectionChanged(newSelection); 083 } 084 } 085 086 private final Runnable edtRunnable = new Runnable() { 087 @Override 088 public void run() { 089 if (selection != null) { 090 fireEvents(inEDTListeners, selection); 091 } 092 } 093 }; 094 095}