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;
import tim.prune.ExternalTools;
import tim.prune.I18nManager;
-import tim.prune.data.Altitude;
+import tim.prune.UpdateMessageBroker;
+import tim.prune.config.Config;
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
{
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
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
_dialog.pack();
// set progress bar and show dialog
_progressBar.setVisible(false);
- _dialog.show();
+ _dialog.setVisible(true);
return true;
}
{
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());
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);
// 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();
}
});
*/
public void run()
{
+ _saveCancelled = false;
PhotoTableEntry entry = null;
Photo photo = null;
int numPhotos = _photoTableModel.getRowCount();
_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())
+ if (photo != null && photo.isModified())
{
// 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.getTextWithNumber("confirm.saveexif.ok", numSaved));
+ if (numFailed > 0)
+ {
+ JOptionPane.showMessageDialog(_parentFrame,
+ I18nManager.getTextWithNumber("error.saveexif.failed", numFailed),
+ I18nManager.getText("dialog.saveexif.title"), JOptionPane.ERROR_MESSAGE);
+ }
+ if (numForced > 0)
+ {
+ JOptionPane.showMessageDialog(_parentFrame,
+ I18nManager.getTextWithNumber("error.saveexif.forced", numForced),
+ I18nManager.getText("dialog.saveexif.title"), JOptionPane.WARNING_MESSAGE);
+ }
// close dialog, all finished
_dialog.dispose();
}
* 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)
{
+ // If photos don't have a file, then can't save them
+ if (inPhoto.getFile() == null) {
+ return false;
+ }
// Check whether photo file still exists
if (!inPhoto.getFile().exists())
{
}
}
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);
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;
}
{
// 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
* @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)
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().getMetricValue():0);
result[paramOffset + 5] = "-GPSAltitudeRef='Above Sea Level'";
// add the filename to modify
result[paramOffset + 6] = inFile.getAbsolutePath();