Index: /trunk/src/org/openstreetmap/josm/gui/bbox/SlippyMapBBoxChooser.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/bbox/SlippyMapBBoxChooser.java	(revision 12954)
+++ /trunk/src/org/openstreetmap/josm/gui/bbox/SlippyMapBBoxChooser.java	(revision 12955)
@@ -22,6 +22,10 @@
 import java.util.concurrent.CopyOnWriteArrayList;
 
+import javax.swing.ButtonModel;
+import javax.swing.JToggleButton;
 import javax.swing.JOptionPane;
 import javax.swing.SpringLayout;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.ChangeEvent;
 
 import org.openstreetmap.gui.jmapviewer.Coordinate;
@@ -44,4 +48,5 @@
 import org.openstreetmap.josm.data.imagery.TileLoaderFactory;
 import org.openstreetmap.josm.data.osm.BBox;
+import org.openstreetmap.josm.data.preferences.BooleanProperty;
 import org.openstreetmap.josm.data.preferences.StringProperty;
 import org.openstreetmap.josm.gui.MainApplication;
@@ -56,5 +61,5 @@
  * This panel displays a map and lets the user chose a {@link BBox}.
  */
-public class SlippyMapBBoxChooser extends JMapViewer implements BBoxChooser, MainLayerManager.ActiveLayerChangeListener {
+public class SlippyMapBBoxChooser extends JMapViewer implements BBoxChooser, ChangeListener, MainLayerManager.ActiveLayerChangeListener {
 
     /**
@@ -121,4 +126,5 @@
 
     private static final StringProperty PROP_MAPSTYLE = new StringProperty("slippy_map_chooser.mapstyle", "Mapnik");
+    private static final BooleanProperty PROP_SHOWDLAREA = new BooleanProperty("slippy_map_chooser.show_downloaded_area", true);
     /**
      * The property name used for the resize button.
@@ -131,4 +137,5 @@
 
     private final SizeButton iSizeButton;
+    private final ButtonModel showDownloadAreaButtonModel;
     private final SourceButton iSourceButton;
     private transient Bounds bbox;
@@ -173,8 +180,11 @@
         List<TileSource> tileSources = getAllTileSources();
 
-        iSourceButton = new SourceButton(this, tileSources);
+        this.showDownloadAreaButtonModel = new JToggleButton.ToggleButtonModel();
+        this.showDownloadAreaButtonModel.setSelected(PROP_SHOWDLAREA.get());
+        this.showDownloadAreaButtonModel.addChangeListener(this);
+        iSourceButton = new SourceButton(this, tileSources, this.showDownloadAreaButtonModel);
         add(iSourceButton);
-        springLayout.putConstraint(SpringLayout.EAST, iSourceButton, 0, SpringLayout.EAST, this);
-        springLayout.putConstraint(SpringLayout.NORTH, iSourceButton, 30, SpringLayout.NORTH, this);
+        springLayout.putConstraint(SpringLayout.EAST, iSourceButton, -2, SpringLayout.EAST, this);
+        springLayout.putConstraint(SpringLayout.NORTH, iSourceButton, 2, SpringLayout.NORTH, this);
 
         iSizeButton = new SizeButton(this);
@@ -231,5 +241,5 @@
         // enough to make sharing code impractical)
         final OsmDataLayer editLayer = MainApplication.getLayerManager().getEditLayer();
-        if (editLayer != null && Config.getPref().getBoolean("draw.data.downloaded_area", true) && !editLayer.data.getDataSources().isEmpty()) {
+        if (editLayer != null && this.showDownloadAreaButtonModel.isSelected() && !editLayer.data.getDataSources().isEmpty()) {
             // initialize area with current viewport
             Rectangle b = this.getBounds();
@@ -271,4 +281,11 @@
     @Override
     public void activeOrEditLayerChanged(MainLayerManager.ActiveLayerChangeEvent e) {
+        this.repaint();
+    }
+
+    @Override
+    public void stateChanged(ChangeEvent e) {
+        // fired for the stateChanged event of this.showDownloadAreaButtonModel
+        PROP_SHOWDLAREA.put(this.showDownloadAreaButtonModel.isSelected());
         this.repaint();
     }
@@ -342,4 +359,7 @@
         this.setTileSource(tileSource);
         PROP_MAPSTYLE.put(tileSource.getName()); // TODO Is name really unique?
+        if (this.iSourceButton.getCurrentSource() != tileSource) { // prevent infinite recursion
+            this.iSourceButton.setCurrentMap(tileSource);
+        }
     }
 
Index: /trunk/src/org/openstreetmap/josm/gui/bbox/SourceButton.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/bbox/SourceButton.java	(revision 12954)
+++ /trunk/src/org/openstreetmap/josm/gui/bbox/SourceButton.java	(revision 12955)
@@ -2,21 +2,25 @@
 package org.openstreetmap.josm.gui.bbox;
 
-import java.awt.Color;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
 import java.awt.Dimension;
-import java.awt.Font;
-import java.awt.FontMetrics;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.Point;
-import java.awt.RenderingHints;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import java.awt.event.MouseListener;
+import java.awt.event.ActionListener;
+import java.awt.event.ActionEvent;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
 
-import javax.swing.ImageIcon;
-import javax.swing.JComponent;
+import javax.swing.AbstractButton;
+import javax.swing.ButtonGroup;
+import javax.swing.ButtonModel;
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JPopupMenu;
+import javax.swing.JRadioButtonMenuItem;
+import javax.swing.JToggleButton;
 
 import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
+import org.openstreetmap.josm.gui.widgets.PopupMenuButton;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
 import org.openstreetmap.josm.tools.ImageProvider;
@@ -26,26 +30,26 @@
  * @since 1390
  */
-public class SourceButton extends JComponent {
+public class SourceButton extends PopupMenuButton {
+    protected class TileSourceButtonModel extends JToggleButton.ToggleButtonModel implements ActionListener {
+        protected final TileSource tileSource;
 
-    private static final int LAYER_HEIGHT = 20;
-    private static final int LEFT_PADDING = 5;
-    private static final int TOP_PADDING = 5;
-    private static final int BOTTOM_PADDING = 5;
+        public TileSourceButtonModel(TileSource tileSource_) {
+            super();
+            this.tileSource = tileSource_;
+            this.addActionListener(this);
+        }
 
-    private transient TileSource[] sources;
+        @Override
+        public void actionPerformed(ActionEvent e) {
+            if (SourceButton.this.slippyMapBBoxChooser.getTileController().getTileSource() != this.tileSource) { // prevent infinite recursion
+                SourceButton.this.slippyMapBBoxChooser.toggleMapSource(this.tileSource);
+            }
+        }
+    }
 
-    private final ImageIcon enlargeImage;
-    private final ImageIcon shrinkImage;
-    private final Dimension hiddenDimension;
-
-    // Calculated after component is added to container
-    private int barWidth;
-    private Dimension shownDimension;
-    private Font font;
-
-    private boolean isEnlarged;
-
-    private int currentMap;
-    private final SlippyMapBBoxChooser slippyMapBBoxChooser;
+    protected final SlippyMapBBoxChooser slippyMapBBoxChooser;
+    protected final ButtonModel showDownloadAreaButtonModel;
+    private List<TileSource> sources;
+    private ButtonGroup sourceButtonGroup;
 
     /**
@@ -54,38 +58,45 @@
      * @param sources list of imagery sources to display
      */
-    public SourceButton(SlippyMapBBoxChooser slippyMapBBoxChooser, Collection<TileSource> sources) {
-        this.slippyMapBBoxChooser = slippyMapBBoxChooser;
-        setSources(sources);
-        enlargeImage = ImageProvider.get("layer-switcher-maximize");
-        shrinkImage = ImageProvider.get("layer-switcher-minimize");
-
-        hiddenDimension = new Dimension(enlargeImage.getIconWidth(), enlargeImage.getIconHeight());
-        setPreferredSize(hiddenDimension);
-
-        addMouseListener(mouseListener);
+    public SourceButton(
+        SlippyMapBBoxChooser slippyMapBBoxChooser_,
+        Collection<TileSource> sources_,
+        ButtonModel showDownloadAreaButtonModel_
+    ) {
+        super(new ImageProvider("dialogs/layerlist").getResource().getImageIcon(new Dimension(16, 16)));
+        this.showDownloadAreaButtonModel = showDownloadAreaButtonModel_;
+        this.slippyMapBBoxChooser = slippyMapBBoxChooser_;
+        this.setPreferredSize(new Dimension(24, 24));
+        this.setSources(sources_);
     }
 
-    private final transient MouseListener mouseListener = new MouseAdapter() {
-        @Override
-        public void mouseReleased(MouseEvent e) {
-            if (e.getButton() == MouseEvent.BUTTON1) {
-                Point point = e.getPoint();
-                if (isEnlarged) {
-                    if (barWidth < point.x && point.y < shrinkImage.getIconHeight()) {
-                        toggle();
-                    } else {
-                        int result = (point.y - 5) / LAYER_HEIGHT;
-                        if (result >= 0 && result < SourceButton.this.sources.length) {
-                            SourceButton.this.slippyMapBBoxChooser.toggleMapSource(SourceButton.this.sources[result]);
-                            currentMap = result;
-                            toggle();
-                        }
-                    }
-                } else {
-                    toggle();
-                }
-            }
+    protected void generatePopupMenu() {
+        JPopupMenu pm = new JPopupMenu();
+        this.sourceButtonGroup = new ButtonGroup();
+        for (TileSource ts : this.sources) {
+            JRadioButtonMenuItem menuItem = new JRadioButtonMenuItem(ts.getName());
+            TileSourceButtonModel buttonModel = new TileSourceButtonModel(ts);
+            menuItem.setModel(buttonModel);
+            pm.add(menuItem);
+            this.sourceButtonGroup.add(menuItem);
+
+            // attempt to initialize button group matching current state of slippyMapBBoxChooser
+            buttonModel.setSelected(this.slippyMapBBoxChooser.getTileController().getTileSource() == ts);
         }
-    };
+
+        pm.addSeparator();
+
+        JCheckBoxMenuItem showDownloadAreaItem = new JCheckBoxMenuItem(tr("Show downloaded area"));
+        showDownloadAreaItem.setModel(this.showDownloadAreaButtonModel);
+        pm.add(showDownloadAreaItem);
+
+        this.setPopupMenu(pm);
+    }
+
+    private void setSourceDefault() {
+        Enumeration<AbstractButton> elems = this.sourceButtonGroup.getElements();
+        if (elems.hasMoreElements()) {
+            elems.nextElement().setSelected(true);
+        }
+    }
 
     /**
@@ -94,51 +105,30 @@
      * @since 6364
      */
-    public final void setSources(Collection<TileSource> sources) {
-        CheckParameterUtil.ensureParameterNotNull(sources, "sources");
-        this.sources = sources.toArray(new TileSource[sources.size()]);
-        shownDimension = null;
-    }
-
-    @Override
-    protected void paintComponent(Graphics graphics) {
-        Graphics2D g = (Graphics2D) graphics.create();
-        try {
-            calculateShownDimension();
-            g.setFont(font);
-            if (isEnlarged) {
-                g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
-                int radioButtonSize = 10;
-
-                g.setColor(new Color(0, 0, 139, 179));
-                g.fillRoundRect(0, 0, barWidth + shrinkImage.getIconWidth(),
-                        sources.length * LAYER_HEIGHT + TOP_PADDING + BOTTOM_PADDING, 10, 10);
-                for (int i = 0; i < sources.length; i++) {
-                    g.setColor(Color.WHITE);
-                    g.fillOval(LEFT_PADDING, TOP_PADDING + i * LAYER_HEIGHT + 6, radioButtonSize, radioButtonSize);
-                    g.drawString(sources[i].getName(), LEFT_PADDING + radioButtonSize + LEFT_PADDING,
-                            TOP_PADDING + i * LAYER_HEIGHT + g.getFontMetrics().getHeight());
-                    if (currentMap == i) {
-                        g.setColor(Color.BLACK);
-                        g.fillOval(LEFT_PADDING + 1, TOP_PADDING + 7 + i * LAYER_HEIGHT, radioButtonSize - 2, radioButtonSize - 2);
-                    }
-                }
-
-                g.drawImage(shrinkImage.getImage(), barWidth, 0, null);
-            } else {
-                g.drawImage(enlargeImage.getImage(), 0, 0, null);
-            }
-        } finally {
-            g.dispose();
+    public final void setSources(Collection<TileSource> sources_) {
+        CheckParameterUtil.ensureParameterNotNull(sources_, "sources_");
+        this.sources = new ArrayList<TileSource>(sources_);
+        this.generatePopupMenu();
+        if (this.sourceButtonGroup.getSelection() == null) {
+            this.setSourceDefault();
         }
     }
 
     /**
-     * Toggle the visibility of imagery source list.
+     * Get the tile sources.
+     * @return unmodifiable collection of tile sources
      */
-    public void toggle() {
-        this.isEnlarged = !this.isEnlarged;
-        calculateShownDimension();
-        setPreferredSize(isEnlarged ? shownDimension : hiddenDimension);
-        revalidate();
+    public final Collection<TileSource> getSources() {
+        return Collections.unmodifiableCollection(this.sources);
+    }
+
+    /**
+     * Get the currently-selected tile source.
+     */
+    public final TileSource getCurrentSource() {
+        TileSourceButtonModel buttonModel = (TileSourceButtonModel) this.sourceButtonGroup.getSelection();
+        if (buttonModel != null) {
+            return buttonModel.tileSource;
+        }
+        return null;
     }
 
@@ -148,27 +138,14 @@
      */
     public void setCurrentMap(TileSource tileSource) {
-        for (int i = 0; i < sources.length; i++) {
-            if (sources[i].equals(tileSource)) {
-                currentMap = i;
+        Enumeration<AbstractButton> elems = this.sourceButtonGroup.getElements();
+        while (elems.hasMoreElements()) {
+            AbstractButton b = elems.nextElement();
+            if (((TileSourceButtonModel) b.getModel()).tileSource == tileSource) {
+                b.setSelected(true);
                 return;
             }
         }
-        currentMap = 0;
-    }
-
-    private void calculateShownDimension() {
-        if (shownDimension == null) {
-            font = getFont().deriveFont(Font.BOLD).deriveFont(15.0f);
-            int textWidth = 0;
-            FontMetrics fm = getFontMetrics(font);
-            for (TileSource source: sources) {
-                int width = fm.stringWidth(source.getName());
-                if (width > textWidth) {
-                    textWidth = width;
-                }
-            }
-            barWidth = textWidth + 50;
-            shownDimension = new Dimension(barWidth + shrinkImage.getIconWidth(), sources.length * LAYER_HEIGHT + TOP_PADDING + BOTTOM_PADDING);
-        }
+        // failed to find the correct one
+        this.setSourceDefault();
     }
 }
Index: /trunk/src/org/openstreetmap/josm/gui/widgets/PopupMenuButton.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/widgets/PopupMenuButton.java	(revision 12955)
+++ /trunk/src/org/openstreetmap/josm/gui/widgets/PopupMenuButton.java	(revision 12955)
@@ -0,0 +1,152 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.widgets;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.event.ActionListener;
+import java.awt.event.ActionEvent;
+import java.awt.geom.Path2D;
+
+import javax.swing.Action;
+import javax.swing.Icon;
+import javax.swing.JButton;
+import javax.swing.JPopupMenu;
+
+/**
+ * Button triggering the appearance of a JPopupMenu when activated.
+ * @since 12955
+ */
+public class PopupMenuButton extends JButton implements ActionListener {
+    private JPopupMenu menu;
+
+    /**
+     * @see JButton#JButton()
+     */
+    public PopupMenuButton() {
+        super();
+        this.initialize();
+    }
+
+    /**
+     * @see JButton#JButton(Action)
+     */
+    public PopupMenuButton(Action a) {
+        super(a);
+        this.initialize();
+    }
+
+    /**
+     * @see JButton#JButton(Icon)
+     */
+    public PopupMenuButton(Icon i) {
+        super(i);
+        this.initialize();
+    }
+
+    /**
+     * @see JButton#JButton(String)
+     */
+    public PopupMenuButton(String t) {
+        super(t);
+        this.initialize();
+    }
+
+    /**
+     * @see JButton#JButton(String, Icon)
+     */
+    public PopupMenuButton(String t, Icon i) {
+        super(t, i);
+        this.initialize();
+    }
+
+    /**
+     * Pass-through to {@link JButton#JButton()} allowing associated popup menu to be set
+     */
+    public PopupMenuButton(JPopupMenu m) {
+        super();
+        this.initialize(m);
+    }
+
+    /**
+     * Pass-through to {@link JButton#JButton(Action)} allowing associated popup menu to be set
+     */
+    public PopupMenuButton(Action a, JPopupMenu m) {
+        super(a);
+        this.initialize(m);
+    }
+
+    /**
+     * Pass-through to {@link JButton#JButton(Icon)} allowing associated popup menu to be set
+     */
+    public PopupMenuButton(Icon i, JPopupMenu m) {
+        super(i);
+        this.initialize(m);
+    }
+
+    /**
+     * Pass-through to {@link JButton#JButton(String)} allowing associated popup menu to be set
+     */
+    public PopupMenuButton(String t, JPopupMenu m) {
+        super(t);
+        this.initialize(m);
+    }
+
+    /**
+     * Pass-through to {@link JButton#JButton(String, Icon)} allowing associated popup menu to be set
+     */
+    public PopupMenuButton(String t, Icon i, JPopupMenu m) {
+        super(t, i);
+        this.initialize(m);
+    }
+
+    private void initialize(JPopupMenu m) {
+        this.menu = m;
+        this.initialize();
+    }
+
+    private void initialize() {
+        this.addActionListener(this);
+    }
+
+    /**
+     * Get the popup menu associated with this button
+     */
+    public JPopupMenu getPopupMenu() {
+        return this.menu;
+    }
+
+    /**
+     * Set the popup menu associated with this button
+     * @param m Menu to show when button is triggered
+     */
+    public void setPopupMenu(JPopupMenu m) {
+        this.menu = m;
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent e) {
+        this.menu.show(this, 0, this.getHeight());
+    }
+
+    @Override
+    public void paint(Graphics g) {
+        super.paint(g);
+        Graphics2D g2d = (Graphics2D) g;
+
+        //
+        // paint small arrow in bottom right corner
+        //
+        Dimension size = this.getSize();
+
+        Path2D p = new Path2D.Float();
+        p.moveTo(size.getWidth() - 7, size.getHeight() - 4);
+        p.lineTo(size.getWidth() - 1, size.getHeight() - 4);
+        p.lineTo(size.getWidth() - 4, size.getHeight() - 1);
+        p.closePath();
+
+        g2d.setPaint(Color.BLACK);
+        g2d.fill(p);
+    }
+}
