// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.data.osm.event; import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.concurrent.CopyOnWriteArrayList; import javax.swing.SwingUtilities; import org.openstreetmap.josm.data.SelectionChangedListener; import org.openstreetmap.josm.data.osm.DataSet; import org.openstreetmap.josm.data.osm.OsmPrimitive; import org.openstreetmap.josm.data.osm.event.DatasetEventManager.FireMode; /** * Similar like {@link DatasetEventManager}, just for selection events. Because currently selection changed * event are global, only FIRE_IN_EDT and FIRE_EDT_CONSOLIDATED modes are really useful * */ public class SelectionEventManager implements SelectionChangedListener { private static final SelectionEventManager instance = new SelectionEventManager(); public static SelectionEventManager getInstance() { return instance; } private static class ListenerInfo { private final SelectionChangedListener listener; ListenerInfo(SelectionChangedListener listener) { this.listener = listener; } @Override public int hashCode() { return Objects.hash(listener); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ListenerInfo that = (ListenerInfo) o; return Objects.equals(listener, that.listener); } } private Collection<? extends OsmPrimitive> selection; private final CopyOnWriteArrayList<ListenerInfo> inEDTListeners = new CopyOnWriteArrayList<>(); private final CopyOnWriteArrayList<ListenerInfo> normalListeners = new CopyOnWriteArrayList<>(); /** * Constructs a new {@code SelectionEventManager}. */ public SelectionEventManager() { DataSet.addSelectionListener(this); } public void addSelectionListener(SelectionChangedListener listener, FireMode fireMode) { if (fireMode == FireMode.IN_EDT) throw new UnsupportedOperationException("IN_EDT mode not supported, you probably want to use IN_EDT_CONSOLIDATED."); if (fireMode == FireMode.IN_EDT || fireMode == FireMode.IN_EDT_CONSOLIDATED) { inEDTListeners.addIfAbsent(new ListenerInfo(listener)); } else { normalListeners.addIfAbsent(new ListenerInfo(listener)); } } public void removeSelectionListener(SelectionChangedListener listener) { ListenerInfo searchListener = new ListenerInfo(listener); inEDTListeners.remove(searchListener); normalListeners.remove(searchListener); } @Override public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) { fireEvents(normalListeners, newSelection); selection = newSelection; SwingUtilities.invokeLater(edtRunnable); } private static void fireEvents(List<ListenerInfo> listeners, Collection<? extends OsmPrimitive> newSelection) { for (ListenerInfo listener: listeners) { listener.listener.selectionChanged(newSelection); } } private final Runnable edtRunnable = () -> { if (selection != null) { fireEvents(inEDTListeners, selection); } }; }