001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.bbox;
003
004import java.awt.Color;
005import java.awt.Dimension;
006import java.awt.Font;
007import java.awt.FontMetrics;
008import java.awt.Graphics;
009import java.awt.Graphics2D;
010import java.awt.Point;
011import java.awt.RenderingHints;
012import java.awt.event.MouseAdapter;
013import java.awt.event.MouseEvent;
014import java.awt.event.MouseListener;
015import java.util.Collection;
016
017import javax.swing.ImageIcon;
018import javax.swing.JComponent;
019
020import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
021import org.openstreetmap.josm.tools.CheckParameterUtil;
022import org.openstreetmap.josm.tools.ImageProvider;
023
024public class SourceButton extends JComponent {
025
026    private static final int LAYER_HEIGHT = 20;
027    private static final int LEFT_PADDING = 5;
028    private static final int TOP_PADDING = 5;
029    private static final int BOTTOM_PADDING = 5;
030
031    private TileSource[] sources;
032
033    private final ImageIcon enlargeImage;
034    private final ImageIcon shrinkImage;
035    private final Dimension hiddenDimension;
036
037    // Calculated after component is added to container
038    private int barWidth;
039    private Dimension shownDimension;
040    private Font font;
041
042    private boolean isEnlarged = false;
043
044    private int currentMap;
045    private final SlippyMapBBoxChooser slippyMapBBoxChooser;
046
047    public SourceButton(SlippyMapBBoxChooser slippyMapBBoxChooser, Collection<TileSource> sources) {
048        this.slippyMapBBoxChooser = slippyMapBBoxChooser;
049        setSources(sources);
050        enlargeImage = ImageProvider.get("layer-switcher-maximize");
051        shrinkImage = ImageProvider.get("layer-switcher-minimize");
052
053        hiddenDimension= new Dimension(enlargeImage.getIconWidth(), enlargeImage.getIconHeight());
054        setPreferredSize(hiddenDimension);
055
056        addMouseListener(mouseListener);
057    }
058
059    private final MouseListener mouseListener = new MouseAdapter() {
060        @Override
061        public void mouseReleased(MouseEvent e) {
062            if (e.getButton() == MouseEvent.BUTTON1) {
063                Point point = e.getPoint();
064                if (isEnlarged) {
065                    if (barWidth < point.x && point.y < shrinkImage.getIconHeight()) {
066                        toggle();
067                    } else {
068                        int result = (point.y - 5) / LAYER_HEIGHT;
069                        if (result >= 0 && result < SourceButton.this.sources.length) {
070                            SourceButton.this.slippyMapBBoxChooser.toggleMapSource(SourceButton.this.sources[result]);
071                            currentMap = result;
072                            toggle();
073                        }
074                    }
075                } else {
076                    toggle();
077                }
078            }
079        }
080    };
081
082    /**
083     * Set the tile sources.
084     * @param sources The tile sources to display
085     * @since 6364
086     */
087    public final void setSources(Collection<TileSource> sources) {
088        CheckParameterUtil.ensureParameterNotNull(sources, "sources");
089        this.sources = sources.toArray(new TileSource[sources.size()]);
090        shownDimension = null;
091    }
092
093    @Override
094    protected void paintComponent(Graphics graphics) {
095        Graphics2D g = (Graphics2D) graphics.create();
096        try {
097            calculateShownDimension();
098            g.setFont(font);
099            if (isEnlarged) {
100                g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
101                int radioButtonSize = 10;
102
103                g.setColor(new Color(0, 0, 139, 179));
104                g.fillRoundRect(0, 0, barWidth + shrinkImage.getIconWidth(), sources.length * LAYER_HEIGHT + TOP_PADDING + BOTTOM_PADDING, 10, 10);
105                for (int i=0; i<sources.length; i++) {
106                    g.setColor(Color.WHITE);
107                    g.fillOval(LEFT_PADDING, TOP_PADDING + i * LAYER_HEIGHT + 6, radioButtonSize, radioButtonSize);
108                    g.drawString(sources[i].getName(), LEFT_PADDING + radioButtonSize + LEFT_PADDING, TOP_PADDING + i * LAYER_HEIGHT + g.getFontMetrics().getHeight());
109                    if (currentMap == i) {
110                        g.setColor(Color.BLACK);
111                        g.fillOval(LEFT_PADDING + 1, TOP_PADDING + 7 + i * LAYER_HEIGHT, radioButtonSize - 2, radioButtonSize - 2);
112                    }
113                }
114
115                g.drawImage(shrinkImage.getImage(), barWidth, 0, null);
116            } else {
117                g.drawImage(enlargeImage.getImage(), 0, 0, null);
118            }
119        } finally {
120            g.dispose();
121        }
122    }
123
124    public void toggle() {
125        this.isEnlarged = !this.isEnlarged;
126        calculateShownDimension();
127        setPreferredSize(isEnlarged?shownDimension:hiddenDimension);
128        revalidate();
129    }
130
131
132    public void setCurrentMap(TileSource tileSource) {
133        for (int i=0; i<sources.length; i++) {
134            if (sources[i].equals(tileSource)) {
135                currentMap = i;
136                return;
137            }
138        }
139        currentMap = 0;
140    }
141
142    private void calculateShownDimension() {
143        if (shownDimension == null) {
144            font = getFont().deriveFont(Font.BOLD).deriveFont(15.0f);
145            int textWidth = 0;
146            FontMetrics fm = getFontMetrics(font);
147            for (TileSource source: sources) {
148                int width = fm.stringWidth(source.getName());
149                if (width > textWidth) {
150                    textWidth = width;
151                }
152            }
153            barWidth = textWidth + 50;
154            shownDimension = new Dimension(barWidth + shrinkImage.getIconWidth(), sources.length * LAYER_HEIGHT + TOP_PADDING + BOTTOM_PADDING);
155        }
156    }
157}