Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/FilterDialog.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/FilterDialog.java	(revision 12798)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/FilterDialog.java	(revision 12799)
@@ -43,9 +43,9 @@
 import org.openstreetmap.josm.gui.MapFrame;
 import org.openstreetmap.josm.gui.MapFrame.MapModeChangeListener;
+import org.openstreetmap.josm.gui.util.MultikeyActionsHandler;
+import org.openstreetmap.josm.gui.util.MultikeyShortcutAction;
 import org.openstreetmap.josm.gui.SideButton;
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.InputMapUtils;
-import org.openstreetmap.josm.tools.MultikeyActionsHandler;
-import org.openstreetmap.josm.tools.MultikeyShortcutAction;
 import org.openstreetmap.josm.tools.Shortcut;
 
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java	(revision 12798)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java	(revision 12799)
@@ -66,4 +66,6 @@
 import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
 import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
+import org.openstreetmap.josm.gui.util.MultikeyActionsHandler;
+import org.openstreetmap.josm.gui.util.MultikeyShortcutAction.MultikeyInfo;
 import org.openstreetmap.josm.gui.layer.NativeScaleLayer;
 import org.openstreetmap.josm.gui.widgets.DisableShortcutsOnFocusGainedTextField;
@@ -73,6 +75,4 @@
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.InputMapUtils;
-import org.openstreetmap.josm.tools.MultikeyActionsHandler;
-import org.openstreetmap.josm.tools.MultikeyShortcutAction.MultikeyInfo;
 import org.openstreetmap.josm.tools.Shortcut;
 
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/layer/ActivateLayerAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/layer/ActivateLayerAction.java	(revision 12798)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/layer/ActivateLayerAction.java	(revision 12799)
@@ -18,7 +18,7 @@
 import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
 import org.openstreetmap.josm.gui.util.GuiHelper;
+import org.openstreetmap.josm.gui.util.MultikeyShortcutAction;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
 import org.openstreetmap.josm.tools.ImageProvider;
-import org.openstreetmap.josm.tools.MultikeyShortcutAction;
 import org.openstreetmap.josm.tools.Shortcut;
 
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/layer/ShowHideLayerAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/layer/ShowHideLayerAction.java	(revision 12798)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/layer/ShowHideLayerAction.java	(revision 12799)
@@ -19,6 +19,6 @@
 import org.openstreetmap.josm.gui.layer.Layer;
 import org.openstreetmap.josm.gui.layer.Layer.LayerAction;
+import org.openstreetmap.josm.gui.util.MultikeyShortcutAction;
 import org.openstreetmap.josm.tools.ImageProvider;
-import org.openstreetmap.josm.tools.MultikeyShortcutAction;
 import org.openstreetmap.josm.tools.Shortcut;
 
Index: /trunk/src/org/openstreetmap/josm/gui/io/UploadDialog.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/io/UploadDialog.java	(revision 12798)
+++ /trunk/src/org/openstreetmap/josm/gui/io/UploadDialog.java	(revision 12799)
@@ -55,4 +55,5 @@
 import org.openstreetmap.josm.gui.help.HelpUtil;
 import org.openstreetmap.josm.gui.util.GuiHelper;
+import org.openstreetmap.josm.gui.util.MultiLineFlowLayout;
 import org.openstreetmap.josm.gui.util.WindowGeometry;
 import org.openstreetmap.josm.io.OsmApi;
@@ -64,5 +65,4 @@
 import org.openstreetmap.josm.tools.ImageProvider.ImageSizes;
 import org.openstreetmap.josm.tools.InputMapUtils;
-import org.openstreetmap.josm.tools.MultiLineFlowLayout;
 import org.openstreetmap.josm.tools.Utils;
 
Index: /trunk/src/org/openstreetmap/josm/gui/layer/JumpToMarkerActions.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/JumpToMarkerActions.java	(revision 12798)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/JumpToMarkerActions.java	(revision 12799)
@@ -12,6 +12,6 @@
 
 import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
-import org.openstreetmap.josm.tools.MultikeyActionsHandler;
-import org.openstreetmap.josm.tools.MultikeyShortcutAction;
+import org.openstreetmap.josm.gui.util.MultikeyActionsHandler;
+import org.openstreetmap.josm.gui.util.MultikeyShortcutAction;
 import org.openstreetmap.josm.tools.Shortcut;
 
Index: /trunk/src/org/openstreetmap/josm/gui/util/MultiLineFlowLayout.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/util/MultiLineFlowLayout.java	(revision 12799)
+++ /trunk/src/org/openstreetmap/josm/gui/util/MultiLineFlowLayout.java	(revision 12799)
@@ -0,0 +1,109 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.util;
+
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Insets;
+import java.util.function.Function;
+
+/**
+ * This is an extension of the flow layout that preferes wrapping the text instead of increasing the component width
+ * when there is not enough space.
+ * <p>
+ * This allows for a better preffered size computation.
+ * It should be used in all places where a flow layout fills the full width of the parent container.
+ * <p>
+ * This does not support baseline alignment.
+ * @author Michael Zangl
+ * @since 10622
+ */
+public class MultiLineFlowLayout extends FlowLayout {
+    /**
+     * Same as {@link FlowLayout#FlowLayout()}
+     */
+    public MultiLineFlowLayout() {
+        super();
+    }
+
+    /**
+     * Same as {@link FlowLayout#FlowLayout(int, int, int)}
+     * @param align Alignment
+     * @param hgap horizontal gap
+     * @param vgap vertical gap
+     */
+    public MultiLineFlowLayout(int align, int hgap, int vgap) {
+        super(align, hgap, vgap);
+    }
+
+    /**
+     * Same as {@link FlowLayout#FlowLayout(int)}
+     * @param align Alignment
+     */
+    public MultiLineFlowLayout(int align) {
+        super(align);
+    }
+
+    @Override
+    public Dimension preferredLayoutSize(Container target) {
+        return getLayoutSize(target, Component::getPreferredSize);
+    }
+
+    @Override
+    public Dimension minimumLayoutSize(Container target) {
+        return getLayoutSize(target, Component::getMinimumSize);
+    }
+
+    private Dimension getLayoutSize(Container target, Function<Component, Dimension> baseSize) {
+        synchronized (target.getTreeLock()) {
+            int outerWidth = getWidthOf(target);
+
+            Insets insets = target.getInsets();
+            int containerWidth = outerWidth - insets.left - insets.right - getHgap() * 2;
+
+            int x = 0;
+            int totalHeight = insets.top + insets.bottom + getVgap() * 2;
+            int rowHeight = 0;
+            for (int i = 0; i < target.getComponentCount(); i++) {
+                Component child = target.getComponent(i);
+                if (!child.isVisible()) {
+                    continue;
+                }
+                Dimension size = baseSize.apply(child);
+                if (x != 0) {
+                    x += getHgap();
+                }
+                x += size.width;
+                if (x > containerWidth) {
+                    totalHeight += rowHeight + getVgap();
+                    rowHeight = 0;
+                    x = 0;
+                }
+
+                rowHeight = Math.max(rowHeight, size.height);
+            }
+            totalHeight += rowHeight;
+
+            return new Dimension(outerWidth, totalHeight);
+        }
+    }
+
+    private static int getWidthOf(Container target) {
+        Container current = target;
+        while (current.getWidth() == 0 && current.getParent() != null) {
+            current = current.getParent();
+        }
+        int width = current.getWidth();
+        if (width == 0) {
+            return Integer.MAX_VALUE;
+        } else {
+            return width;
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "MultiLineFlowLayout [align=" + getAlignment() + ']';
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/gui/util/MultikeyActionsHandler.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/util/MultikeyActionsHandler.java	(revision 12799)
+++ /trunk/src/org/openstreetmap/josm/gui/util/MultikeyActionsHandler.java	(revision 12799)
@@ -0,0 +1,231 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.util;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.KeyEventDispatcher;
+import java.awt.KeyboardFocusManager;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.JMenuItem;
+import javax.swing.JPanel;
+import javax.swing.JPopupMenu;
+import javax.swing.KeyStroke;
+import javax.swing.SwingUtilities;
+import javax.swing.event.PopupMenuEvent;
+import javax.swing.event.PopupMenuListener;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.util.MultikeyShortcutAction.MultikeyInfo;
+import org.openstreetmap.josm.tools.Shortcut;
+
+public final class MultikeyActionsHandler {
+
+    private static final long DIALOG_DELAY = 1000;
+    private static final String STATUS_BAR_ID = "multikeyShortcut";
+
+    private final Map<MultikeyShortcutAction, MyAction> myActions = new HashMap<>();
+
+    static final class ShowLayersPopupWorker implements Runnable {
+        static final class StatusLinePopupMenuListener implements PopupMenuListener {
+            @Override
+            public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
+                // Do nothing
+            }
+
+            @Override
+            public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
+                MainApplication.getMap().statusLine.resetHelpText(STATUS_BAR_ID);
+            }
+
+            @Override
+            public void popupMenuCanceled(PopupMenuEvent e) {
+                // Do nothing
+            }
+        }
+
+        private final MyAction action;
+
+        ShowLayersPopupWorker(MyAction action) {
+            this.action = action;
+        }
+
+        @Override
+        public void run() {
+            JPopupMenu layers = new JPopupMenu();
+
+            JMenuItem lbTitle = new JMenuItem((String) action.action.getValue(Action.SHORT_DESCRIPTION));
+            lbTitle.setEnabled(false);
+            JPanel pnTitle = new JPanel();
+            pnTitle.add(lbTitle);
+            layers.add(pnTitle);
+
+            char repeatKey = (char) action.shortcut.getKeyStroke().getKeyCode();
+            boolean repeatKeyUsed = false;
+
+            for (final MultikeyInfo info: action.action.getMultikeyCombinations()) {
+
+                if (info.getShortcut() == repeatKey) {
+                    repeatKeyUsed = true;
+                }
+
+                JMenuItem item = new JMenuItem(formatMenuText(action.shortcut.getKeyStroke(),
+                        String.valueOf(info.getShortcut()), info.getDescription()));
+                item.setMnemonic(info.getShortcut());
+                item.addActionListener(e -> action.action.executeMultikeyAction(info.getIndex(), false));
+                layers.add(item);
+            }
+
+            if (!repeatKeyUsed) {
+                MultikeyInfo lastLayer = action.action.getLastMultikeyAction();
+                if (lastLayer != null) {
+                    JMenuItem repeateItem = new JMenuItem(formatMenuText(action.shortcut.getKeyStroke(),
+                            KeyEvent.getKeyText(action.shortcut.getKeyStroke().getKeyCode()),
+                            "Repeat " + lastLayer.getDescription()));
+                    repeateItem.setMnemonic(action.shortcut.getKeyStroke().getKeyCode());
+                    repeateItem.addActionListener(e -> action.action.executeMultikeyAction(-1, true));
+                    layers.add(repeateItem);
+                }
+            }
+            layers.addPopupMenuListener(new StatusLinePopupMenuListener());
+            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());
+        }
+    }
+
+    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) {
+                int index = getIndex(e.getKeyCode());
+                if (index >= 0) {
+                    lastAction.action.executeMultikeyAction(index, e.getKeyCode() == lastAction.shortcut.getKeyStroke().getKeyCode());
+                }
+                lastAction = null;
+                MainApplication.getMap().statusLine.resetHelpText(STATUS_BAR_ID);
+                return true;
+            }
+            return false;
+        }
+
+        private int getIndex(int lastKey) {
+            if (lastKey >= KeyEvent.VK_1 && lastKey <= KeyEvent.VK_9)
+                return lastKey - KeyEvent.VK_1;
+            else if (lastKey == KeyEvent.VK_0)
+                return 9;
+            else if (lastKey >= KeyEvent.VK_A && lastKey <= KeyEvent.VK_Z)
+                return lastKey - KeyEvent.VK_A + 10;
+            else
+                return -1;
+        }
+    }
+
+    private class MyAction extends AbstractAction {
+
+        private final transient MultikeyShortcutAction action;
+        private final transient Shortcut shortcut;
+
+        MyAction(MultikeyShortcutAction action) {
+            this.action = action;
+            this.shortcut = action.getMultikeyShortcut();
+        }
+
+        @Override
+        public void actionPerformed(ActionEvent e) {
+            lastTimestamp = e.getWhen();
+            lastAction = this;
+            timer.schedule(new MyTimerTask(lastTimestamp, lastAction), DIALOG_DELAY);
+            MainApplication.getMap().statusLine.setHelpText(STATUS_BAR_ID,
+                    tr("{0}... [please type its number]", (String) action.getValue(SHORT_DESCRIPTION)));
+        }
+
+        @Override
+        public String toString() {
+            return "MultikeyAction" + action;
+        }
+    }
+
+    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) {
+                SwingUtilities.invokeLater(new ShowLayersPopupWorker(lastAction));
+                MultikeyActionsHandler.this.lastAction = null;
+            }
+        }
+    }
+
+    private long lastTimestamp;
+    private MyAction lastAction;
+    private final Timer timer;
+
+    private MultikeyActionsHandler() {
+        KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(new MyKeyEventDispatcher());
+        timer = new Timer();
+    }
+
+    private static MultikeyActionsHandler instance;
+
+    /**
+     * Replies the unique instance of this class.
+     * @return The unique instance of this class
+     */
+    public static synchronized MultikeyActionsHandler getInstance() {
+        if (instance == null) {
+            instance = new MultikeyActionsHandler();
+        }
+        return instance;
+    }
+
+    private static String formatMenuText(KeyStroke keyStroke, String index, String description) {
+        String shortcutText = Shortcut.getKeyText(keyStroke) + ',' + index;
+
+        return "<html><i>" + shortcutText + "</i>&nbsp;&nbsp;&nbsp;&nbsp;" + description;
+    }
+
+    /**
+     * Registers an action and its shortcut
+     * @param action The action to add
+     */
+    public void addAction(MultikeyShortcutAction action) {
+        if (action.getMultikeyShortcut() != null) {
+            MyAction myAction = new MyAction(action);
+            myActions.put(action, myAction);
+            MainApplication.registerActionShortcut(myAction, myAction.shortcut);
+        }
+    }
+
+    /**
+     * Unregisters an action and its shortcut completely
+     * @param action The action to remove
+     */
+    public void removeAction(MultikeyShortcutAction action) {
+        MyAction a = myActions.get(action);
+        if (a != null) {
+            MainApplication.unregisterActionShortcut(a, a.shortcut);
+            myActions.remove(action);
+        }
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/gui/util/MultikeyShortcutAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/util/MultikeyShortcutAction.java	(revision 12799)
+++ /trunk/src/org/openstreetmap/josm/gui/util/MultikeyShortcutAction.java	(revision 12799)
@@ -0,0 +1,47 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.util;
+
+import java.util.List;
+
+import javax.swing.Action;
+
+import org.openstreetmap.josm.tools.Shortcut;
+
+public interface MultikeyShortcutAction extends Action {
+
+    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 < 9)
+                return (char) ('1' + index);
+            else if (index == 9)
+                return '0';
+            else
+                return (char) ('A' + index - 10);
+        }
+
+        public String getDescription() {
+            return description;
+        }
+    }
+
+    Shortcut getMultikeyShortcut();
+
+    void executeMultikeyAction(int index, boolean repeatLastAction);
+
+    List<MultikeyInfo> getMultikeyCombinations();
+
+    MultikeyInfo getLastMultikeyAction();
+
+}
Index: unk/src/org/openstreetmap/josm/tools/MultiLineFlowLayout.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/tools/MultiLineFlowLayout.java	(revision 12798)
+++ 	(revision )
@@ -1,109 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.tools;
-
-import java.awt.Component;
-import java.awt.Container;
-import java.awt.Dimension;
-import java.awt.FlowLayout;
-import java.awt.Insets;
-import java.util.function.Function;
-
-/**
- * This is an extension of the flow layout that preferes wrapping the text instead of increasing the component width
- * when there is not enough space.
- * <p>
- * This allows for a better preffered size computation.
- * It should be used in all places where a flow layout fills the full width of the parent container.
- * <p>
- * This does not support baseline alignment.
- * @author Michael Zangl
- * @since 10622
- */
-public class MultiLineFlowLayout extends FlowLayout {
-    /**
-     * Same as {@link FlowLayout#FlowLayout()}
-     */
-    public MultiLineFlowLayout() {
-        super();
-    }
-
-    /**
-     * Same as {@link FlowLayout#FlowLayout(int, int, int)}
-     * @param align Alignment
-     * @param hgap horizontal gap
-     * @param vgap vertical gap
-     */
-    public MultiLineFlowLayout(int align, int hgap, int vgap) {
-        super(align, hgap, vgap);
-    }
-
-    /**
-     * Same as {@link FlowLayout#FlowLayout(int)}
-     * @param align Alignment
-     */
-    public MultiLineFlowLayout(int align) {
-        super(align);
-    }
-
-    @Override
-    public Dimension preferredLayoutSize(Container target) {
-        return getLayoutSize(target, Component::getPreferredSize);
-    }
-
-    @Override
-    public Dimension minimumLayoutSize(Container target) {
-        return getLayoutSize(target, Component::getMinimumSize);
-    }
-
-    private Dimension getLayoutSize(Container target, Function<Component, Dimension> baseSize) {
-        synchronized (target.getTreeLock()) {
-            int outerWidth = getWidthOf(target);
-
-            Insets insets = target.getInsets();
-            int containerWidth = outerWidth - insets.left - insets.right - getHgap() * 2;
-
-            int x = 0;
-            int totalHeight = insets.top + insets.bottom + getVgap() * 2;
-            int rowHeight = 0;
-            for (int i = 0; i < target.getComponentCount(); i++) {
-                Component child = target.getComponent(i);
-                if (!child.isVisible()) {
-                    continue;
-                }
-                Dimension size = baseSize.apply(child);
-                if (x != 0) {
-                    x += getHgap();
-                }
-                x += size.width;
-                if (x > containerWidth) {
-                    totalHeight += rowHeight + getVgap();
-                    rowHeight = 0;
-                    x = 0;
-                }
-
-                rowHeight = Math.max(rowHeight, size.height);
-            }
-            totalHeight += rowHeight;
-
-            return new Dimension(outerWidth, totalHeight);
-        }
-    }
-
-    private static int getWidthOf(Container target) {
-        Container current = target;
-        while (current.getWidth() == 0 && current.getParent() != null) {
-            current = current.getParent();
-        }
-        int width = current.getWidth();
-        if (width == 0) {
-            return Integer.MAX_VALUE;
-        } else {
-            return width;
-        }
-    }
-
-    @Override
-    public String toString() {
-        return "MultiLineFlowLayout [align=" + getAlignment() + ']';
-    }
-}
Index: unk/src/org/openstreetmap/josm/tools/MultikeyActionsHandler.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/tools/MultikeyActionsHandler.java	(revision 12798)
+++ 	(revision )
@@ -1,230 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.tools;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.awt.KeyEventDispatcher;
-import java.awt.KeyboardFocusManager;
-import java.awt.event.ActionEvent;
-import java.awt.event.KeyEvent;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Timer;
-import java.util.TimerTask;
-
-import javax.swing.AbstractAction;
-import javax.swing.Action;
-import javax.swing.JMenuItem;
-import javax.swing.JPanel;
-import javax.swing.JPopupMenu;
-import javax.swing.KeyStroke;
-import javax.swing.SwingUtilities;
-import javax.swing.event.PopupMenuEvent;
-import javax.swing.event.PopupMenuListener;
-
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.gui.MainApplication;
-import org.openstreetmap.josm.tools.MultikeyShortcutAction.MultikeyInfo;
-
-public final class MultikeyActionsHandler {
-
-    private static final long DIALOG_DELAY = 1000;
-    private static final String STATUS_BAR_ID = "multikeyShortcut";
-
-    private final Map<MultikeyShortcutAction, MyAction> myActions = new HashMap<>();
-
-    static final class ShowLayersPopupWorker implements Runnable {
-        static final class StatusLinePopupMenuListener implements PopupMenuListener {
-            @Override
-            public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
-                // Do nothing
-            }
-
-            @Override
-            public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
-                MainApplication.getMap().statusLine.resetHelpText(STATUS_BAR_ID);
-            }
-
-            @Override
-            public void popupMenuCanceled(PopupMenuEvent e) {
-                // Do nothing
-            }
-        }
-
-        private final MyAction action;
-
-        ShowLayersPopupWorker(MyAction action) {
-            this.action = action;
-        }
-
-        @Override
-        public void run() {
-            JPopupMenu layers = new JPopupMenu();
-
-            JMenuItem lbTitle = new JMenuItem((String) action.action.getValue(Action.SHORT_DESCRIPTION));
-            lbTitle.setEnabled(false);
-            JPanel pnTitle = new JPanel();
-            pnTitle.add(lbTitle);
-            layers.add(pnTitle);
-
-            char repeatKey = (char) action.shortcut.getKeyStroke().getKeyCode();
-            boolean repeatKeyUsed = false;
-
-            for (final MultikeyInfo info: action.action.getMultikeyCombinations()) {
-
-                if (info.getShortcut() == repeatKey) {
-                    repeatKeyUsed = true;
-                }
-
-                JMenuItem item = new JMenuItem(formatMenuText(action.shortcut.getKeyStroke(),
-                        String.valueOf(info.getShortcut()), info.getDescription()));
-                item.setMnemonic(info.getShortcut());
-                item.addActionListener(e -> action.action.executeMultikeyAction(info.getIndex(), false));
-                layers.add(item);
-            }
-
-            if (!repeatKeyUsed) {
-                MultikeyInfo lastLayer = action.action.getLastMultikeyAction();
-                if (lastLayer != null) {
-                    JMenuItem repeateItem = new JMenuItem(formatMenuText(action.shortcut.getKeyStroke(),
-                            KeyEvent.getKeyText(action.shortcut.getKeyStroke().getKeyCode()),
-                            "Repeat " + lastLayer.getDescription()));
-                    repeateItem.setMnemonic(action.shortcut.getKeyStroke().getKeyCode());
-                    repeateItem.addActionListener(e -> action.action.executeMultikeyAction(-1, true));
-                    layers.add(repeateItem);
-                }
-            }
-            layers.addPopupMenuListener(new StatusLinePopupMenuListener());
-            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());
-        }
-    }
-
-    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) {
-                int index = getIndex(e.getKeyCode());
-                if (index >= 0) {
-                    lastAction.action.executeMultikeyAction(index, e.getKeyCode() == lastAction.shortcut.getKeyStroke().getKeyCode());
-                }
-                lastAction = null;
-                MainApplication.getMap().statusLine.resetHelpText(STATUS_BAR_ID);
-                return true;
-            }
-            return false;
-        }
-
-        private int getIndex(int lastKey) {
-            if (lastKey >= KeyEvent.VK_1 && lastKey <= KeyEvent.VK_9)
-                return lastKey - KeyEvent.VK_1;
-            else if (lastKey == KeyEvent.VK_0)
-                return 9;
-            else if (lastKey >= KeyEvent.VK_A && lastKey <= KeyEvent.VK_Z)
-                return lastKey - KeyEvent.VK_A + 10;
-            else
-                return -1;
-        }
-    }
-
-    private class MyAction extends AbstractAction {
-
-        private final transient MultikeyShortcutAction action;
-        private final transient Shortcut shortcut;
-
-        MyAction(MultikeyShortcutAction action) {
-            this.action = action;
-            this.shortcut = action.getMultikeyShortcut();
-        }
-
-        @Override
-        public void actionPerformed(ActionEvent e) {
-            lastTimestamp = e.getWhen();
-            lastAction = this;
-            timer.schedule(new MyTimerTask(lastTimestamp, lastAction), DIALOG_DELAY);
-            MainApplication.getMap().statusLine.setHelpText(STATUS_BAR_ID,
-                    tr("{0}... [please type its number]", (String) action.getValue(SHORT_DESCRIPTION)));
-        }
-
-        @Override
-        public String toString() {
-            return "MultikeyAction" + action;
-        }
-    }
-
-    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) {
-                SwingUtilities.invokeLater(new ShowLayersPopupWorker(lastAction));
-                MultikeyActionsHandler.this.lastAction = null;
-            }
-        }
-    }
-
-    private long lastTimestamp;
-    private MyAction lastAction;
-    private final Timer timer;
-
-    private MultikeyActionsHandler() {
-        KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(new MyKeyEventDispatcher());
-        timer = new Timer();
-    }
-
-    private static MultikeyActionsHandler instance;
-
-    /**
-     * Replies the unique instance of this class.
-     * @return The unique instance of this class
-     */
-    public static synchronized MultikeyActionsHandler getInstance() {
-        if (instance == null) {
-            instance = new MultikeyActionsHandler();
-        }
-        return instance;
-    }
-
-    private static String formatMenuText(KeyStroke keyStroke, String index, String description) {
-        String shortcutText = Shortcut.getKeyText(keyStroke) + ',' + index;
-
-        return "<html><i>" + shortcutText + "</i>&nbsp;&nbsp;&nbsp;&nbsp;" + description;
-    }
-
-    /**
-     * Registers an action and its shortcut
-     * @param action The action to add
-     */
-    public void addAction(MultikeyShortcutAction action) {
-        if (action.getMultikeyShortcut() != null) {
-            MyAction myAction = new MyAction(action);
-            myActions.put(action, myAction);
-            MainApplication.registerActionShortcut(myAction, myAction.shortcut);
-        }
-    }
-
-    /**
-     * Unregisters an action and its shortcut completely
-     * @param action The action to remove
-     */
-    public void removeAction(MultikeyShortcutAction action) {
-        MyAction a = myActions.get(action);
-        if (a != null) {
-            MainApplication.unregisterActionShortcut(a, a.shortcut);
-            myActions.remove(action);
-        }
-    }
-}
Index: unk/src/org/openstreetmap/josm/tools/MultikeyShortcutAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/tools/MultikeyShortcutAction.java	(revision 12798)
+++ 	(revision )
@@ -1,45 +1,0 @@
-// 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 {
-
-    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 < 9)
-                return (char) ('1' + index);
-            else if (index == 9)
-                return '0';
-            else
-                return (char) ('A' + index - 10);
-        }
-
-        public String getDescription() {
-            return description;
-        }
-    }
-
-    Shortcut getMultikeyShortcut();
-
-    void executeMultikeyAction(int index, boolean repeatLastAction);
-
-    List<MultikeyInfo> getMultikeyCombinations();
-
-    MultikeyInfo getLastMultikeyAction();
-
-}
Index: /trunk/test/unit/org/openstreetmap/josm/gui/util/MultiLineFlowLayoutTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/gui/util/MultiLineFlowLayoutTest.java	(revision 12799)
+++ /trunk/test/unit/org/openstreetmap/josm/gui/util/MultiLineFlowLayoutTest.java	(revision 12799)
@@ -0,0 +1,137 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.util;
+
+import static org.junit.Assert.assertEquals;
+
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+
+import javax.swing.BorderFactory;
+import javax.swing.JPanel;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.openstreetmap.josm.gui.util.MultiLineFlowLayout;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
+/**
+ * Test {@link MultiLineFlowLayout}
+ * @author Michael Zangl
+ */
+public class MultiLineFlowLayoutTest {
+    /**
+     * No special rules.
+     */
+    @Rule
+    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
+    public JOSMTestRules test = new JOSMTestRules();
+
+    private static final int TEST_WIDHT = 500;
+    private JPanel container;
+
+    /**
+     * Prepare test container.
+     */
+    @Before
+    public void setUp() {
+        JPanel parent = new JPanel();
+        parent.setBounds(0, 0, TEST_WIDHT, 500);
+
+        container = new JPanel(new MultiLineFlowLayout(FlowLayout.CENTER, 0, 0));
+        parent.add(container);
+    }
+
+    /**
+     * Test that one line is layed out correctly
+     */
+    @Test
+    public void testOneLine() {
+        fillOneLine();
+
+        container.invalidate();
+        Dimension preffered = container.getPreferredSize();
+        assertEquals(TEST_WIDHT, preffered.width);
+        assertEquals(100, preffered.height);
+
+        Dimension minimum = container.getMinimumSize();
+        assertEquals(TEST_WIDHT, minimum.width);
+        assertEquals(50, minimum.height);
+    }
+
+    /**
+     * Test that insets are respected
+     */
+    @Test
+    public void testInsets() {
+        fillOneLine();
+
+        container.setBorder(BorderFactory.createEmptyBorder(3, 0, 7, 0));
+        container.invalidate();
+        Dimension preffered = container.getPreferredSize();
+        assertEquals(TEST_WIDHT, preffered.width);
+        assertEquals(110, preffered.height);
+
+        // This should force wrapping
+        container.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 40));
+        container.invalidate();
+        preffered = container.getPreferredSize();
+        assertEquals(TEST_WIDHT, preffered.width);
+        assertEquals(200, preffered.height);
+    }
+
+    /**
+     * Test that gaps are respected
+     */
+    @Test
+    public void testGaps() {
+        fillOneLine();
+
+        container.setLayout(new MultiLineFlowLayout(FlowLayout.LEADING, 20, 10));
+        container.invalidate();
+        Dimension preffered = container.getPreferredSize();
+        assertEquals(TEST_WIDHT, preffered.width);
+        assertEquals(230, preffered.height);
+    }
+
+    /**
+     * Test that it behaves the same as FlowLayout for one line.
+     */
+    @Test
+    public void testSameAsFlowLayout() {
+        fillOneLine();
+        JPanel childx = new JPanel();
+        childx.setPreferredSize(new Dimension(300, 100));
+        childx.setMinimumSize(new Dimension(200, 50));
+        childx.setVisible(false);
+        container.add(childx);
+        container.setBorder(BorderFactory.createEmptyBorder(3, 4, 5, 6));
+
+        container.setLayout(new MultiLineFlowLayout(FlowLayout.LEADING, 2, 1));
+        container.invalidate();
+        Dimension is = container.getPreferredSize();
+
+        container.setLayout(new FlowLayout(FlowLayout.LEADING, 2, 1));
+        container.invalidate();
+        Dimension should = container.getPreferredSize();
+
+        assertEquals(should.height, is.height);
+    }
+
+    private void fillOneLine() {
+        JPanel child1 = new JPanel();
+        child1.setPreferredSize(new Dimension(300, 100));
+        child1.setMinimumSize(new Dimension(200, 50));
+        container.add(child1);
+        JPanel child2 = new JPanel();
+        child2.setPreferredSize(new Dimension(100, 100));
+        child1.setMinimumSize(new Dimension(100, 50));
+        container.add(child2);
+        JPanel child3 = new JPanel();
+        child3.setPreferredSize(new Dimension(50, 100));
+        child1.setMinimumSize(new Dimension(50, 50));
+        container.add(child3);
+    }
+}
Index: unk/test/unit/org/openstreetmap/josm/tools/MultiLineFlowLayoutTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/tools/MultiLineFlowLayoutTest.java	(revision 12798)
+++ 	(revision )
@@ -1,136 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.tools;
-
-import static org.junit.Assert.assertEquals;
-
-import java.awt.Dimension;
-import java.awt.FlowLayout;
-
-import javax.swing.BorderFactory;
-import javax.swing.JPanel;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
-/**
- * Test {@link MultiLineFlowLayout}
- * @author Michael Zangl
- */
-public class MultiLineFlowLayoutTest {
-    /**
-     * No special rules.
-     */
-    @Rule
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules();
-
-    private static final int TEST_WIDHT = 500;
-    private JPanel container;
-
-    /**
-     * Prepare test container.
-     */
-    @Before
-    public void setUp() {
-        JPanel parent = new JPanel();
-        parent.setBounds(0, 0, TEST_WIDHT, 500);
-
-        container = new JPanel(new MultiLineFlowLayout(FlowLayout.CENTER, 0, 0));
-        parent.add(container);
-    }
-
-    /**
-     * Test that one line is layed out correctly
-     */
-    @Test
-    public void testOneLine() {
-        fillOneLine();
-
-        container.invalidate();
-        Dimension preffered = container.getPreferredSize();
-        assertEquals(TEST_WIDHT, preffered.width);
-        assertEquals(100, preffered.height);
-
-        Dimension minimum = container.getMinimumSize();
-        assertEquals(TEST_WIDHT, minimum.width);
-        assertEquals(50, minimum.height);
-    }
-
-    /**
-     * Test that insets are respected
-     */
-    @Test
-    public void testInsets() {
-        fillOneLine();
-
-        container.setBorder(BorderFactory.createEmptyBorder(3, 0, 7, 0));
-        container.invalidate();
-        Dimension preffered = container.getPreferredSize();
-        assertEquals(TEST_WIDHT, preffered.width);
-        assertEquals(110, preffered.height);
-
-        // This should force wrapping
-        container.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 40));
-        container.invalidate();
-        preffered = container.getPreferredSize();
-        assertEquals(TEST_WIDHT, preffered.width);
-        assertEquals(200, preffered.height);
-    }
-
-    /**
-     * Test that gaps are respected
-     */
-    @Test
-    public void testGaps() {
-        fillOneLine();
-
-        container.setLayout(new MultiLineFlowLayout(FlowLayout.LEADING, 20, 10));
-        container.invalidate();
-        Dimension preffered = container.getPreferredSize();
-        assertEquals(TEST_WIDHT, preffered.width);
-        assertEquals(230, preffered.height);
-    }
-
-    /**
-     * Test that it behaves the same as FlowLayout for one line.
-     */
-    @Test
-    public void testSameAsFlowLayout() {
-        fillOneLine();
-        JPanel childx = new JPanel();
-        childx.setPreferredSize(new Dimension(300, 100));
-        childx.setMinimumSize(new Dimension(200, 50));
-        childx.setVisible(false);
-        container.add(childx);
-        container.setBorder(BorderFactory.createEmptyBorder(3, 4, 5, 6));
-
-        container.setLayout(new MultiLineFlowLayout(FlowLayout.LEADING, 2, 1));
-        container.invalidate();
-        Dimension is = container.getPreferredSize();
-
-        container.setLayout(new FlowLayout(FlowLayout.LEADING, 2, 1));
-        container.invalidate();
-        Dimension should = container.getPreferredSize();
-
-        assertEquals(should.height, is.height);
-    }
-
-    private void fillOneLine() {
-        JPanel child1 = new JPanel();
-        child1.setPreferredSize(new Dimension(300, 100));
-        child1.setMinimumSize(new Dimension(200, 50));
-        container.add(child1);
-        JPanel child2 = new JPanel();
-        child2.setPreferredSize(new Dimension(100, 100));
-        child1.setMinimumSize(new Dimension(100, 50));
-        container.add(child2);
-        JPanel child3 = new JPanel();
-        child3.setPreferredSize(new Dimension(50, 100));
-        child1.setMinimumSize(new Dimension(50, 50));
-        container.add(child3);
-    }
-}
