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