]> gitweb.fperrin.net Git - GpsPrune.git/blobdiff - tim/prune/save/ExifSaver.java
Version 11, August 2010
[GpsPrune.git] / tim / prune / save / ExifSaver.java
index ec0c2f446840359f2d9c7a24772c6bb7883ec96e..bc8d220c41ac7559f7695e107272bd04b71f49a1 100644 (file)
@@ -8,6 +8,7 @@ import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.io.File;
 
+import javax.swing.BorderFactory;
 import javax.swing.BoxLayout;
 import javax.swing.JButton;
 import javax.swing.JCheckBox;
@@ -21,12 +22,13 @@ import javax.swing.JTable;
 
 import tim.prune.ExternalTools;
 import tim.prune.I18nManager;
+import tim.prune.UpdateMessageBroker;
+import tim.prune.config.Config;
 import tim.prune.data.Altitude;
 import tim.prune.data.Coordinate;
 import tim.prune.data.DataPoint;
 import tim.prune.data.Photo;
 import tim.prune.data.PhotoList;
-import tim.prune.data.PhotoStatus;
 
 /**
  * Class to call Exiftool to save coordinate information in jpg files
@@ -35,9 +37,12 @@ public class ExifSaver implements Runnable
 {
        private Frame _parentFrame = null;
        private JDialog _dialog = null;
+       private JButton _okButton = null;
        private JCheckBox _overwriteCheckbox = null;
+       private JCheckBox _forceCheckbox = null;
        private JProgressBar _progressBar = null;
        private PhotoTableModel _photoTableModel = null;
+       private boolean _saveCancelled = false;
 
 
        // To preserve timestamps of file use parameter -P
@@ -77,7 +82,7 @@ public class ExifSaver implements Runnable
        public boolean saveExifInformation(PhotoList inPhotoList)
        {
                // Check if external exif tool can be called
-               boolean exifToolInstalled = ExternalTools.isExiftoolInstalled();
+               boolean exifToolInstalled = ExternalTools.isToolInstalled(ExternalTools.TOOL_EXIFTOOL);
                if (!exifToolInstalled)
                {
                        // show warning
@@ -112,7 +117,7 @@ public class ExifSaver implements Runnable
                _dialog.pack();
                // set progress bar and show dialog
                _progressBar.setVisible(false);
-               _dialog.show();
+               _dialog.setVisible(true);
                return true;
        }
 
@@ -125,7 +130,10 @@ public class ExifSaver implements Runnable
        {
                JPanel panel = new JPanel();
                panel.setLayout(new BorderLayout());
-               panel.add(new JLabel(I18nManager.getText("dialog.saveexif.intro")), BorderLayout.NORTH);
+               // Label at top
+               JLabel topLabel = new JLabel(I18nManager.getText("dialog.saveexif.intro"));
+               topLabel.setBorder(BorderFactory.createEmptyBorder(8, 6, 5, 6));
+               panel.add(topLabel, BorderLayout.NORTH);
                // centre panel with most controls
                JPanel centrePanel = new JPanel();
                centrePanel.setLayout(new BorderLayout());
@@ -136,9 +144,16 @@ public class ExifSaver implements Runnable
                JScrollPane scrollPane = new JScrollPane(photoTable);
                scrollPane.setPreferredSize(new Dimension(300, 160));
                tablePanel.add(scrollPane, BorderLayout.CENTER);
+               // Pair of checkboxes
+               JPanel checkPanel = new JPanel();
+               checkPanel.setLayout(new BoxLayout(checkPanel, BoxLayout.Y_AXIS));
                _overwriteCheckbox = new JCheckBox(I18nManager.getText("dialog.saveexif.overwrite"));
                _overwriteCheckbox.setSelected(false);
-               tablePanel.add(_overwriteCheckbox, BorderLayout.SOUTH);
+               checkPanel.add(_overwriteCheckbox);
+               _forceCheckbox = new JCheckBox(I18nManager.getText("dialog.saveexif.force"));
+               _forceCheckbox.setSelected(false);
+               checkPanel.add(_forceCheckbox);
+               tablePanel.add(checkPanel, BorderLayout.SOUTH);
                centrePanel.add(tablePanel, BorderLayout.CENTER);
                // progress bar below main controls
                _progressBar = new JProgressBar(0, 100);
@@ -167,19 +182,22 @@ public class ExifSaver implements Runnable
                // Lower panel with ok and cancel buttons
                JPanel buttonPanel = new JPanel();
                buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
-               JButton okButton = new JButton(I18nManager.getText("button.ok"));
-               okButton.addActionListener(new ActionListener() {
+               _okButton = new JButton(I18nManager.getText("button.ok"));
+               _okButton.addActionListener(new ActionListener() {
                        public void actionPerformed(ActionEvent e)
                        {
+                               // disable ok button
+                               _okButton.setEnabled(false);
                                // start new thread to do save
                                new Thread(ExifSaver.this).start();
                        }
                });
-               buttonPanel.add(okButton);
+               buttonPanel.add(_okButton);
                JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
                cancelButton.addActionListener(new ActionListener() {
                        public void actionPerformed(ActionEvent e)
                        {
+                               _saveCancelled = true;
                                _dialog.dispose();
                        }
                });
@@ -209,6 +227,7 @@ public class ExifSaver implements Runnable
         */
        public void run()
        {
+               _saveCancelled = false;
                PhotoTableEntry entry = null;
                Photo photo = null;
                int numPhotos = _photoTableModel.getRowCount();
@@ -216,32 +235,53 @@ public class ExifSaver implements Runnable
                _progressBar.setValue(0);
                _progressBar.setVisible(true);
                boolean overwriteFlag = _overwriteCheckbox.isSelected();
-               int numSaved = 0;
+               int numSaved = 0, numFailed = 0, numForced = 0;
                // Loop over all photos in list
                for (int i=0; i<numPhotos; i++)
                {
                        entry = _photoTableModel.getPhotoTableEntry(i);
-                       if (entry != null && entry.getSaveFlag())
+                       if (entry != null && entry.getSaveFlag() && !_saveCancelled)
                        {
                                // Only look at photos which are selected and whose status has changed since load
                                photo = entry.getPhoto();
                                if (photo != null && photo.getOriginalStatus() != photo.getCurrentStatus())
                                {
                                        // Increment counter if save successful
-                                       if (savePhoto(photo, overwriteFlag))
-                                       {
+                                       if (savePhoto(photo, overwriteFlag, false)) {
                                                numSaved++;
                                        }
+                                       else {
+                                               if (_forceCheckbox.isSelected() && savePhoto(photo, overwriteFlag, true))
+                                               {
+                                                       numForced++;
+                                               }
+                                               else {
+                                                       numFailed++;
+                                               }
+                                       }
                                }
                        }
                        // update progress bar
                        _progressBar.setValue(i + 1);
                }
                _progressBar.setVisible(false);
-               // Show confirmation dialog
-               JOptionPane.showMessageDialog(_dialog, I18nManager.getText("dialog.saveexif.ok1") + " "
-                       + numSaved + " " + I18nManager.getText("dialog.saveexif.ok2"),
-                       I18nManager.getText("dialog.saveexif.title"), JOptionPane.INFORMATION_MESSAGE);
+               // Show confirmation
+               UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.saveexif.ok1") + " "
+                       + numSaved + " " + I18nManager.getText("confirm.saveexif.ok2"));
+               if (numFailed > 0)
+               {
+                       JOptionPane.showMessageDialog(_parentFrame,
+                               I18nManager.getText("error.saveexif.failed1") + " " + numFailed + " "
+                               + I18nManager.getText("error.saveexif.failed2"),
+                               I18nManager.getText("dialog.saveexif.title"), JOptionPane.ERROR_MESSAGE);
+               }
+               if (numForced > 0)
+               {
+                       JOptionPane.showMessageDialog(_parentFrame,
+                               I18nManager.getText("error.saveexif.forced1") + " " + numForced + " "
+                               + I18nManager.getText("error.saveexif.forced2"),
+                               I18nManager.getText("dialog.saveexif.title"), JOptionPane.WARNING_MESSAGE);
+               }
                // close dialog, all finished
                _dialog.dispose();
        }
@@ -251,9 +291,10 @@ public class ExifSaver implements Runnable
         * Save the details for the given photo
         * @param inPhoto Photo object
         * @param inOverwriteFlag true to overwrite file, false otherwise
+        * @param inForceFlag true to force write, ignoring minor errors
         * @return true if details saved ok
         */
-       private boolean savePhoto(Photo inPhoto, boolean inOverwriteFlag)
+       private boolean savePhoto(Photo inPhoto, boolean inOverwriteFlag, boolean inForceFlag)
        {
                // Check whether photo file still exists
                if (!inPhoto.getFile().exists())
@@ -285,7 +326,7 @@ public class ExifSaver implements Runnable
                        }
                }
                String[] command = null;
-               if (inPhoto.getCurrentStatus() == PhotoStatus.NOT_CONNECTED)
+               if (inPhoto.getCurrentStatus() == Photo.Status.NOT_CONNECTED)
                {
                        // Photo is no longer connected, so delete gps tags
                        command = getDeleteGpsExifTagsCommand(inPhoto.getFile(), inOverwriteFlag);
@@ -293,21 +334,27 @@ public class ExifSaver implements Runnable
                else
                {
                        // Photo is now connected, so write new gps tags
-                       command = getWriteGpsExifTagsCommand(inPhoto.getFile(), inPhoto.getDataPoint(), inOverwriteFlag);
+                       command = getWriteGpsExifTagsCommand(inPhoto.getFile(), inPhoto.getDataPoint(), inOverwriteFlag, inForceFlag);
                }
                // Execute exif command
+               boolean saved = false;
                try
                {
-                       Runtime.getRuntime().exec(command);
+                       Process process = Runtime.getRuntime().exec(command);
+                       // Wait for process to finish so not too many run in parallel
+                       try {
+                               process.waitFor();
+                       }
+                       catch (InterruptedException ie) {}
+                       saved = (process.exitValue() == 0);
                }
                catch (Exception e)
                {
                        // show error message
                        JOptionPane.showMessageDialog(_parentFrame, "Exception: '" + e.getClass().getName() + "' : "
                                + e.getMessage(), I18nManager.getText("dialog.saveexif.title"), JOptionPane.ERROR_MESSAGE);
-                       return false;
                }
-               return true;
+               return saved;
        }
 
 
@@ -321,7 +368,7 @@ public class ExifSaver implements Runnable
        {
                // Make a string array to construct the command and its parameters
                String[] result = new String[inOverwrite?5:4];
-               result[0] = "exiftool";
+               result[0] = Config.getConfigString(Config.KEY_EXIFTOOL_PATH);
                result[1] = "-P";
                if (inOverwrite) {result[2] = " -overwrite_original_in_place";}
                // remove all gps tags
@@ -337,16 +384,22 @@ public class ExifSaver implements Runnable
         * @param inFile file to which to write the tags
         * @param inPoint DataPoint object containing coordinate information
         * @param inOverwrite true to overwrite file, false to create copy
+        * @param inForce true to force write, ignoring minor errors
         * @return external command to write gps tags
         */
-       private static String[] getWriteGpsExifTagsCommand(File inFile, DataPoint inPoint, boolean inOverwrite)
+       private static String[] getWriteGpsExifTagsCommand(File inFile, DataPoint inPoint,
+               boolean inOverwrite, boolean inForce)
        {
                // Make a string array to construct the command and its parameters
-               String[] result = new String[inOverwrite?10:9];
-               result[0] = "exiftool";
+               String[] result = new String[(inOverwrite?10:9) + (inForce?1:0)];
+               result[0] = Config.getConfigString(Config.KEY_EXIFTOOL_PATH);
                result[1] = "-P";
                if (inOverwrite) {result[2] = "-overwrite_original_in_place";}
                int paramOffset = inOverwrite?3:2;
+               if (inForce) {
+                       result[paramOffset] = "-m";
+                       paramOffset++;
+               }
                // To set latitude : -GPSLatitude='12 34 56.78' -GPSLatitudeRef='N'
                // (latitude as space-separated deg min sec, reference as either N or S)
                result[paramOffset] = "-GPSLatitude='" + inPoint.getLatitude().output(Coordinate.FORMAT_DEG_MIN_SEC_WITH_SPACES)
@@ -358,7 +411,7 @@ public class ExifSaver implements Runnable
                result[paramOffset + 3] = "-GPSLongitudeRef=" + inPoint.getLongitude().output(Coordinate.FORMAT_CARDINAL);
                // add altitude if it has it
                result[paramOffset + 4] = "-GPSAltitude="
-                + (inPoint.hasAltitude()?inPoint.getAltitude().getValue(Altitude.FORMAT_METRES):0);
+                + (inPoint.hasAltitude()?inPoint.getAltitude().getValue(Altitude.Format.METRES):0);
                result[paramOffset + 5] = "-GPSAltitudeRef='Above Sea Level'";
                // add the filename to modify
                result[paramOffset + 6] = inFile.getAbsolutePath();