001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.bbox; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.Dimension; 007import java.awt.event.ActionEvent; 008import java.awt.event.ActionListener; 009import java.util.ArrayList; 010import java.util.Collection; 011import java.util.Collections; 012import java.util.Enumeration; 013import java.util.List; 014import java.util.Objects; 015 016import javax.swing.AbstractButton; 017import javax.swing.ButtonGroup; 018import javax.swing.ButtonModel; 019import javax.swing.JCheckBoxMenuItem; 020import javax.swing.JPopupMenu; 021import javax.swing.JRadioButtonMenuItem; 022import javax.swing.JToggleButton; 023 024import org.openstreetmap.gui.jmapviewer.interfaces.TileSource; 025import org.openstreetmap.josm.gui.widgets.PopupMenuButton; 026import org.openstreetmap.josm.tools.ImageProvider; 027 028/** 029 * Button that allows to choose the imagery source used for slippy map background. 030 * @since 1390 031 */ 032public class SourceButton extends PopupMenuButton { 033 protected class TileSourceButtonModel extends JToggleButton.ToggleButtonModel implements ActionListener { 034 protected final TileSource tileSource; 035 036 public TileSourceButtonModel(TileSource tileSource) { 037 this.tileSource = tileSource; 038 this.addActionListener(this); 039 } 040 041 @Override 042 public void actionPerformed(ActionEvent e) { 043 if (SourceButton.this.slippyMapBBoxChooser.getTileController().getTileSource() != this.tileSource) { // prevent infinite recursion 044 SourceButton.this.slippyMapBBoxChooser.toggleMapSource(this.tileSource); 045 } 046 } 047 } 048 049 protected final SlippyMapBBoxChooser slippyMapBBoxChooser; 050 protected final ButtonModel showDownloadAreaButtonModel; 051 private List<TileSource> sources; 052 private ButtonGroup sourceButtonGroup; 053 054 /** 055 * Constructs a new {@code SourceButton}. 056 * @param slippyMapBBoxChooser parent slippy map 057 * @param sources list of imagery sources to display 058 * @param showDownloadAreaButtonModel model for the "Show downloaded area" button 059 * @since 12955 060 */ 061 public SourceButton( 062 SlippyMapBBoxChooser slippyMapBBoxChooser, 063 Collection<TileSource> sources, 064 ButtonModel showDownloadAreaButtonModel 065 ) { 066 super(new ImageProvider("dialogs/layerlist").getResource().getImageIcon(new Dimension(16, 16))); 067 this.showDownloadAreaButtonModel = showDownloadAreaButtonModel; 068 this.slippyMapBBoxChooser = slippyMapBBoxChooser; 069 this.setPreferredSize(new Dimension(24, 24)); 070 this.setSources(sources); 071 } 072 073 protected void generatePopupMenu() { 074 JPopupMenu pm = new JPopupMenu(); 075 this.sourceButtonGroup = new ButtonGroup(); 076 for (TileSource ts : this.sources) { 077 JRadioButtonMenuItem menuItem = new JRadioButtonMenuItem(ts.getName()); 078 TileSourceButtonModel buttonModel = new TileSourceButtonModel(ts); 079 menuItem.setModel(buttonModel); 080 pm.add(menuItem); 081 this.sourceButtonGroup.add(menuItem); 082 083 // attempt to initialize button group matching current state of slippyMapBBoxChooser 084 buttonModel.setSelected(this.slippyMapBBoxChooser.getTileController().getTileSource() == ts); 085 } 086 087 pm.addSeparator(); 088 089 JCheckBoxMenuItem showDownloadAreaItem = new JCheckBoxMenuItem(tr("Show downloaded area")); 090 showDownloadAreaItem.setModel(this.showDownloadAreaButtonModel); 091 pm.add(showDownloadAreaItem); 092 093 this.setPopupMenu(pm); 094 } 095 096 private void setSourceDefault() { 097 Enumeration<AbstractButton> elems = this.sourceButtonGroup.getElements(); 098 if (elems.hasMoreElements()) { 099 elems.nextElement().setSelected(true); 100 } 101 } 102 103 /** 104 * Set the tile sources. 105 * @param sources The tile sources to display 106 * @since 6364 107 */ 108 public final void setSources(Collection<TileSource> sources) { 109 this.sources = new ArrayList<>(Objects.requireNonNull(sources, "sources")); 110 this.generatePopupMenu(); 111 if (this.sourceButtonGroup.getSelection() == null) { 112 this.setSourceDefault(); 113 } 114 } 115 116 /** 117 * Get the tile sources. 118 * @return unmodifiable collection of tile sources 119 */ 120 public final Collection<TileSource> getSources() { 121 return Collections.unmodifiableCollection(this.sources); 122 } 123 124 /** 125 * Get the currently-selected tile source. 126 * @return currently-selected tile source 127 */ 128 public final TileSource getCurrentSource() { 129 TileSourceButtonModel buttonModel = (TileSourceButtonModel) this.sourceButtonGroup.getSelection(); 130 if (buttonModel != null) { 131 return buttonModel.tileSource; 132 } 133 return null; 134 } 135 136 /** 137 * Changes the current imagery source used for slippy map background. 138 * @param tileSource the new imagery source to use 139 */ 140 public void setCurrentMap(TileSource tileSource) { 141 Enumeration<AbstractButton> elems = this.sourceButtonGroup.getElements(); 142 while (elems.hasMoreElements()) { 143 AbstractButton b = elems.nextElement(); 144 if (((TileSourceButtonModel) b.getModel()).tileSource == tileSource) { 145 b.setSelected(true); 146 return; 147 } 148 } 149 // failed to find the correct one 150 this.setSourceDefault(); 151 } 152}