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, 5 months 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.