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

Last change on this file since 14247 was 14247, checked in by Don-vip, 6 years ago

remove stuff deprecated in June. RIP SelectionChangedListener

  • Property svn:eol-style set to native
File size: 6.4 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.osm.event;
3
4import java.util.Collections;
5import java.util.HashSet;
6import java.util.List;
7import java.util.Objects;
8import java.util.concurrent.CopyOnWriteArrayList;
9import java.util.stream.Stream;
10
11import org.openstreetmap.josm.data.osm.DataIntegrityProblemException;
12import org.openstreetmap.josm.data.osm.DataSelectionListener;
13import org.openstreetmap.josm.data.osm.DataSet;
14import org.openstreetmap.josm.gui.MainApplication;
15import org.openstreetmap.josm.gui.layer.MainLayerManager;
16import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
17import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
18import org.openstreetmap.josm.gui.util.GuiHelper;
19import org.openstreetmap.josm.tools.bugreport.BugReport;
20import org.openstreetmap.josm.tools.bugreport.ReportedException;
21
22/**
23 * Similar like {@link DatasetEventManager}, just for selection events.
24 *
25 * It allows to register listeners to global selection events for the selection in the current edit layer.
26 *
27 * If you want to listen to selections to a specific data layer,
28 * you can register a listener to that layer by using {@link DataSet#addSelectionListener(DataSelectionListener)}
29 *
30 * @since 2912
31 */
32public class SelectionEventManager implements DataSelectionListener, ActiveLayerChangeListener {
33
34 private static final SelectionEventManager INSTANCE = new SelectionEventManager();
35
36 /**
37 * Returns the unique instance.
38 * @return the unique instance
39 */
40 public static SelectionEventManager getInstance() {
41 return INSTANCE;
42 }
43
44 private interface ListenerInfo {
45 void fire(SelectionChangeEvent event);
46 }
47
48 private static class DataListenerInfo implements ListenerInfo {
49 private final DataSelectionListener listener;
50
51 DataListenerInfo(DataSelectionListener listener) {
52 this.listener = listener;
53 }
54
55 @Override
56 public void fire(SelectionChangeEvent event) {
57 listener.selectionChanged(event);
58 }
59
60 @Override
61 public int hashCode() {
62 return Objects.hash(listener);
63 }
64
65 @Override
66 public boolean equals(Object o) {
67 if (this == o) return true;
68 if (o == null || getClass() != o.getClass()) return false;
69 DataListenerInfo that = (DataListenerInfo) o;
70 return Objects.equals(listener, that.listener);
71 }
72
73 @Override
74 public String toString() {
75 return "DataListenerInfo [listener=" + listener + ']';
76 }
77 }
78
79 private final CopyOnWriteArrayList<ListenerInfo> inEDTListeners = new CopyOnWriteArrayList<>();
80 private final CopyOnWriteArrayList<ListenerInfo> immedatelyListeners = new CopyOnWriteArrayList<>();
81
82 /**
83 * Constructs a new {@code SelectionEventManager}.
84 */
85 protected SelectionEventManager() {
86 MainLayerManager layerManager = MainApplication.getLayerManager();
87 // We do not allow for destructing this object.
88 // Currently, this is a singleton class, so this is not required.
89 layerManager.addAndFireActiveLayerChangeListener(this);
90 }
91
92 /**
93 * Adds a selection listener that gets notified for selections immediately.
94 * @param listener The listener to add.
95 * @since 12098
96 */
97 public void addSelectionListener(DataSelectionListener listener) {
98 immedatelyListeners.addIfAbsent(new DataListenerInfo(listener));
99 }
100
101 /**
102 * Adds a selection listener that gets notified for selections later in the EDT thread.
103 * Events are sent in the right order but may be delayed.
104 * @param listener The listener to add.
105 * @since 12098
106 */
107 public void addSelectionListenerForEdt(DataSelectionListener listener) {
108 inEDTListeners.addIfAbsent(new DataListenerInfo(listener));
109 }
110
111 /**
112 * Unregisters a {@code DataSelectionListener}.
113 * @param listener listener to remove
114 * @since 12098
115 */
116 public void removeSelectionListener(DataSelectionListener listener) {
117 remove(new DataListenerInfo(listener));
118 }
119
120 private void remove(ListenerInfo searchListener) {
121 inEDTListeners.remove(searchListener);
122 immedatelyListeners.remove(searchListener);
123 }
124
125 @Override
126 public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
127 DataSet oldDataSet = e.getPreviousDataSet();
128 if (oldDataSet != null) {
129 // Fake a selection removal
130 // Relying on this allows components to not have to monitor layer changes.
131 // If we would not do this, e.g. the move command would have a hard time tracking which layer
132 // the last moved selection was in.
133 selectionChanged(new SelectionReplaceEvent(oldDataSet,
134 new HashSet<>(oldDataSet.getAllSelected()), Stream.empty()));
135 oldDataSet.removeSelectionListener(this);
136 }
137 DataSet newDataSet = e.getSource().getActiveDataSet();
138 if (newDataSet != null) {
139 newDataSet.addSelectionListener(this);
140 // Fake a selection add
141 selectionChanged(new SelectionReplaceEvent(newDataSet,
142 Collections.emptySet(), newDataSet.getAllSelected().stream()));
143 }
144 }
145
146 @Override
147 public void selectionChanged(SelectionChangeEvent event) {
148 fireEvent(immedatelyListeners, event);
149 try {
150 GuiHelper.runInEDTAndWaitWithException(() -> fireEvent(inEDTListeners, event));
151 } catch (ReportedException e) {
152 throw BugReport.intercept(e).put("event", event).put("inEDTListeners", inEDTListeners);
153 }
154 }
155
156 private static void fireEvent(List<ListenerInfo> listeners, SelectionChangeEvent event) {
157 for (ListenerInfo listener: listeners) {
158 try {
159 listener.fire(event);
160 } catch (DataIntegrityProblemException e) {
161 throw BugReport.intercept(e).put("event", event).put("listeners", listeners);
162 }
163 }
164 }
165
166 /**
167 * Only to be used during unit tests, to reset the state. Do not use it in plugins/other code.
168 * Called after the layer manager was reset by the test framework.
169 */
170 public void resetState() {
171 inEDTListeners.clear();
172 immedatelyListeners.clear();
173 MainApplication.getLayerManager().addAndFireActiveLayerChangeListener(this);
174 }
175}
Note: See TracBrowser for help on using the repository browser.