001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.util;
003
004import java.util.Collection;
005import java.util.Collections;
006import java.util.HashSet;
007import java.util.Iterator;
008import java.util.Set;
009
010import org.openstreetmap.josm.Main;
011import org.openstreetmap.josm.data.osm.DataSet;
012import org.openstreetmap.josm.data.osm.OsmPrimitive;
013import org.openstreetmap.josm.data.osm.Relation;
014
015/**
016 * This class stores the set of highlighted primitives and
017 * allows easy and fast change of highlighting.
018 */
019public class HighlightHelper {
020    private final Set<OsmPrimitive> highlightedPrimitives = new HashSet<>();
021
022    /**
023     * Highlight and remember given primitives
024     * @param prims - primitives to highlight/unhighlight
025     * @return {@code true} if a repaint is needed
026     */
027    public boolean highlight(Collection<? extends OsmPrimitive> prims) {
028        return highlight(prims, false);
029    }
030
031    /**
032     * Highlight and remember given primitives
033     * @param prims - primitives to highlight/unhighlight
034     * @param only - remove previous highlighting
035     * @return {@code true} if a repaint is needed
036     */
037    public boolean highlight(Collection<? extends OsmPrimitive> prims, boolean only) {
038        boolean needsRepaint = false;
039        if (only) {
040            Iterator<OsmPrimitive> it = highlightedPrimitives.iterator();
041            while (it.hasNext()) {
042                OsmPrimitive p = it.next();
043                if (!prims.contains(p)) {
044                    p.setHighlighted(false);
045                    it.remove();
046                    needsRepaint = true;
047                }
048            }
049        }
050        for (OsmPrimitive p: prims) {
051            needsRepaint |= setHighlight(p, true);
052        }
053
054        return needsRepaint;
055    }
056
057    /**
058     * Highlight and remember given primitives, forgetting previously highlighted by this instance
059     * @param prims - primitives to highlight/unhighlight
060     * @return {@code true} if a repaint is needed
061     */
062    public boolean highlightOnly(Collection<? extends OsmPrimitive> prims) {
063        return highlight(prims, true);
064    }
065
066    /**
067     * Highlight and remember given primitive, forgetting previously highlighted by this instance
068     * @param p - primitives to highlight/unhighlight
069     * @return {@code true} if a repaint is needed
070     */
071    public boolean highlightOnly(OsmPrimitive p) {
072        return highlight(Collections.singleton(p), true);
073    }
074
075    /**
076     * Highlight and remember given primitive
077     * @param p - primitive to highlight/unhighlight
078     * @param flag - true to highlight
079     * @return {@code true} if a repaint is needed
080     */
081    public boolean setHighlight(OsmPrimitive p, boolean flag) {
082        return setHighlight(p, flag, new HashSet<Relation>());
083    }
084
085    private boolean setHighlight(OsmPrimitive p, boolean flag, Set<Relation> seenRelations) {
086        if (p instanceof Relation) {
087            Relation r = (Relation) p;
088            seenRelations.add(r);
089            boolean needRepaint = false;
090            for (OsmPrimitive m : r.getMemberPrimitives()) {
091                if (!(m instanceof Relation) || !seenRelations.contains(m)) {
092                    needRepaint |= setHighlight(m, flag, seenRelations);
093                }
094            }
095            return needRepaint;
096        } else if (flag) {
097            if (highlightedPrimitives.add(p)) {
098                p.setHighlighted(true);
099                return true;
100            }
101        } else {
102            if (highlightedPrimitives.remove(p)) {
103                p.setHighlighted(false);
104                return true;
105            }
106        }
107        return false;
108    }
109
110    /**
111     * Clear highlighting of all remembered primitives
112     */
113    public void clear() {
114        for (OsmPrimitive p: highlightedPrimitives) {
115            p.setHighlighted(false);
116        }
117        highlightedPrimitives.clear();
118    }
119
120    /**
121     * Slow method to import all currently highlighted primitives into this instance
122     */
123    public void findAllHighlighted() {
124        DataSet ds = Main.getLayerManager().getEditDataSet();
125        if (ds != null) {
126            highlightedPrimitives.addAll(ds.allNonDeletedPrimitives());
127        }
128    }
129
130    /**
131     * Slow method to remove highlights from all primitives
132     */
133    public static void clearAllHighlighted() {
134        DataSet ds = Main.getLayerManager().getEditDataSet();
135        if (ds != null) {
136            for (OsmPrimitive p: ds.allNonDeletedPrimitives()) {
137                p.setHighlighted(false);
138            }
139        }
140    }
141}