Index: trunk/src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java	(revision 4594)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java	(revision 4595)
@@ -16,4 +16,5 @@
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -64,4 +65,7 @@
 import org.openstreetmap.josm.tools.CheckParameterUtil;
 import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.MultikeyActionsHandler;
+import org.openstreetmap.josm.tools.MultikeyShortcutAction;
+import org.openstreetmap.josm.tools.MultikeyShortcutAction.MultikeyInfo;
 import org.openstreetmap.josm.tools.Shortcut;
 
@@ -85,4 +89,6 @@
             throw new IllegalStateException("Dialog was already created");
         instance = new LayerListDialog(mapFrame);
+
+        MultikeyActionsHandler.getInstance().addAction(instance.new ShowHideLayerAction(false));
     }
 
@@ -113,4 +119,5 @@
     ActivateLayerAction activateLayerAction;
 
+    //TODO This duplicates ShowHide actions functionality
     /** stores which layer index to toggle and executes the ShowHide action if the layer is present */
     private final class ToggleLayerIndexVisibility extends AbstractAction {
@@ -123,5 +130,5 @@
             final Layer l = model.getLayer(model.getRowCount() - layerIndex);
             if(l != null) {
-                new ShowHideLayerAction(l).actionPerformed(e);
+                l.toggleVisible();
             }
         }
@@ -258,12 +265,12 @@
 
         createLayout(layerList, true, Arrays.asList(new SideButton[] {
-            new SideButton(moveUpAction),
-            new SideButton(moveDownAction),
-            new SideButton(activateLayerAction),
-            new SideButton(showHideLayerAction),
-            opacityButton,
-            new SideButton(mergeLayerAction),
-            new SideButton(duplicateLayerAction),
-            new SideButton(deleteLayerAction, false)
+                new SideButton(moveUpAction),
+                new SideButton(moveDownAction),
+                new SideButton(activateLayerAction),
+                new SideButton(showHideLayerAction),
+                opacityButton,
+                new SideButton(mergeLayerAction),
+                new SideButton(duplicateLayerAction),
+                new SideButton(deleteLayerAction, false)
         }));
 
@@ -420,21 +427,7 @@
     }
 
-    public final class ShowHideLayerAction extends AbstractAction implements IEnabledStateUpdating, LayerAction {
-        private  Layer layer;
-
-        /**
-         * Creates a {@see ShowHideLayerAction} which toggle the visibility of
-         * a specific layer.
-         *
-         * @param layer  the layer. Must not be null.
-         * @exception IllegalArgumentException thrown, if layer is null
-         */
-        public ShowHideLayerAction(Layer layer) throws IllegalArgumentException {
-            this();
-            putValue(NAME, tr("Show/Hide"));
-            CheckParameterUtil.ensureParameterNotNull(layer, "layer");
-            this.layer = layer;
-            updateEnabledState();
-        }
+    public final class ShowHideLayerAction extends AbstractAction implements IEnabledStateUpdating, LayerAction, MultikeyShortcutAction {
+
+        private WeakReference<Layer> lastLayer;
 
         /**
@@ -443,17 +436,39 @@
          *
          */
-        public ShowHideLayerAction() {
+        public ShowHideLayerAction(boolean init) {
             putValue(SMALL_ICON, ImageProvider.get("dialogs", "showhide"));
             putValue(SHORT_DESCRIPTION, tr("Toggle visible state of the selected layer."));
             putValue("help", HelpUtil.ht("/Dialog/LayerList#ShowHideLayer"));
-            updateEnabledState();
+            putValue(ACCELERATOR_KEY, Shortcut.registerShortcut("core_multikey:showHideLayer", "", 'S', Shortcut.GROUP_DIRECT, KeyEvent.ALT_DOWN_MASK + KeyEvent.CTRL_DOWN_MASK).getKeyStroke());
+            if (init) {
+                updateEnabledState();
+            }
+        }
+
+        public ShowHideLayerAction() {
+            this(true);
         }
 
         @Override
         public void actionPerformed(ActionEvent e) {
-            if (layer != null) {
-                layer.toggleVisible();
-            } else {
-                for(Layer l : model.getSelectedLayers()) {
+            for(Layer l : model.getSelectedLayers()) {
+                l.toggleVisible();
+            }
+        }
+
+        @Override
+        public void executeMultikeyAction(int index) {
+            Layer l = LayerListDialog.getLayerForIndex(index);
+            if (l != null) {
+                l.toggleVisible();
+                lastLayer = new WeakReference<Layer>(l);
+            }
+        }
+
+        @Override
+        public void repeateLastMultikeyAction() {
+            if (lastLayer != null) {
+                Layer l = lastLayer.get();
+                if (LayerListDialog.isLayerValid(l)) {
                     l.toggleVisible();
                 }
@@ -463,9 +478,5 @@
         @Override
         public void updateEnabledState() {
-            if (layer == null) {
-                setEnabled(! getModel().getSelectedLayers().isEmpty());
-            } else {
-                setEnabled(true);
-            }
+            setEnabled(!model.getSelectedLayers().isEmpty());
         }
 
@@ -488,4 +499,16 @@
         public int hashCode() {
             return getClass().hashCode();
+        }
+
+        @Override
+        public List<MultikeyInfo> getMultikeyCombinations() {
+            return LayerListDialog.getLayerInfoByClass(Layer.class);
+        }
+
+        @Override
+        public MultikeyInfo getLastMultikeyAction() {
+            if (lastLayer != null)
+                return LayerListDialog.getLayerInfo(lastLayer.get());
+            return null;
         }
     }
@@ -905,6 +928,7 @@
                     }
                 }
-                if(c == null)
+                if(c == null) {
                     c = Main.pref.getUIColor(isSelected ? "Table.selectionForeground" : "Table.foreground");
+                }
                 label.setForeground(c);
             }
@@ -1459,5 +1483,5 @@
      */
     public ShowHideLayerAction createShowHideLayerAction() {
-        ShowHideLayerAction act = new ShowHideLayerAction();
+        ShowHideLayerAction act = new ShowHideLayerAction(true);
         act.putValue(Action.NAME, tr("Show/Hide"));
         return act;
@@ -1497,3 +1521,61 @@
         return new MergeAction(layer);
     }
+
+    public static Layer getLayerForIndex(int index) {
+
+        if (!Main.isDisplayingMapView())
+            return null;
+
+        List<Layer> layers = Main.map.mapView.getAllLayersAsList();
+
+        if (index < layers.size())
+            return layers.get(index);
+        else
+            return null;
+    }
+
+    public static List<MultikeyInfo> getLayerInfoByClass(Class<? extends Layer> layerClass) {
+
+        List<MultikeyInfo> result = new ArrayList<MultikeyShortcutAction.MultikeyInfo>();
+
+        if (!Main.isDisplayingMapView())
+            return result;
+
+        List<Layer> layers = Main.map.mapView.getAllLayersAsList();
+
+        int index = 0;
+        for (Layer l: layers) {
+            if (layerClass.isAssignableFrom(l.getClass())) {
+                result.add(new MultikeyInfo(index, l.getName()));
+            }
+            index++;
+        }
+
+        return result;
+    }
+
+    public static boolean isLayerValid(Layer l) {
+        if (l == null)
+            return false;
+
+        if (!Main.isDisplayingMapView())
+            return false;
+
+        return Main.map.mapView.getAllLayersAsList().indexOf(l) >= 0;
+    }
+
+    public static MultikeyInfo getLayerInfo(Layer l) {
+
+        if (l == null)
+            return null;
+
+        if (!Main.isDisplayingMapView())
+            return null;
+
+        int index = Main.map.mapView.getAllLayersAsList().indexOf(l);
+        if (index < 0)
+            return null;
+
+        return new MultikeyInfo(index, l.getName());
+    }
 }
Index: trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java	(revision 4594)
+++ trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java	(revision 4595)
@@ -12,7 +12,9 @@
 import java.awt.Point;
 import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
 import java.io.File;
+import java.lang.ref.WeakReference;
 import java.net.URL;
 import java.util.ArrayList;
@@ -42,4 +44,7 @@
 import org.openstreetmap.josm.tools.AudioPlayer;
 import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.MultikeyActionsHandler;
+import org.openstreetmap.josm.tools.MultikeyShortcutAction;
+import org.openstreetmap.josm.tools.Shortcut;
 
 /**
@@ -59,9 +64,9 @@
      * A list of markers.
      */
-    public final Collection<Marker> data;
+    public final List<Marker> data;
     private boolean mousePressed = false;
     public GpxLayer fromLayer = null;
-
-    @SuppressWarnings("unchecked")
+    private Marker currentMarker;
+
     public MarkerLayer(GpxData indata, String name, File associatedFile, GpxLayer fromLayer) {
 
@@ -125,4 +130,10 @@
     }
 
+    static {
+        MultikeyActionsHandler.getInstance().addAction(new JumpToNextMarker(null));
+        MultikeyActionsHandler.getInstance().addAction(new JumpToPreviousMarker(null));
+    }
+
+
     /**
      * Return a static icon.
@@ -200,4 +211,6 @@
             components.add (new MoveAudio());
         }
+        components.add(new JumpToNextMarker(this));
+        components.add(new JumpToPreviousMarker(this));
         components.add(new RenameLayerAction(getAssociatedFile(), this));
         components.add(SeparatorLayerAction.INSTANCE);
@@ -292,4 +305,39 @@
         data.addAll(newData);
         return ret;
+    }
+
+    public void jumpToNextMarker() {
+        if (currentMarker == null) {
+            currentMarker = data.get(0);
+        } else {
+            boolean foundCurrent = false;
+            for (Marker m: data) {
+                if (foundCurrent) {
+                    currentMarker = m;
+                    break;
+                } else if (currentMarker == m) {
+                    foundCurrent = true;
+                }
+            }
+        }
+        Main.map.mapView.zoomTo(currentMarker.getEastNorth());
+    }
+
+    public void jumpToPreviousMarker() {
+        if (currentMarker == null) {
+            currentMarker = data.get(data.size() - 1);
+        } else {
+            boolean foundCurrent = false;
+            for (int i=data.size() - 1; i>=0; i--) {
+                Marker m = data.get(i);
+                if (foundCurrent) {
+                    currentMarker = m;
+                    break;
+                } else if (currentMarker == m) {
+                    foundCurrent = true;
+                }
+            }
+        }
+        Main.map.mapView.zoomTo(currentMarker.getEastNorth());
     }
 
@@ -393,4 +441,116 @@
     }
 
+    public static final class JumpToNextMarker extends AbstractAction implements MultikeyShortcutAction {
+
+        private final MarkerLayer layer;
+        private WeakReference<MarkerLayer> lastLayer;
+
+        public JumpToNextMarker(MarkerLayer layer) {
+            putValue(ACCELERATOR_KEY, Shortcut.registerShortcut("core_multikey:nextMarker", "", 'J', Shortcut.GROUP_DIRECT, KeyEvent.ALT_DOWN_MASK + KeyEvent.CTRL_DOWN_MASK).getKeyStroke());
+            putValue(SHORT_DESCRIPTION, tr("Jump to next marker"));
+            putValue(NAME, tr("Jump to next marker"));
+
+            this.layer = layer;
+        }
+
+        @Override
+        public void actionPerformed(ActionEvent e) {
+            execute(layer);
+        }
+
+        @Override
+        public void executeMultikeyAction(int index) {
+            Layer l = LayerListDialog.getLayerForIndex(index);
+            if (l != null && l instanceof MarkerLayer) {
+                execute((MarkerLayer) l);
+            }
+        }
+
+        @Override
+        public void repeateLastMultikeyAction() {
+            if (lastLayer != null) {
+                MarkerLayer l = lastLayer.get();
+                if (LayerListDialog.isLayerValid(l)) {
+                    execute(l);
+                }
+            }
+        }
+
+        private void execute(MarkerLayer l) {
+            l.jumpToNextMarker();
+            lastLayer = new WeakReference<MarkerLayer>(l);
+        }
+
+        @Override
+        public List<MultikeyInfo> getMultikeyCombinations() {
+            return LayerListDialog.getLayerInfoByClass(MarkerLayer.class);
+        }
+
+        @Override
+        public MultikeyInfo getLastMultikeyAction() {
+            if (lastLayer != null)
+                return LayerListDialog.getLayerInfo(lastLayer.get());
+            else
+                return null;
+        }
+
+    }
+
+    public static final class JumpToPreviousMarker extends AbstractAction implements MultikeyShortcutAction {
+
+        private WeakReference<MarkerLayer> lastLayer;
+        private final MarkerLayer layer;
+
+        public JumpToPreviousMarker(MarkerLayer layer) {
+            this.layer = layer;
+
+            putValue(ACCELERATOR_KEY, Shortcut.registerShortcut("core_multikey:previousMarker", "", 'P', Shortcut.GROUP_DIRECT, KeyEvent.ALT_DOWN_MASK + KeyEvent.CTRL_DOWN_MASK).getKeyStroke());
+            putValue(SHORT_DESCRIPTION, tr("Jump to previous marker"));
+            putValue(NAME, tr("Jump to previous marker"));
+        }
+
+        @Override
+        public void actionPerformed(ActionEvent e) {
+            execute(layer);
+        }
+
+        @Override
+        public void executeMultikeyAction(int index) {
+            Layer l = LayerListDialog.getLayerForIndex(index);
+            if (l != null && l instanceof MarkerLayer) {
+                execute((MarkerLayer) l);
+            }
+        }
+
+        @Override
+        public void repeateLastMultikeyAction() {
+            if (lastLayer != null) {
+                MarkerLayer l = lastLayer.get();
+                if (LayerListDialog.isLayerValid(l)) {
+                    execute(l);
+                }
+            }
+        }
+
+        private void execute(MarkerLayer l) {
+            l.jumpToPreviousMarker();
+            lastLayer = new WeakReference<MarkerLayer>(l);
+        }
+
+        @Override
+        public List<MultikeyInfo> getMultikeyCombinations() {
+            return LayerListDialog.getLayerInfoByClass(MarkerLayer.class);
+        }
+
+        @Override
+        public MultikeyInfo getLastMultikeyAction() {
+            if (lastLayer != null)
+                return LayerListDialog.getLayerInfo(lastLayer.get());
+            else
+                return null;
+        }
+
+    }
+
     private class SynchronizeAudio extends AbstractAction {
 
@@ -455,3 +615,4 @@
         }
     }
+
 }
Index: trunk/src/org/openstreetmap/josm/tools/MultikeyActionsHandler.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/MultikeyActionsHandler.java	(revision 4595)
+++ trunk/src/org/openstreetmap/josm/tools/MultikeyActionsHandler.java	(revision 4595)
@@ -0,0 +1,173 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.tools;
+
+import java.awt.KeyEventDispatcher;
+import java.awt.KeyboardFocusManager;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.JMenuItem;
+import javax.swing.JPopupMenu;
+import javax.swing.KeyStroke;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.tools.MultikeyShortcutAction.MultikeyInfo;
+
+public class MultikeyActionsHandler {
+
+    private static final long DIALOG_DELAY = 2000;
+
+    private class MyKeyEventDispatcher implements KeyEventDispatcher {
+        @Override
+        public boolean dispatchKeyEvent(KeyEvent e) {
+
+            if (e.getWhen() == lastTimestamp)
+                return false;
+
+            if (lastAction != null && e.getID() == KeyEvent.KEY_PRESSED) {
+                if (e.getKeyCode() == lastAction.shortcut.getKeyCode()) {
+                    lastAction.action.repeateLastMultikeyAction();
+                } else {
+                    int index = getIndex(e.getKeyChar());
+                    if (index >= 0) {
+                        lastAction.action.executeMultikeyAction(index);
+                    }
+                }
+                lastAction = null;
+                return true;
+            }
+            return false;
+        }
+
+        private int getIndex(char lastKey) {
+            if (lastKey >= KeyEvent.VK_0 && lastKey <= KeyEvent.VK_9)
+                return lastKey - KeyEvent.VK_0;
+            else if (lastKey >= KeyEvent.VK_A && lastKey <= KeyEvent.VK_Z)
+                return lastKey - KeyEvent.VK_A + 10;
+            else
+                return -1;
+        }
+    }
+
+    private class MyAction extends AbstractAction {
+
+        final MultikeyShortcutAction action;
+        final KeyStroke shortcut;
+
+        MyAction(MultikeyShortcutAction action) {
+            this.action = action;
+            this.shortcut = (KeyStroke) action.getValue(ACCELERATOR_KEY);
+        }
+
+        @Override
+        public void actionPerformed(ActionEvent e) {
+            lastTimestamp = e.getWhen();
+            lastAction = this;
+            timer.schedule(new MyTimerTask(lastTimestamp, lastAction), DIALOG_DELAY);
+        }
+
+        @Override
+        public String toString() {
+            return "MultikeyAction" + action.toString();
+        }
+    }
+
+    private class MyTimerTask extends TimerTask {
+        private final long lastTimestamp;
+        private final MyAction lastAction;
+
+        MyTimerTask(long lastTimestamp, MyAction lastAction) {
+            this.lastTimestamp = lastTimestamp;
+            this.lastAction = lastAction;
+        }
+
+        @Override
+        public void run() {
+            if (lastTimestamp == MultikeyActionsHandler.this.lastTimestamp &&
+                    lastAction == MultikeyActionsHandler.this.lastAction) {
+                showLayersPopup(lastAction);
+                MultikeyActionsHandler.this.lastAction = null;
+            }
+        }
+
+    }
+
+    private long lastTimestamp;
+    private MyAction lastAction;
+    private Timer timer;
+
+
+    private MultikeyActionsHandler() {
+        KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(new MyKeyEventDispatcher());
+        timer =new Timer();
+    }
+
+    private static MultikeyActionsHandler instance;
+
+    public static MultikeyActionsHandler getInstance() {
+        if (instance == null) {
+            instance = new MultikeyActionsHandler();
+        }
+        return instance;
+    }
+
+    private String formatMenuText(KeyStroke keyStroke, String index, String description) {
+        String shortcutText = KeyEvent.getKeyModifiersText(keyStroke.getModifiers()) + "+" + KeyEvent.getKeyText(keyStroke.getKeyCode()) + "," + index;
+
+        return "<html><i>" + shortcutText + "</i>&nbsp;&nbsp;&nbsp;&nbsp;" + description;
+
+    }
+
+    private void showLayersPopup(final MyAction action) {
+        JPopupMenu layers = new JPopupMenu();
+
+        JMenuItem lbTitle = new JMenuItem((String) action.action.getValue(Action.SHORT_DESCRIPTION));
+        lbTitle.setHorizontalAlignment(JMenuItem.CENTER);
+        lbTitle.setEnabled(false);
+        layers.add(lbTitle);
+
+
+        for (final MultikeyInfo info: action.action.getMultikeyCombinations()) {
+            JMenuItem item = new JMenuItem(formatMenuText(action.shortcut, String.valueOf(info.getShortcut()), info.getDescription()));
+            item.setMnemonic(info.getShortcut());
+            item.addActionListener(new ActionListener() {
+                @Override
+                public void actionPerformed(ActionEvent e) {
+                    action.action.executeMultikeyAction(info.getIndex());
+                }
+            });
+            layers.add(item);
+        }
+
+        MultikeyInfo lastLayer = action.action.getLastMultikeyAction();
+        if (lastLayer != null) {
+            JMenuItem repeateItem = new JMenuItem(formatMenuText(action.shortcut,
+                    KeyEvent.getKeyText(action.shortcut.getKeyCode()),
+                    "Repeat " + lastLayer.getDescription()));
+            repeateItem.addActionListener(new ActionListener() {
+                @Override
+                public void actionPerformed(ActionEvent e) {
+                    action.action.repeateLastMultikeyAction();
+                }
+            });
+            layers.add(repeateItem);
+        }
+
+        layers.show(Main.parent, Integer.MAX_VALUE, Integer.MAX_VALUE);
+        layers.setLocation(Main.parent.getX() + Main.parent.getWidth() - layers.getWidth(), Main.parent.getY() + Main.parent.getHeight() - layers.getHeight());
+    }
+
+    public void addAction(MultikeyShortcutAction action) {
+        if (!(action.getValue(Action.ACCELERATOR_KEY) instanceof KeyStroke))
+            throw new IllegalArgumentException("Action must have shortcut set");
+        MyAction myAction = new MyAction(action);
+        Main.registerActionShortcut(myAction, myAction.shortcut);
+    }
+
+
+}
Index: trunk/src/org/openstreetmap/josm/tools/MultikeyShortcutAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/MultikeyShortcutAction.java	(revision 4595)
+++ trunk/src/org/openstreetmap/josm/tools/MultikeyShortcutAction.java	(revision 4595)
@@ -0,0 +1,40 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.tools;
+
+import java.util.List;
+
+import javax.swing.Action;
+
+public interface MultikeyShortcutAction extends Action {
+
+    public static class MultikeyInfo {
+        private final int index;
+        private final String description;
+
+        public MultikeyInfo(int index, String description) {
+            this.index = index;
+            this.description = description;
+        }
+
+        public int getIndex() {
+            return index;
+        }
+
+        public char getShortcut() {
+            if (index < 10)
+                return (char)('0' + index);
+            else
+                return (char)('A' +  index - 10);
+        }
+
+        public String getDescription() {
+            return description;
+        }
+    }
+
+    void executeMultikeyAction(int index);
+    void repeateLastMultikeyAction();
+    List<MultikeyInfo> getMultikeyCombinations();
+    MultikeyInfo getLastMultikeyAction();
+
+}
Index: trunk/src/org/openstreetmap/josm/tools/Shortcut.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/Shortcut.java	(revision 4594)
+++ trunk/src/org/openstreetmap/josm/tools/Shortcut.java	(revision 4595)
@@ -2,5 +2,4 @@
 package org.openstreetmap.josm.tools;
 
-import java.awt.Menu;
 import static org.openstreetmap.josm.tools.I18n.tr;
 
@@ -209,4 +208,9 @@
     }
 
+    @Override
+    public String toString() {
+        return getKeyText();
+    }
+
     ///////////////////////////////
     // everything's static below //
@@ -504,9 +508,9 @@
                                 :
                                     tr("Using the shortcut ''{0}'' instead.\n\n", potentialShortcut.getKeyText())
-                        )+
-                        tr("(Hint: You can edit the shortcuts in the preferences.)"),
-                        tr("Error"),
-                        JOptionPane.ERROR_MESSAGE
-        );
+                                )+
+                                tr("(Hint: You can edit the shortcuts in the preferences.)"),
+                                tr("Error"),
+                                JOptionPane.ERROR_MESSAGE
+                );
     }
 
