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