001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.util;
003
004import java.awt.Component;
005import java.awt.Cursor;
006import java.util.Iterator;
007import java.util.LinkedHashMap;
008import java.util.Objects;
009import java.util.concurrent.CopyOnWriteArrayList;
010
011/**
012 * This class manages multiple cursors for multiple components.
013 * All components share the same cursor that was last set using {@link #setNewCursor(Cursor, Object)}
014 *
015 * @author Michael Zangl
016 */
017public class CursorManager {
018
019    private final LinkedHashMap<Object, Cursor> cursors = new LinkedHashMap<>();
020    private final CopyOnWriteArrayList<Component> components = new CopyOnWriteArrayList<>();
021
022    /**
023     * Creates a new NavigationCursorManager
024     * @param forComponent The initial component the cursor should be managed for.
025     */
026    public CursorManager(Component forComponent) {
027        addComponent(forComponent);
028    }
029
030    /**
031     * Adds a component that this manager should send cursor changes to.
032     * @param forComponent The component.
033     */
034    public synchronized void addComponent(Component forComponent) {
035        components.addIfAbsent(forComponent);
036        forComponent.setCursor(getCurrentCursor());
037    }
038
039    /**
040     * Removes a component that this manager should send cursor changes to. The current cursor is not reset.
041     * @param forComponent The component.
042     */
043    public synchronized void removeComponent(Component forComponent) {
044        components.remove(forComponent);
045    }
046
047    /**
048     * Set new cursor.
049     * @param cursor The new cursor to use.
050     * @param reference A reference object that can be passed to the next set/reset calls to identify the caller.
051     */
052    public synchronized void setNewCursor(Cursor cursor, Object reference) {
053        Objects.requireNonNull(reference, "Cannot register a cursor that can never be removed.");
054        // re-insert to allow overriding.
055        cursors.remove(reference);
056        cursors.put(reference, cursor);
057        updateCursor();
058    }
059
060    /**
061     * Remove the new cursor that was set with the given reference object. and reset to previous
062     * @param reference A reference object that can be passed to the next set/reset calls to identify the caller.
063     */
064    public synchronized void resetCursor(Object reference) {
065        if (reference == null) {
066            return;
067        }
068        cursors.remove(reference);
069        updateCursor();
070    }
071
072    private void updateCursor() {
073        Cursor cursor = getCurrentCursor();
074        for (Component c : components) {
075            c.setCursor(cursor);
076        }
077    }
078
079    private Cursor getCurrentCursor() {
080        Iterator<Cursor> it = cursors.values().iterator();
081        Cursor cursor = null;
082        while (it.hasNext()) {
083            cursor = it.next();
084        }
085        return cursor;
086    }
087
088}