Index: trunk/src/org/openstreetmap/josm/gui/dialogs/FilterDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/FilterDialog.java	(revision 12796)
+++ 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 12796)
+++ 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 12796)
+++ 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 12796)
+++ 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 12796)
+++ 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 12796)
+++ 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();
+
+}
