001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.preferences.imagery;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.awt.Color;
007import java.awt.GridBagLayout;
008import java.awt.event.ActionEvent;
009import java.awt.event.ActionListener;
010import java.io.File;
011import java.io.FilenameFilter;
012
013import javax.swing.JButton;
014import javax.swing.JColorChooser;
015import javax.swing.JLabel;
016import javax.swing.JOptionPane;
017import javax.swing.JPanel;
018import javax.swing.JSlider;
019import javax.swing.JSpinner;
020import javax.swing.SpinnerNumberModel;
021
022import org.openstreetmap.josm.data.cache.JCSCacheManager;
023import org.openstreetmap.josm.data.imagery.CachedTileLoaderFactory;
024import org.openstreetmap.josm.gui.layer.AbstractCachedTileSourceLayer;
025import org.openstreetmap.josm.gui.layer.AbstractTileSourceLayer;
026import org.openstreetmap.josm.gui.layer.ImageryLayer;
027import org.openstreetmap.josm.gui.widgets.JosmComboBox;
028import org.openstreetmap.josm.gui.widgets.JosmTextField;
029import org.openstreetmap.josm.tools.ColorHelper;
030import org.openstreetmap.josm.tools.GBC;
031import org.openstreetmap.josm.tools.Utils;
032
033/**
034 * {@code JPanel} giving access to common imagery settings.
035 * @since 5465
036 */
037public class CommonSettingsPanel extends JPanel {
038
039    // Common Settings
040    private final JButton btnFadeColor;
041    private final JSlider fadeAmount = new JSlider(0, 100);
042    private final JosmComboBox<String> sharpen;
043    private final JosmTextField tilecacheDir = new JosmTextField();
044    private final JSpinner maxElementsOnDisk;
045    private final JSlider tilesZoom = new JSlider(-2, 2, 0);
046
047
048    /**
049     * Constructs a new {@code CommonSettingsPanel}.
050     */
051    public CommonSettingsPanel() {
052        super(new GridBagLayout());
053
054        this.maxElementsOnDisk = new JSpinner(new SpinnerNumberModel(
055                AbstractCachedTileSourceLayer.MAX_DISK_CACHE_SIZE.get().intValue(), 0, Integer.MAX_VALUE, 1));
056
057
058        this.btnFadeColor = new JButton();
059
060        this.btnFadeColor.addActionListener(new ActionListener() {
061            @Override
062            public void actionPerformed(ActionEvent e) {
063                JColorChooser chooser = new JColorChooser(btnFadeColor.getBackground());
064                int answer = JOptionPane.showConfirmDialog(
065                        CommonSettingsPanel.this, chooser,
066                        tr("Choose a color for {0}", tr("imagery fade")),
067                        JOptionPane.OK_CANCEL_OPTION,
068                        JOptionPane.PLAIN_MESSAGE);
069                if (answer == JOptionPane.OK_OPTION) {
070                    Color colFadeColor = chooser.getColor();
071                    btnFadeColor.setBackground(colFadeColor);
072                    btnFadeColor.setText(ColorHelper.color2html(colFadeColor));
073                }
074            }
075        });
076
077        add(new JLabel(tr("Fade Color: ")), GBC.std());
078        add(GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL));
079        add(this.btnFadeColor, GBC.eol().fill(GBC.HORIZONTAL));
080
081        add(new JLabel(tr("Fade amount: ")), GBC.std());
082        add(GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL));
083        add(this.fadeAmount, GBC.eol().fill(GBC.HORIZONTAL));
084
085        this.sharpen = new JosmComboBox<>(new String[] {
086                tr("None"),
087                tr("Soft"),
088                tr("Strong")});
089        add(new JLabel(tr("Sharpen (requires layer re-add): ")));
090        add(GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL));
091        add(this.sharpen, GBC.eol().fill(GBC.HORIZONTAL));
092
093        add(new JLabel(tr("Tile cache directory: ")), GBC.std());
094        add(GBC.glue(5, 0), GBC.std());
095        add(tilecacheDir, GBC.eol().fill(GBC.HORIZONTAL));
096
097        add(new JLabel(tr("Maximum size of disk cache (per imagery) in MB: ")), GBC.std());
098        add(GBC.glue(5, 0), GBC.std());
099        add(this.maxElementsOnDisk, GBC.eol());
100
101        this.tilesZoom.setPaintLabels(true);
102        this.tilesZoom.setMajorTickSpacing(2);
103        this.tilesZoom.setMinorTickSpacing(1);
104        this.tilesZoom.setPaintTicks(true);
105        add(new JLabel(tr("Tiles zoom offset:")));
106        add(GBC.glue(5, 0), GBC.std());
107        add(this.tilesZoom, GBC.eol());
108    }
109
110    /**
111     * Loads the common settings.
112     */
113    public void loadSettings() {
114        Color colFadeColor = ImageryLayer.PROP_FADE_COLOR.get();
115        this.btnFadeColor.setBackground(colFadeColor);
116        this.btnFadeColor.setText(ColorHelper.color2html(colFadeColor));
117        this.fadeAmount.setValue(ImageryLayer.PROP_FADE_AMOUNT.get());
118        this.sharpen.setSelectedIndex(Math.max(0, Math.min(2, ImageryLayer.PROP_SHARPEN_LEVEL.get())));
119        this.tilecacheDir.setText(CachedTileLoaderFactory.PROP_TILECACHE_DIR.get());
120        this.maxElementsOnDisk.setValue(AbstractCachedTileSourceLayer.MAX_DISK_CACHE_SIZE.get());
121        this.tilesZoom.setValue(AbstractTileSourceLayer.ZOOM_OFFSET.get());
122    }
123
124    /**
125     * Saves the common settings.
126     * @return true when restart is required
127     */
128    public boolean saveSettings() {
129        ImageryLayer.PROP_FADE_AMOUNT.put(this.fadeAmount.getValue());
130        ImageryLayer.PROP_FADE_COLOR.put(this.btnFadeColor.getBackground());
131        ImageryLayer.PROP_SHARPEN_LEVEL.put(sharpen.getSelectedIndex());
132
133        boolean restartRequired = false;
134        if (!AbstractCachedTileSourceLayer.MAX_DISK_CACHE_SIZE.get().equals(this.maxElementsOnDisk.getValue())) {
135            if (((Integer) this.maxElementsOnDisk.getValue()) < AbstractCachedTileSourceLayer.MAX_DISK_CACHE_SIZE.get() &&
136                    JCSCacheManager.USE_BLOCK_CACHE.get()) {
137                // reducing size of the cache, this requires deletion of the files
138                removeCacheFiles(CachedTileLoaderFactory.PROP_TILECACHE_DIR.get());
139            }
140            AbstractCachedTileSourceLayer.MAX_DISK_CACHE_SIZE.put((Integer) this.maxElementsOnDisk.getValue());
141            restartRequired = true;
142        }
143
144
145        if (!CachedTileLoaderFactory.PROP_TILECACHE_DIR.get().equals(this.tilecacheDir.getText())) {
146            restartRequired = true;
147            removeCacheFiles(CachedTileLoaderFactory.PROP_TILECACHE_DIR.get()); // clear old cache directory
148            CachedTileLoaderFactory.PROP_TILECACHE_DIR.put(this.tilecacheDir.getText());
149        }
150
151        if (!AbstractTileSourceLayer.ZOOM_OFFSET.get().equals(this.tilesZoom.getValue())) {
152            // TODO: make warning about too small MEMORY_CACHE_SIZE?
153            AbstractTileSourceLayer.ZOOM_OFFSET.put(this.tilesZoom.getValue());
154            restartRequired = true;
155        }
156        return restartRequired;
157    }
158
159    private void removeCacheFiles(String path) {
160        File directory = new File(path);
161        File[] cacheFiles = directory.listFiles(new FilenameFilter() {
162            @Override
163            public boolean accept(File dir, String name) {
164                return name.endsWith(".data") || name.endsWith(".key");
165            }
166
167        });
168        JCSCacheManager.shutdown(); // shutdown Cache - so files can by safely deleted
169        for (File cacheFile: cacheFiles) {
170            Utils.deleteFile(cacheFile);
171        }
172    }
173}