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