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

Last change on this file since 12109 was 12109, checked in by michael2402, 19 months ago

SelectionEventManager: Typo / checkstyle

  • Property svn:eol-style set to native
File size: 7.9 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 javax.swing.SwingUtilities;
12
13import org.openstreetmap.josm.Main;
14import org.openstreetmap.josm.data.SelectionChangedListener;
15import org.openstreetmap.josm.data.osm.DataSelectionListener;
16import org.openstreetmap.josm.data.osm.DataSet;
17import org.openstreetmap.josm.data.osm.event.DatasetEventManager.FireMode;
18import org.openstreetmap.josm.gui.layer.MainLayerManager;
19import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
20import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
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 abstract static class AbstractListenerInfo {
45        abstract void fire(SelectionChangeEvent event);
46    }
47
48    private static class ListenerInfo extends AbstractListenerInfo {
49        private final SelectionChangedListener listener;
50
51        ListenerInfo(SelectionChangedListener listener) {
52            this.listener = listener;
53        }
54
55        @Override
56        void fire(SelectionChangeEvent event) {
57            listener.selectionChanged(event.getSelection());
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            ListenerInfo that = (ListenerInfo) o;
70            return Objects.equals(listener, that.listener);
71        }
72    }
73
74    private static class DataListenerInfo extends AbstractListenerInfo {
75        private final DataSelectionListener listener;
76
77        DataListenerInfo(DataSelectionListener listener) {
78            this.listener = listener;
79        }
80
81        @Override
82        void fire(SelectionChangeEvent event) {
83            listener.selectionChanged(event);
84        }
85
86        @Override
87        public int hashCode() {
88            return Objects.hash(listener);
89        }
90
91        @Override
92        public boolean equals(Object o) {
93            if (this == o) return true;
94            if (o == null || getClass() != o.getClass()) return false;
95            DataListenerInfo that = (DataListenerInfo) o;
96            return Objects.equals(listener, that.listener);
97        }
98    }
99
100    private final CopyOnWriteArrayList<AbstractListenerInfo> inEDTListeners = new CopyOnWriteArrayList<>();
101    private final CopyOnWriteArrayList<AbstractListenerInfo> immedatelyListeners = new CopyOnWriteArrayList<>();
102
103    /**
104     * Constructs a new {@code SelectionEventManager}.
105     */
106    protected SelectionEventManager() {
107        MainLayerManager layerManager = Main.getLayerManager();
108        // We do not allow for destructing this object.
109        // Currently, this is a singleton class, so this is not required.
110        layerManager.addAndFireActiveLayerChangeListener(this);
111    }
112
113    /**
114     * Registers a new {@code SelectionChangedListener}.
115     *
116     * It is preferred to add a DataSelectionListener - that listener will receive more information about the event.
117     * @param listener listener to add
118     * @param fireMode Set this to IN_EDT_CONSOLIDATED if you want the event to be fired in the EDT thread.
119     *                 Set it to IMMEDIATELY if you want the event to fire in the thread that caused the selection update.
120     */
121    public void addSelectionListener(SelectionChangedListener listener, FireMode fireMode) {
122        if (fireMode == FireMode.IN_EDT) {
123            throw new UnsupportedOperationException("IN_EDT mode not supported, you probably want to use IN_EDT_CONSOLIDATED.");
124        } else if (fireMode == FireMode.IN_EDT_CONSOLIDATED) {
125            inEDTListeners.addIfAbsent(new ListenerInfo(listener));
126        } else {
127            immedatelyListeners.addIfAbsent(new ListenerInfo(listener));
128        }
129    }
130
131    /**
132     * Adds a selection listener that gets notified for selections immediately.
133     * @param listener The listener to add.
134     * @since 12098
135     */
136    public void addSelectionListener(DataSelectionListener listener) {
137        immedatelyListeners.addIfAbsent(new DataListenerInfo(listener));
138    }
139
140    /**
141     * Adds a selection listener that gets notified for selections later in the EDT thread.
142     * Events are sent in the right order but may be delayed.
143     * @param listener The listener to add.
144     * @since 12098
145     */
146    public void addSelectionListenerForEdt(DataSelectionListener listener) {
147        inEDTListeners.addIfAbsent(new DataListenerInfo(listener));
148    }
149
150    /**
151     * Unregisters a {@code SelectionChangedListener}.
152     * @param listener listener to remove
153     */
154    public void removeSelectionListener(SelectionChangedListener listener) {
155        remove(new ListenerInfo(listener));
156    }
157
158    /**
159     * Unregisters a {@code DataSelectionListener}.
160     * @param listener listener to remove
161     * @since 12098
162     */
163    public void removeSelectionListener(DataSelectionListener listener) {
164        remove(new DataListenerInfo(listener));
165    }
166
167    private void remove(AbstractListenerInfo searchListener) {
168        inEDTListeners.remove(searchListener);
169        immedatelyListeners.remove(searchListener);
170    }
171
172    @Override
173    public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
174        DataSet oldDataSet = e.getPreviousEditDataSet();
175        if (oldDataSet != null) {
176            // Fake a selection removal
177            // Relying on this allows components to not have to monitor layer changes.
178            // If we would not do this, e.g. the move command would have a hard time tracking which layer
179            // the last moved selection was in.
180            SelectionReplaceEvent event = new SelectionReplaceEvent(oldDataSet,
181                    new HashSet<>(oldDataSet.getAllSelected()), Stream.empty());
182            selectionChanged(event);
183            oldDataSet.removeSelectionListener(this);
184        }
185        DataSet newDataSet = e.getSource().getEditDataSet();
186        if (newDataSet != null) {
187            newDataSet.addSelectionListener(this);
188            // Fake a selection add
189            SelectionReplaceEvent event = new SelectionReplaceEvent(newDataSet,
190                    Collections.emptySet(), newDataSet.getAllSelected().stream());
191            selectionChanged(event);
192        }
193    }
194
195    @Override
196    public void selectionChanged(SelectionChangeEvent event) {
197        fireEvent(immedatelyListeners, event);
198        SwingUtilities.invokeLater(() -> fireEvent(inEDTListeners, event));
199    }
200
201    private static void fireEvent(List<AbstractListenerInfo> listeners, SelectionChangeEvent event) {
202        for (AbstractListenerInfo listener: listeners) {
203            listener.fire(event);
204        }
205    }
206
207    /**
208     * Only to be used during unit tests, to reset the state. Do not use it in plugins/other code.
209     * Called after the layer manager was reset by the test framework.
210     */
211    public void resetState() {
212        inEDTListeners.clear();
213        immedatelyListeners.clear();
214        Main.getLayerManager().addAndFireActiveLayerChangeListener(this);
215    }
216}
Note: See TracBrowser for help on using the repository browser.