]> gitweb.fperrin.net Git - GpsPrune.git/blobdiff - src/tim/prune/function/cache/ManageCacheFunction.java
Moved source into separate src directory due to popular request
[GpsPrune.git] / src / tim / prune / function / cache / ManageCacheFunction.java
diff --git a/src/tim/prune/function/cache/ManageCacheFunction.java b/src/tim/prune/function/cache/ManageCacheFunction.java
new file mode 100644 (file)
index 0000000..5431e9e
--- /dev/null
@@ -0,0 +1,414 @@
+package tim.prune.function.cache;
+
+import java.awt.BorderLayout;
+import java.awt.CardLayout;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.ButtonGroup;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JProgressBar;
+import javax.swing.JRadioButton;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.ListSelectionModel;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.config.Config;
+import tim.prune.gui.WholeNumberField;
+
+/**
+ * Function class to manage the tile cache on local disk
+ */
+public class ManageCacheFunction extends GenericFunction implements Runnable
+{
+       private JDialog _dialog = null;
+       private CardLayout _cards = null;
+       private JPanel _cardPanel = null;
+       private JProgressBar _progressBar = null;
+       private File _cacheDir = null;
+       private TileCacheModel _model = null;
+       private JTable _setsTable = null;
+       private JButton _deleteSetButton = null;
+       private JLabel _tileSetLabel = null, _zoomLabel = null;
+       private JLabel _ageLabel = null;
+       private JRadioButton _deleteAllRadio = null;
+       private WholeNumberField _daysField = null;
+
+       private static TileFilter _TILEFILTER = new TileFilter();
+
+
+       /**
+        * Constructor
+        * @param inApp App object
+        */
+       public ManageCacheFunction(App inApp) {
+               super(inApp);
+       }
+
+       /**
+        * @return name key
+        */
+       public String getNameKey() {
+               return "function.managetilecache";
+       }
+
+       /**
+        * Show the dialog to start
+        */
+       public void begin()
+       {
+               // First check if directory even exists
+               _cacheDir = null;
+               String path = Config.getConfigString(Config.KEY_DISK_CACHE);
+               if (path != null && !path.equals("")) {
+                       _cacheDir = new File(path);
+               }
+               if (_cacheDir == null || !_cacheDir.exists() || !_cacheDir.isDirectory())
+               {
+                       _app.showErrorMessage(getNameKey(), "error.cache.notthere");
+                       return;
+               }
+
+               // Build the dialog if it hasn't already been built
+               if (_dialog == null)
+               {
+                       _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true); // modal
+                       _dialog.setLocationRelativeTo(_parentFrame);
+                       _cardPanel = makeContents();
+                       _dialog.getContentPane().add(_cardPanel);
+                       _dialog.pack();
+               }
+               // Start a new thread to build the model
+               new Thread(this).start();
+               // Show the first panel of the dialog including progress bar
+               _cards.first(_cardPanel);
+               _dialog.setVisible(true);
+       }
+
+
+       /**
+        * Make the components for the dialog
+        * @return contents inside a panel
+        */
+       private JPanel makeContents()
+       {
+               JPanel dialogPanel = new JPanel();
+               _cards = new CardLayout();
+               dialogPanel.setLayout(_cards);
+
+               // Make first card including progress bar
+               JPanel firstCard = new JPanel();
+               firstCard.setLayout(new BorderLayout());
+               JPanel progPanel = new JPanel();
+               progPanel.setLayout(new BoxLayout(progPanel, BoxLayout.Y_AXIS));
+               progPanel.add(Box.createVerticalGlue());
+               progPanel.add(new JLabel(I18nManager.getText("confirm.running")));
+               _progressBar = new JProgressBar(0, 10);
+               _progressBar.setIndeterminate(true);
+               progPanel.add(_progressBar);
+               progPanel.add(Box.createVerticalGlue());
+               firstCard.add(progPanel, BorderLayout.CENTER);
+               // Cancel button at bottom
+               JPanel buttonPanel = new JPanel();
+               buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+               JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
+               cancelButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               // Cancel model building and close dialog
+                               if (_model != null) _model.cancel();
+                               _dialog.dispose();
+                       }
+               });
+               buttonPanel.add(cancelButton);
+               firstCard.add(buttonPanel, BorderLayout.SOUTH);
+               dialogPanel.add(firstCard, "card1");
+
+               // Make second card including tileset table
+               JPanel secondCard = new JPanel();
+               secondCard.setLayout(new BorderLayout());
+               // Table in the middle
+               JPanel midPanel = new JPanel();
+               midPanel.setLayout(new BorderLayout());
+               _setsTable = new JTable();
+               _setsTable.setPreferredScrollableViewportSize(new Dimension(500, 130));
+               _setsTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+               midPanel.add(new JScrollPane(_setsTable), BorderLayout.CENTER);
+               midPanel.setBorder(BorderFactory.createEmptyBorder(8, 5, 8, 5));
+               secondCard.add(midPanel, BorderLayout.CENTER);
+               // Activate buttons if a tileset is selected
+               _setsTable.getSelectionModel().addListSelectionListener(
+                       new ListSelectionListener() {
+                               public void valueChanged(ListSelectionEvent e) {
+                                       ListSelectionModel lsm = (ListSelectionModel) e.getSource();
+                                       _deleteSetButton.setEnabled(!lsm.isSelectionEmpty());
+                               }
+                       });
+
+               // button panel at bottom
+               buttonPanel = new JPanel();
+               // left group
+               JPanel leftPanel = new JPanel();
+               leftPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
+               _deleteSetButton = new JButton(I18nManager.getText("button.delete"));
+               _deleteSetButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent arg0) {
+                               showDeleteCard();
+                       }
+               });
+               leftPanel.add(_deleteSetButton);
+               // right group
+               JPanel rightPanel = new JPanel();
+               rightPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+               JButton closeButton = new JButton(I18nManager.getText("button.close"));
+               closeButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               if (_dialog != null) _dialog.dispose();
+                       }
+               });
+               rightPanel.add(closeButton);
+               buttonPanel.add(leftPanel, BorderLayout.WEST);
+               buttonPanel.add(rightPanel, BorderLayout.EAST);
+               secondCard.add(buttonPanel, BorderLayout.SOUTH);
+               dialogPanel.add(secondCard, "card2");
+
+               // Make third card including delete options
+               JPanel thirdCard = new JPanel();
+               thirdCard.setLayout(new BorderLayout());
+               // main panel
+               JPanel mainPanel = new JPanel();
+               GridBagLayout gridbag = new GridBagLayout();
+               GridBagConstraints c = new GridBagConstraints();
+               mainPanel.setLayout(gridbag);
+               c.gridx = 0; c.gridy = 0;
+               c.gridheight = 1; c.gridwidth = 2;
+               c.weightx = 0.0; c.weighty = 0.0;
+               c.anchor = GridBagConstraints.FIRST_LINE_START;
+               _tileSetLabel = new JLabel("dummy text to be replaced");
+               mainPanel.add(_tileSetLabel, c);
+               c.gridx = 0; c.gridy = 1;
+               c.ipady = 20;
+               _zoomLabel = new JLabel("dummy text to be replaced");
+               mainPanel.add(_zoomLabel, c);
+
+               JRadioButton deleteOldRadio = new JRadioButton(I18nManager.getText("dialog.diskcache.deleteold"));
+               _deleteAllRadio = new JRadioButton(I18nManager.getText("dialog.diskcache.deleteall"));
+               ButtonGroup bGroup = new ButtonGroup();
+               bGroup.add(_deleteAllRadio);
+               bGroup.add(deleteOldRadio);
+               _deleteAllRadio.addChangeListener(new ChangeListener() {
+                       public void stateChanged(ChangeEvent e) {
+                               enableAgeFields();
+                       }
+               });
+               c.gridx = 0; c.gridy = 2;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.ipady = 0;
+               mainPanel.add(deleteOldRadio, c);
+               c.gridwidth = 1;
+               c.gridx = 0; c.gridy = 3;
+               c.insets = new Insets(0, 40, 0, 0);
+               _ageLabel = new JLabel(I18nManager.getText("dialog.diskcache.maximumage"));
+               mainPanel.add(_ageLabel, c);
+               _daysField = new WholeNumberField(2);
+               _daysField.setMinimumSize(new Dimension(20, 1));
+               _daysField.setText("30"); // default is 30 days
+               c.gridx = 1; c.gridy = 3;
+               c.ipadx = 20;
+               c.insets = new Insets(0, 15, 0, 0);
+               mainPanel.add(_daysField, c);
+               c.gridx = 0; c.gridy = 4;
+               c.gridwidth = 2;
+               c.ipadx = 0;
+               c.insets = new Insets(0, 0, 0, 0);
+               mainPanel.add(_deleteAllRadio, c);
+               _deleteAllRadio.setSelected(true);
+               thirdCard.add(mainPanel, BorderLayout.CENTER);
+               // button panel
+               buttonPanel = new JPanel();
+               buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+               JButton deleteButton = new JButton(I18nManager.getText("button.delete"));
+               deleteButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent arg0) {
+                               deleteCurrentSets();
+                       }
+               });
+               buttonPanel.add(deleteButton);
+               cancelButton = new JButton(I18nManager.getText("button.cancel"));
+               cancelButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               // Go back to second card
+                               _cards.show(_cardPanel, "card2");
+                       }
+               });
+               buttonPanel.add(cancelButton);
+               thirdCard.add(buttonPanel, BorderLayout.SOUTH);
+               dialogPanel.add(thirdCard, "card3");
+               return dialogPanel;
+       }
+
+
+       /**
+        * Construct the model in a separate thread
+        * and go on to the second panel of the dialog
+        */
+       public void run()
+       {
+               // TODO: Maybe this can be speeded up so that it just finds the tilesets first and then gets the details later
+               // Check if directory has anything in it
+               _model = new TileCacheModel(_cacheDir);
+               _model.buildTileSets();
+               if (_model.isAborted()) return;
+
+               if (_model.getNumTileSets() <= 0)
+               {
+                       _app.showErrorMessage(getNameKey(), "error.cache.empty");
+                       _dialog.dispose();
+                       return;
+               }
+
+               // Set controls according to current config
+               _setsTable.setModel(new TileSetTableModel(_model));
+               _deleteSetButton.setEnabled(false);
+               // Set column widths after model has been set
+               _setsTable.getColumnModel().getColumn(0).setPreferredWidth(220);
+               _setsTable.getColumnModel().getColumn(1).setPreferredWidth(200);
+               // Show second panel
+               _cards.next(_cardPanel);
+       }
+
+       /**
+        * Prepare and show the delete panel
+        */
+       private void showDeleteCard()
+       {
+               // set tileset label
+               int numSelected = 0;
+               String desc = null;
+               RowInfo totals = new RowInfo();
+               for (int i=0; i<_setsTable.getRowCount(); i++) {
+                       if (_setsTable.isRowSelected(i))
+                       {
+                               if (desc == null) desc = _model.getTileSet(i).getPath();
+                               totals.addRow(_model.getTileSet(i).getRowInfo());
+                               numSelected++;
+                       }
+               }
+               if (numSelected == 0) return;
+               String tileSetDesc = (numSelected == 1?desc:I18nManager.getText("dialog.diskcache.tileset.multiple"));
+               _tileSetLabel.setText(I18nManager.getText("dialog.diskcache.tileset") + " : "
+                       + tileSetDesc);
+               _zoomLabel.setText(I18nManager.getText("dialog.diskcache.table.zoom") + " : "
+                       + totals.getZoomRange());
+
+               // enable/disable edit fields
+               enableAgeFields();
+               // show the next card
+               _cards.next(_cardPanel);
+       }
+
+       /**
+        * Enable or disable the fields for entering tile age
+        */
+       private void enableAgeFields()
+       {
+               boolean showAgeBoxes = !_deleteAllRadio.isSelected();
+               _ageLabel.setEnabled(showAgeBoxes);
+               _daysField.setEnabled(showAgeBoxes);
+       }
+
+       /**
+        * Try to delete all the files in the currently selected tilesets
+        * (Maybe more than one tileset is selected in the table)
+        */
+       private void deleteCurrentSets()
+       {
+               // Determine age limit if given
+               int ageLimit = -1;
+               if (!_deleteAllRadio.isSelected()) {
+                       ageLimit = _daysField.getValue();
+               }
+               // Loop over selected tilesets and delete them
+               int totalDeleted = 0;
+               for (int i=0; i<_setsTable.getRowCount(); i++)
+               {
+                       if (_setsTable.isRowSelected(i))
+                       {
+                               File dir = new File(_model.getCacheDir(), _model.getTileSet(i).getPath());
+                               if (dir.exists())
+                               {
+                                       int numFilesDeleted = deleteFilesFrom(dir, ageLimit);
+                                       if (numFilesDeleted > 0) {
+                                               totalDeleted += numFilesDeleted;
+                                       }
+                               }
+                       }
+               }
+               if (totalDeleted > 0)
+               {
+                       // Show confirmation message
+                       JOptionPane.showMessageDialog(_dialog, I18nManager.getTextWithNumber("dialog.diskcache.deleted", totalDeleted),
+                               I18nManager.getText(getNameKey()), JOptionPane.INFORMATION_MESSAGE);
+                       // reload model
+                       _cards.first(_cardPanel);
+                       new Thread(this).start();
+               }
+               else {
+                       _app.showErrorMessage(getNameKey(), "error.cache.cannotdelete");
+               }
+       }
+
+
+       /**
+        * Delete recursively all files which are older than the age limit
+        * @param inDir directory to delete from
+        * @param inMaxDays age limit in days
+        * @return number of files deleted
+        */
+       private static int deleteFilesFrom(File inDir, int inMaxDays)
+       {
+               int numDeleted = 0;
+               long now = System.currentTimeMillis();
+               if (inDir.exists() && inDir.isDirectory())
+               {
+                       for (File subdir : inDir.listFiles())
+                       {
+                               if (subdir.isDirectory()) {
+                                       numDeleted += deleteFilesFrom(subdir, inMaxDays);
+                               }
+                               else if (subdir.isFile() && subdir.exists() && _TILEFILTER.accept(subdir))
+                               {
+                                       long fileAge = (now - subdir.lastModified()) / 1000 / 60 / 60 / 24;
+                                       if (inMaxDays < 0 || fileAge > inMaxDays)
+                                       {
+                                               if (subdir.delete()) {
+                                                       numDeleted++;
+                                               }
+                                       }
+                               }
+                       }
+                       // Try to delete the directory (doesn't work if not empty)
+                       inDir.delete();
+               }
+               return numDeleted;
+       }
+}