001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.layer;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.awt.event.ActionEvent;
007import java.awt.event.KeyEvent;
008import java.lang.ref.WeakReference;
009import java.util.List;
010
011import javax.swing.AbstractAction;
012
013import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
014import org.openstreetmap.josm.gui.util.MultikeyActionsHandler;
015import org.openstreetmap.josm.gui.util.MultikeyShortcutAction;
016import org.openstreetmap.josm.tools.Shortcut;
017
018/**
019 * Manages actions to jump from one marker to the next for layers that show markers
020 * ({@link org.openstreetmap.josm.gui.layer.geoimage.GeoImageLayer},
021 * {@link org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer}).
022 *
023 * Registers global multi-key shortcuts and offers actions for the right-click menu of
024 * the layers.
025 */
026public final class JumpToMarkerActions {
027
028    /**
029     * Interface for a layer that displays markers and supports jumping from
030     * one marker to the next.
031     */
032    public interface JumpToMarkerLayer {
033        /**
034         * Jump (move the viewport) to the next marker.
035         */
036        void jumpToNextMarker();
037
038        /**
039         * Jump (move the viewport) to the previous marker.
040         */
041        void jumpToPreviousMarker();
042    }
043
044    private JumpToMarkerActions() {
045        // Hide default constructor for utils classes
046    }
047
048    private static volatile JumpToNextMarker jumpToNextMarkerAction;
049    private static volatile JumpToPreviousMarker jumpToPreviousMarkerAction;
050
051    /**
052     * Initialize the actions, register shortcuts.
053     */
054    public static void initialize() {
055        jumpToNextMarkerAction = new JumpToNextMarker(null);
056        jumpToPreviousMarkerAction = new JumpToPreviousMarker(null);
057        MultikeyActionsHandler.getInstance().addAction(jumpToNextMarkerAction);
058        MultikeyActionsHandler.getInstance().addAction(jumpToPreviousMarkerAction);
059    }
060
061    /**
062     * Unregister the actions.
063     */
064    public static void unregisterActions() {
065        MultikeyActionsHandler.getInstance().removeAction(jumpToNextMarkerAction);
066        MultikeyActionsHandler.getInstance().removeAction(jumpToPreviousMarkerAction);
067    }
068
069    private abstract static class JumpToMarker extends AbstractAction implements MultikeyShortcutAction {
070
071        private final transient JumpToMarkerLayer layer;
072        private final transient Shortcut multikeyShortcut;
073        private transient WeakReference<Layer> lastLayer;
074
075        JumpToMarker(JumpToMarkerLayer layer, Shortcut shortcut) {
076            this.layer = layer;
077            this.multikeyShortcut = shortcut;
078            this.multikeyShortcut.setAccelerator(this);
079        }
080
081        protected final void setLastLayer(Layer l) {
082            lastLayer = new WeakReference<>(l);
083        }
084
085        @Override
086        public Shortcut getMultikeyShortcut() {
087            return multikeyShortcut;
088        }
089
090        @Override
091        public void actionPerformed(ActionEvent e) {
092            execute(layer);
093        }
094
095        @Override
096        public void executeMultikeyAction(int index, boolean repeat) {
097            Layer l = LayerListDialog.getLayerForIndex(index);
098            if (l != null) {
099                if (l instanceof JumpToMarkerLayer) {
100                    execute((JumpToMarkerLayer) l);
101                }
102            } else if (repeat && lastLayer != null) {
103                l = lastLayer.get();
104                if (LayerListDialog.isLayerValid(l) && l instanceof JumpToMarkerLayer) {
105                    execute((JumpToMarkerLayer) l);
106                }
107            }
108        }
109
110        protected abstract void execute(JumpToMarkerLayer l);
111
112        @Override
113        public List<MultikeyInfo> getMultikeyCombinations() {
114            return LayerListDialog.getLayerInfoByClass(JumpToMarkerLayer.class);
115        }
116
117        @Override
118        public MultikeyInfo getLastMultikeyAction() {
119            if (lastLayer != null)
120                return LayerListDialog.getLayerInfo(lastLayer.get());
121            else
122                return null;
123        }
124    }
125
126    public static final class JumpToNextMarker extends JumpToMarker {
127
128        public JumpToNextMarker(JumpToMarkerLayer layer) {
129            super(layer, Shortcut.registerShortcut("core_multikey:nextMarker", tr("Multikey: {0}", tr("Next marker")),
130                    KeyEvent.VK_J, Shortcut.ALT_CTRL));
131            putValue(SHORT_DESCRIPTION, tr("Jump to next marker"));
132            putValue(NAME, tr("Jump to next marker"));
133        }
134
135        @Override
136        protected void execute(JumpToMarkerLayer l) {
137            l.jumpToNextMarker();
138            setLastLayer((Layer) l);
139        }
140    }
141
142    public static final class JumpToPreviousMarker extends JumpToMarker {
143
144        public JumpToPreviousMarker(JumpToMarkerLayer layer) {
145            super(layer, Shortcut.registerShortcut("core_multikey:previousMarker", tr("Multikey: {0}", tr("Previous marker")),
146                    KeyEvent.VK_P, Shortcut.ALT_CTRL));
147            putValue(SHORT_DESCRIPTION, tr("Jump to previous marker"));
148            putValue(NAME, tr("Jump to previous marker"));
149        }
150
151        @Override
152        protected void execute(JumpToMarkerLayer l) {
153            l.jumpToPreviousMarker();
154            setLastLayer((Layer) l);
155        }
156    }
157}