Changeset 12098 in josm


Ignore:
Timestamp:
2017-05-10T23:00:16+02:00 (2 years ago)
Author:
michael2402
Message:

Allow to globally add the new selection listeners that get detailed events.

Location:
trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/SelectionChangedListener.java

    r10600 r12098  
    44import java.util.Collection;
    55
     6import org.openstreetmap.josm.data.osm.DataSelectionListener;
    67import org.openstreetmap.josm.data.osm.OsmPrimitive;
     8import org.openstreetmap.josm.data.osm.event.SelectionEventManager;
    79
    810/**
     
    1416 * Swing event queue and packed together. So only one selection changed event
    1517 * is issued within a one message dispatch routine.
     18 *
     19 * @see DataSelectionListener For a more advanced listener class.
     20 * @see SelectionEventManager For managing your selection events.
    1621 *
    1722 * @author imi
  • trunk/src/org/openstreetmap/josm/data/osm/event/SelectionEventManager.java

    r12096 r12098  
    22package org.openstreetmap.josm.data.osm.event;
    33
    4 import java.util.Collection;
    54import java.util.Collections;
    65import java.util.HashSet;
    76import java.util.List;
    87import java.util.Objects;
    9 import java.util.Set;
    108import java.util.concurrent.CopyOnWriteArrayList;
    119import java.util.stream.Stream;
     
    1715import org.openstreetmap.josm.data.osm.DataSelectionListener;
    1816import org.openstreetmap.josm.data.osm.DataSet;
    19 import org.openstreetmap.josm.data.osm.OsmPrimitive;
    2017import org.openstreetmap.josm.data.osm.event.DatasetEventManager.FireMode;
    2118import org.openstreetmap.josm.gui.layer.MainLayerManager;
     
    4542    }
    4643
    47     private static class ListenerInfo {
     44    private abstract static class AbstractListenerInfo {
     45        abstract void fire(SelectionChangeEvent event);
     46    }
     47
     48    private static class ListenerInfo extends AbstractListenerInfo {
    4849        private final SelectionChangedListener listener;
    4950
    5051        ListenerInfo(SelectionChangedListener listener) {
    5152            this.listener = listener;
     53        }
     54
     55        @Override
     56        void fire(SelectionChangeEvent event) {
     57            listener.selectionChanged(event.getSelection());
    5258        }
    5359
     
    6672    }
    6773
    68     private Collection<? extends OsmPrimitive> selection;
    69     private final CopyOnWriteArrayList<ListenerInfo> inEDTListeners = new CopyOnWriteArrayList<>();
    70     private final CopyOnWriteArrayList<ListenerInfo> normalListeners = new CopyOnWriteArrayList<>();
     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<>();
    71102
    72103    /**
     
    82113    /**
    83114     * Registers a new {@code SelectionChangedListener}.
     115     *
     116     * It is preferred to add a DataSelectionListener - that listener will receive more information about the event.
    84117     * @param listener listener to add
    85118     * @param fireMode Set this to IN_EDT_CONSOLIDATED if you want the event to be fired in the EDT thread.
     
    92125            inEDTListeners.addIfAbsent(new ListenerInfo(listener));
    93126        } else {
    94             normalListeners.addIfAbsent(new ListenerInfo(listener));
    95         }
     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));
    96148    }
    97149
     
    101153     */
    102154    public void removeSelectionListener(SelectionChangedListener listener) {
    103         ListenerInfo searchListener = new ListenerInfo(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) {
    104168        inEDTListeners.remove(searchListener);
    105         normalListeners.remove(searchListener);
     169        immedatelyListeners.remove(searchListener);
    106170    }
    107171
     
    130194
    131195    @Override
    132     public void selectionChanged(SelectionChangeEvent e) {
    133         Set<OsmPrimitive> newSelection = e.getSelection();
    134         fireEvents(normalListeners, newSelection);
    135         selection = newSelection;
    136         SwingUtilities.invokeLater(edtRunnable);
    137     }
    138 
    139     private static void fireEvents(List<ListenerInfo> listeners, Collection<? extends OsmPrimitive> newSelection) {
    140         for (ListenerInfo listener: listeners) {
    141             listener.listener.selectionChanged(newSelection);
    142         }
    143     }
    144 
    145     private final Runnable edtRunnable = () -> {
    146         if (selection != null) {
    147             fireEvents(inEDTListeners, selection);
    148         }
    149     };
     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    }
    150206
    151207    /**
     
    155211    public void resetState() {
    156212        inEDTListeners.clear();
    157         normalListeners.clear();
     213        immedatelyListeners.clear();
    158214        Main.getLayerManager().addAndFireActiveLayerChangeListener(this);
    159215    }
  • trunk/test/unit/org/openstreetmap/josm/data/osm/event/SelectionEventManagerTest.java

    r12060 r12098  
    33
    44import static org.junit.Assert.assertEquals;
    5 import static org.junit.Assert.assertNull;
    65
    76import java.util.Arrays;
    87import java.util.Collection;
    98import java.util.HashSet;
     9import java.util.List;
    1010
    1111import org.junit.Rule;
     
    1414import org.openstreetmap.josm.command.CommandTest.CommandTestDataWithRelation;
    1515import org.openstreetmap.josm.data.SelectionChangedListener;
     16import org.openstreetmap.josm.data.osm.DataSelectionListener;
    1617import org.openstreetmap.josm.data.osm.OsmPrimitive;
    1718import org.openstreetmap.josm.data.osm.event.DatasetEventManager.FireMode;
     19import org.openstreetmap.josm.gui.util.GuiHelper;
    1820import org.openstreetmap.josm.testutils.JOSMTestRules;
    1921
     
    2628 */
    2729public class SelectionEventManagerTest {
    28     private final class SelectionListener implements SelectionChangedListener {
     30    private final class SelectionListener implements SelectionChangedListener, DataSelectionListener {
    2931        private Collection<? extends OsmPrimitive> newSelection;
     32        private final String name;
     33
     34        public SelectionListener(String name) {
     35            this.name = name;
     36        }
    3037
    3138        @Override
    3239        public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
    3340            this.newSelection = newSelection;
     41        }
     42
     43        @Override
     44        public void selectionChanged(SelectionChangeEvent event) {
     45            this.newSelection = event.getSelection();
    3446        }
    3547    }
     
    5264        assertEquals(testData1.layer, Main.getLayerManager().getEditLayer());
    5365
    54         SelectionListener listener = new SelectionListener();
    55         SelectionEventManager.getInstance().addSelectionListener(listener, FireMode.IMMEDIATELY);
    56         assertNull(listener.newSelection);
     66        SelectionListener listener1 = new SelectionListener("IMMEDIATELY");
     67        SelectionListener listener2 = new SelectionListener("IN_EDT_CONSOLIDATED");
     68        SelectionListener listener3 = new SelectionListener("normal");
     69        SelectionListener listener4 = new SelectionListener("edt");
     70        SelectionEventManager instance = SelectionEventManager.getInstance();
     71        instance.addSelectionListener(listener1, FireMode.IMMEDIATELY);
     72        instance.addSelectionListener(listener2, FireMode.IN_EDT_CONSOLIDATED);
     73        instance.addSelectionListener(listener3);
     74        instance.addSelectionListenerForEdt(listener4);
     75        List<SelectionListener> listeners = Arrays.asList(listener1, listener2, listener3, listener4);
     76        assertSelectionEquals(listeners, null);
    5777
    5878        // active layer, should change
    5979        testData1.layer.data.setSelected(testData1.existingNode.getPrimitiveId());
    60         assertEquals(new HashSet<OsmPrimitive>(Arrays.asList(testData1.existingNode)), listener.newSelection);
     80        assertSelectionEquals(listeners, new HashSet<OsmPrimitive>(Arrays.asList(testData1.existingNode)));
    6181
    62         listener.newSelection = null;
    6382        testData1.layer.data.clearSelection(testData1.existingNode.getPrimitiveId());
    64         assertEquals(new HashSet<OsmPrimitive>(Arrays.asList()), listener.newSelection);
     83        assertSelectionEquals(listeners, new HashSet<OsmPrimitive>(Arrays.asList()));
    6584
    66         listener.newSelection = null;
    6785        testData1.layer.data.addSelected(testData1.existingNode2.getPrimitiveId());
    68         assertEquals(new HashSet<OsmPrimitive>(Arrays.asList(testData1.existingNode2)), listener.newSelection);
     86        assertSelectionEquals(listeners, new HashSet<OsmPrimitive>(Arrays.asList(testData1.existingNode2)));
    6987
    7088        // changing to other dataset should trigger a empty selection
    71         listener.newSelection = null;
    7289        Main.getLayerManager().setActiveLayer(testData2.layer);
    73         assertEquals(new HashSet<OsmPrimitive>(Arrays.asList()), listener.newSelection);
     90        assertSelectionEquals(listeners, new HashSet<OsmPrimitive>(Arrays.asList()));
    7491
    7592        // This should not trigger anything, since the layer is not active any more.
    76         listener.newSelection = null;
    7793        testData1.layer.data.clearSelection(testData1.existingNode.getPrimitiveId());
    78         assertNull(listener.newSelection);
     94        assertSelectionEquals(listeners, null);
     95
     96        testData2.layer.data.setSelected(testData2.existingNode.getPrimitiveId());
     97        assertSelectionEquals(listeners, new HashSet<OsmPrimitive>(Arrays.asList(testData2.existingNode)));
     98
     99        // removal
     100        instance.removeSelectionListener((SelectionChangedListener) listener1);
     101        instance.removeSelectionListener((SelectionChangedListener) listener2);
     102        instance.removeSelectionListener((DataSelectionListener) listener3);
     103        instance.removeSelectionListener((DataSelectionListener) listener4);
     104
     105        // no event triggered now
     106        testData2.layer.data.setSelected(testData2.existingNode2.getPrimitiveId());
     107        assertSelectionEquals(listeners, null);
     108    }
     109
     110    private void assertSelectionEquals(List<SelectionListener> listeners, Object should) {
     111        // sync
     112        GuiHelper.runInEDTAndWait(() -> {});
     113        for (SelectionListener listener : listeners) {
     114            assertEquals(listener.name, should, listener.newSelection);
     115            listener.newSelection = null;
     116        }
    79117    }
    80118
Note: See TracChangeset for help on using the changeset viewer.