source: josm/trunk/src/org/openstreetmap/josm/data/osm/event/SelectionEventManager.java@ 12048

Last change on this file since 12048 was 12048, checked in by michael2402, 7 years ago

Add per-layer selection listeners. Make selection listener code more generic.

  • Property svn:eol-style set to native
File size: 5.6 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.osm.event;
3
4import java.util.Collection;
5import java.util.Collections;
6import java.util.HashSet;
7import java.util.List;
8import java.util.Objects;
9import java.util.Set;
10import java.util.concurrent.CopyOnWriteArrayList;
11import java.util.stream.Stream;
12
13import javax.swing.SwingUtilities;
14
15import org.openstreetmap.josm.Main;
16import org.openstreetmap.josm.data.SelectionChangedListener;
17import org.openstreetmap.josm.data.osm.DataSelectionListener;
18import org.openstreetmap.josm.data.osm.DataSet;
19import org.openstreetmap.josm.data.osm.OsmPrimitive;
20import org.openstreetmap.josm.data.osm.event.DatasetEventManager.FireMode;
21import org.openstreetmap.josm.gui.layer.MainLayerManager;
22import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
23import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
24
25/**
26 * Similar like {@link DatasetEventManager}, just for selection events.
27 *
28 * It allows to register listeners to global selection events for the selection in the current edit layer.
29 *
30 * If you want to listen to selections to a specific data layer,
31 * you can register a listener to that layer by using {@link DataSet#addSelectionListener(DataSelectionListener)}
32 *
33 * @since 2912
34 */
35public class SelectionEventManager implements DataSelectionListener, ActiveLayerChangeListener {
36
37 private static final SelectionEventManager instance = new SelectionEventManager();
38
39 /**
40 * Returns the unique instance.
41 * @return the unique instance
42 */
43 public static SelectionEventManager getInstance() {
44 return instance;
45 }
46
47 private static class ListenerInfo {
48 private final SelectionChangedListener listener;
49
50 ListenerInfo(SelectionChangedListener listener) {
51 this.listener = listener;
52 }
53
54 @Override
55 public int hashCode() {
56 return Objects.hash(listener);
57 }
58
59 @Override
60 public boolean equals(Object o) {
61 if (this == o) return true;
62 if (o == null || getClass() != o.getClass()) return false;
63 ListenerInfo that = (ListenerInfo) o;
64 return Objects.equals(listener, that.listener);
65 }
66 }
67
68 private Collection<? extends OsmPrimitive> selection;
69 private final CopyOnWriteArrayList<ListenerInfo> inEDTListeners = new CopyOnWriteArrayList<>();
70 private final CopyOnWriteArrayList<ListenerInfo> normalListeners = new CopyOnWriteArrayList<>();
71
72 /**
73 * Constructs a new {@code SelectionEventManager}.
74 */
75 protected SelectionEventManager() {
76 MainLayerManager layerManager = Main.getLayerManager();
77 // We do not allow for destructing this object.
78 // Currently, this is a singleton class, so this is not required.
79 layerManager.addAndFireActiveLayerChangeListener(this);
80 }
81
82 /**
83 * Registers a new {@code SelectionChangedListener}.
84 * @param listener listener to add
85 * @param fireMode EDT firing mode
86 */
87 public void addSelectionListener(SelectionChangedListener listener, FireMode fireMode) {
88 if (fireMode == FireMode.IN_EDT)
89 throw new UnsupportedOperationException("IN_EDT mode not supported, you probably want to use IN_EDT_CONSOLIDATED.");
90 if (fireMode == FireMode.IN_EDT || fireMode == FireMode.IN_EDT_CONSOLIDATED) {
91 inEDTListeners.addIfAbsent(new ListenerInfo(listener));
92 } else {
93 normalListeners.addIfAbsent(new ListenerInfo(listener));
94 }
95 }
96
97 /**
98 * Unregisters a {@code SelectionChangedListener}.
99 * @param listener listener to remove
100 */
101 public void removeSelectionListener(SelectionChangedListener listener) {
102 ListenerInfo searchListener = new ListenerInfo(listener);
103 inEDTListeners.remove(searchListener);
104 normalListeners.remove(searchListener);
105 }
106
107 @Override
108 public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
109 DataSet oldDataSet = e.getPreviousEditDataSet();
110 if (oldDataSet != null) {
111 // Fake a selection removal
112 // Relying on this allows components to not have to monitor layer changes.
113 // If we would not do this, e.g. the move command would have a hard time tracking which layer
114 // the last moved selection was in.
115 SelectionReplaceEvent event = new SelectionReplaceEvent(oldDataSet,
116 new HashSet<>(oldDataSet.getAllSelected()), Stream.empty());
117 selectionChanged(event);
118 oldDataSet.removeSelectionListener(this);
119 }
120 DataSet newDataSet = e.getSource().getEditDataSet();
121 if (newDataSet != null) {
122 newDataSet.addSelectionListener(this);
123 // Fake a selection add
124 SelectionReplaceEvent event = new SelectionReplaceEvent(newDataSet,
125 Collections.emptySet(), newDataSet.getAllSelected().stream());
126 selectionChanged(event);
127 }
128 }
129
130 @Override
131 public void selectionChanged(SelectionChangeEvent e) {
132 Set<OsmPrimitive> newSelection = e.getSelection();
133 fireEvents(normalListeners, newSelection);
134 selection = newSelection;
135 SwingUtilities.invokeLater(edtRunnable);
136 }
137
138 private static void fireEvents(List<ListenerInfo> listeners, Collection<? extends OsmPrimitive> newSelection) {
139 for (ListenerInfo listener: listeners) {
140 listener.listener.selectionChanged(newSelection);
141 }
142 }
143
144 private final Runnable edtRunnable = () -> {
145 if (selection != null) {
146 fireEvents(inEDTListeners, selection);
147 }
148 };
149}
Note: See TracBrowser for help on using the repository browser.