summaryrefslogtreecommitdiff
path: root/src/Photo.vala
diff options
context:
space:
mode:
Diffstat (limited to 'src/Photo.vala')
-rw-r--r--src/Photo.vala161
1 files changed, 110 insertions, 51 deletions
diff --git a/src/Photo.vala b/src/Photo.vala
index b67457e..f31a17d 100644
--- a/src/Photo.vala
+++ b/src/Photo.vala
@@ -155,7 +155,7 @@ public enum Rating {
// particular photo without modifying the backing image file. The interface allows for
// transformations to be stored persistently elsewhere or in memory until they're committed en
// masse to an image file.
-public abstract class Photo : PhotoSource, Dateable {
+public abstract class Photo : PhotoSource, Dateable, Positionable {
// Need to use "thumb" rather than "photo" for historical reasons -- this name is used
// directly to load thumbnails from disk by already-existing filenames
public const string TYPENAME = "thumb";
@@ -183,7 +183,7 @@ public abstract class Photo : PhotoSource, Dateable {
"pns", "jps", "mpo",
// RAW extensions
- "3fr", "arw", "srf", "sr2", "bay", "crw", "cr2", "cap", "iiq", "eip", "dcs", "dcr", "drf",
+ "3fr", "arw", "srf", "sr2", "bay", "crw", "cr2", "cr3", "cap", "iiq", "eip", "dcs", "dcr", "drf",
"k25", "kdc", "dng", "erf", "fff", "mef", "mos", "mrw", "nef", "nrw", "orf", "ptx", "pef",
"pxn", "r3d", "raf", "raw", "rw2", "rwl", "rwz", "x3f", "srw"
};
@@ -210,7 +210,7 @@ public abstract class Photo : PhotoSource, Dateable {
// Here, we cache the exposure time to avoid paying to access the row every time we
// need to know it. This is initially set in the constructor, and updated whenever
// the exposure time is set (please see set_exposure_time() for details).
- private time_t cached_exposure_time;
+ private DateTime? cached_exposure_time;
public enum Exception {
NONE = 0,
@@ -640,7 +640,7 @@ public abstract class Photo : PhotoSource, Dateable {
File file = File.new_for_path(bpr.filepath);
FileInfo info = file.query_info(DirectoryMonitor.SUPPLIED_ATTRIBUTES,
FileQueryInfoFlags.NOFOLLOW_SYMLINKS, null);
- TimeVal timestamp = info.get_modification_time();
+ var timestamp = info.get_modification_date_time();
PhotoFileInterrogator interrogator = new PhotoFileInterrogator(
file, PhotoFileSniffer.Options.GET_ALL);
@@ -655,7 +655,7 @@ public abstract class Photo : PhotoSource, Dateable {
bpr.dim = detected.image_dim;
bpr.filesize = info.get_size();
- bpr.timestamp = timestamp.tv_sec;
+ bpr.timestamp = timestamp;
bpr.original_orientation = detected.metadata != null ? detected.metadata.get_orientation() :
Orientation.TOP_LEFT;
@@ -832,7 +832,7 @@ public abstract class Photo : PhotoSource, Dateable {
if (!developments.has_key(d))
return; // we tried!
- // Disgard changes.
+ // Discard changes.
revert_to_master(false);
// Switch master to the new photo.
@@ -1185,7 +1185,7 @@ public abstract class Photo : PhotoSource, Dateable {
return ImportResult.UNSUPPORTED_FORMAT;
}
- TimeVal timestamp = info.get_modification_time();
+ var timestamp = info.get_modification_date_time();
// if all MD5s supplied, don't sniff for them
if (params.exif_md5 != null && params.thumbnail_md5 != null && params.full_md5 != null)
@@ -1217,8 +1217,9 @@ public abstract class Photo : PhotoSource, Dateable {
}
Orientation orientation = Orientation.TOP_LEFT;
- time_t exposure_time = 0;
+ DateTime? exposure_time = null;
string title = "";
+ GpsCoords gps_coords = GpsCoords();
string comment = "";
Rating rating = Rating.UNRATED;
@@ -1234,6 +1235,7 @@ public abstract class Photo : PhotoSource, Dateable {
orientation = detected.metadata.get_orientation();
title = detected.metadata.get_title();
+ gps_coords = detected.metadata.get_gps_coords();
comment = detected.metadata.get_comment();
params.keywords = detected.metadata.get_keywords();
rating = detected.metadata.get_rating();
@@ -1255,7 +1257,7 @@ public abstract class Photo : PhotoSource, Dateable {
params.row.master.filepath = file.get_path();
params.row.master.dim = detected.image_dim;
params.row.master.filesize = info.get_size();
- params.row.master.timestamp = timestamp.tv_sec;
+ params.row.master.timestamp = timestamp;
params.row.exposure_time = exposure_time;
params.row.orientation = orientation;
params.row.master.original_orientation = orientation;
@@ -1269,6 +1271,7 @@ public abstract class Photo : PhotoSource, Dateable {
params.row.flags = 0;
params.row.master.file_format = detected.file_format;
params.row.title = title;
+ params.row.gps_coords = gps_coords;
params.row.comment = comment;
params.row.rating = rating;
@@ -1296,8 +1299,8 @@ public abstract class Photo : PhotoSource, Dateable {
params.row.master.filepath = file.get_path();
params.row.master.dim = Dimensions(0,0);
params.row.master.filesize = 0;
- params.row.master.timestamp = 0;
- params.row.exposure_time = 0;
+ params.row.master.timestamp = null;
+ params.row.exposure_time = null;
params.row.orientation = Orientation.TOP_LEFT;
params.row.master.original_orientation = Orientation.TOP_LEFT;
params.row.import_id = params.import_id;
@@ -1310,6 +1313,7 @@ public abstract class Photo : PhotoSource, Dateable {
params.row.flags = 0;
params.row.master.file_format = PhotoFileFormat.JFIF;
params.row.title = null;
+ params.row.gps_coords = GpsCoords();
params.row.comment = null;
params.row.rating = Rating.UNRATED;
@@ -1350,10 +1354,10 @@ public abstract class Photo : PhotoSource, Dateable {
return null;
}
- TimeVal modification_time = info.get_modification_time();
+ var modification_time = info.get_modification_date_time();
backing.filepath = file.get_path();
- backing.timestamp = modification_time.tv_sec;
+ backing.timestamp = modification_time;
backing.filesize = info.get_size();
backing.file_format = detected.file_format;
backing.dim = detected.image_dim;
@@ -1462,14 +1466,22 @@ public abstract class Photo : PhotoSource, Dateable {
list += "image:orientation";
updated_row.master.original_orientation = backing.original_orientation;
}
-
+
+ GpsCoords gps_coords = GpsCoords();
+
if (detected.metadata != null) {
MetadataDateTime? date_time = detected.metadata.get_exposure_date_time();
- if (date_time != null && updated_row.exposure_time != date_time.get_timestamp())
+ if (date_time != null && updated_row.exposure_time != null &&
+ !updated_row.exposure_time.equal(date_time.get_timestamp()))
list += "metadata:exposure-time";
if (updated_row.title != detected.metadata.get_title())
list += "metadata:name";
+
+ gps_coords = detected.metadata.get_gps_coords();
+ if (updated_row.gps_coords != gps_coords)
+ list += "metadata:gps";
+
if (updated_row.comment != detected.metadata.get_comment())
list += "metadata:comment";
@@ -1490,7 +1502,8 @@ public abstract class Photo : PhotoSource, Dateable {
MetadataDateTime? date_time = detected.metadata.get_exposure_date_time();
if (date_time != null)
updated_row.exposure_time = date_time.get_timestamp();
-
+
+ updated_row.gps_coords = gps_coords;
updated_row.title = detected.metadata.get_title();
updated_row.comment = detected.metadata.get_comment();
updated_row.rating = detected.metadata.get_rating();
@@ -1601,6 +1614,7 @@ public abstract class Photo : PhotoSource, Dateable {
if (reimport_state.metadata != null) {
set_title(reimport_state.metadata.get_title());
+ set_gps_coords(reimport_state.metadata.get_gps_coords());
set_comment(reimport_state.metadata.get_comment());
set_rating(reimport_state.metadata.get_rating());
apply_user_metadata_for_reimport(reimport_state.metadata);
@@ -1695,17 +1709,17 @@ public abstract class Photo : PhotoSource, Dateable {
// Use this only if the master file's modification time has been changed (i.e. touched)
public void set_master_timestamp(FileInfo info) {
- TimeVal modification = info.get_modification_time();
+ var modification = info.get_modification_date_time();
try {
lock (row) {
- if (row.master.timestamp == modification.tv_sec)
+ if (row.master.timestamp.equal(modification))
return;
- PhotoTable.get_instance().update_timestamp(row.photo_id, modification.tv_sec);
- row.master.timestamp = modification.tv_sec;
+ PhotoTable.get_instance().update_timestamp(row.photo_id, modification);
+ row.master.timestamp = modification;
}
- } catch (DatabaseError err) {
+ } catch (Error err) {
AppWindow.database_error(err);
return;
@@ -1718,15 +1732,15 @@ public abstract class Photo : PhotoSource, Dateable {
}
// Use this only if the editable file's modification time has been changed (i.e. touched)
- public void update_editable_modification_time(FileInfo info) throws DatabaseError {
- TimeVal modification = info.get_modification_time();
+ public void update_editable_modification_time(FileInfo info) throws Error {
+ var modification = info.get_modification_date_time();
bool altered = false;
lock (row) {
- if (row.editable_id.is_valid() && editable.timestamp != modification.tv_sec) {
+ if (row.editable_id.is_valid() && !editable.timestamp.equal(modification)) {
BackingPhotoTable.get_instance().update_timestamp(row.editable_id,
- modification.tv_sec);
- editable.timestamp = modification.tv_sec;
+ modification);
+ editable.timestamp = modification;
altered = true;
}
}
@@ -1739,8 +1753,13 @@ public abstract class Photo : PhotoSource, Dateable {
public static void update_many_editable_timestamps(Gee.Map<Photo, FileInfo> map)
throws DatabaseError {
DatabaseTable.begin_transaction();
- foreach (Photo photo in map.keys)
- photo.update_editable_modification_time(map.get(photo));
+ foreach (Photo photo in map.keys) {
+ try {
+ photo.update_editable_modification_time(map.get(photo));
+ } catch (Error err) {
+ debug("Failed to update modification time: %s", err.message);
+ }
+ }
DatabaseTable.commit_transaction();
}
@@ -1853,7 +1872,7 @@ public abstract class Photo : PhotoSource, Dateable {
}
}
}
- } catch (DatabaseError err) {
+ } catch (Error err) {
AppWindow.database_error(err);
}
@@ -1906,7 +1925,7 @@ public abstract class Photo : PhotoSource, Dateable {
}
}
}
- } catch (DatabaseError err) {
+ } catch (Error err) {
AppWindow.database_error(err);
}
@@ -1993,7 +2012,7 @@ public abstract class Photo : PhotoSource, Dateable {
}
}
- public override time_t get_timestamp() {
+ public override DateTime? get_timestamp() {
lock (row) {
return backing_photo_row.timestamp;
}
@@ -2169,7 +2188,7 @@ public abstract class Photo : PhotoSource, Dateable {
}
}
- public void set_master_metadata_dirty(bool dirty) throws DatabaseError {
+ public void set_master_metadata_dirty(bool dirty) throws Error {
bool committed = false;
lock (row) {
if (row.metadata_dirty != dirty) {
@@ -2277,7 +2296,7 @@ public abstract class Photo : PhotoSource, Dateable {
error("Unable to read file information for %s: %s", to_string(), err.message);
}
- TimeVal timestamp = info.get_modification_time();
+ var timestamp = info.get_modification_date_time();
// interrogate file for photo information
PhotoFileInterrogator interrogator = new PhotoFileInterrogator(file);
@@ -2297,7 +2316,7 @@ public abstract class Photo : PhotoSource, Dateable {
bool success;
lock (row) {
success = PhotoTable.get_instance().master_exif_updated(get_photo_id(), info.get_size(),
- timestamp.tv_sec, detected.md5, detected.exif_md5, detected.thumbnail_md5, row);
+ timestamp, detected.md5, detected.exif_md5, detected.thumbnail_md5, row);
}
if (success)
@@ -2324,7 +2343,7 @@ public abstract class Photo : PhotoSource, Dateable {
}
}
- public override time_t get_exposure_time() {
+ public override DateTime? get_exposure_time() {
return cached_exposure_time;
}
@@ -2362,6 +2381,29 @@ public abstract class Photo : PhotoSource, Dateable {
if (committed)
notify_altered(new Alteration("metadata", "name"));
}
+
+ public GpsCoords get_gps_coords() {
+ lock (row) {
+ return row.gps_coords;
+ }
+ }
+
+ public void set_gps_coords(GpsCoords gps_coords) {
+ DatabaseError dberr = null;
+ lock (row) {
+ try {
+ PhotoTable.get_instance().set_gps_coords(row.photo_id, gps_coords);
+ row.gps_coords = gps_coords;
+ } catch (DatabaseError err) {
+ dberr = err;
+ }
+ }
+ if (dberr == null)
+ notify_altered(new Alteration("metadata", "gps"));
+ else
+ warning("Unable to write gps coordinates for %s: %s", to_string(), dberr.message);
+ }
+
public override bool set_comment(string? comment) {
string? new_comment = prep_comment(comment);
@@ -2455,7 +2497,7 @@ public abstract class Photo : PhotoSource, Dateable {
file_exif_updated();
}
- public void set_exposure_time(time_t time) {
+ public void set_exposure_time(DateTime time) {
bool committed;
lock (row) {
committed = PhotoTable.get_instance().set_exposure_time(row.photo_id, time);
@@ -2469,7 +2511,7 @@ public abstract class Photo : PhotoSource, Dateable {
notify_altered(new Alteration("metadata", "exposure-time"));
}
- public void set_exposure_time_persistent(time_t time) throws Error {
+ public void set_exposure_time_persistent(DateTime time) throws Error {
PhotoFileReader source = get_source_reader();
// Try to write to backing file
@@ -2742,7 +2784,8 @@ public abstract class Photo : PhotoSource, Dateable {
lock (row) {
return row.transformations == null
&& (row.orientation != backing_photo_row.original_orientation
- || (date_time != null && row.exposure_time != date_time.get_timestamp()));
+ || (date_time != null && row.exposure_time != null &&
+ !row.exposure_time.equal(date_time.get_timestamp())));
}
}
@@ -2763,7 +2806,7 @@ public abstract class Photo : PhotoSource, Dateable {
// No, use file timestamp as date/time.
lock (row) {
// Did we manually set an exposure date?
- if(backing_photo_row.timestamp != row.exposure_time) {
+ if(nullsafe_date_time_comperator(backing_photo_row.timestamp, row.exposure_time) != 0) {
// Yes, we need to save this.
return true;
}
@@ -2773,7 +2816,7 @@ public abstract class Photo : PhotoSource, Dateable {
lock (row) {
return row.transformations != null
|| row.orientation != backing_photo_row.original_orientation
- || (date_time != null && row.exposure_time != date_time.get_timestamp())
+ || (date_time != null && !row.exposure_time.equal(date_time.get_timestamp()))
|| (get_comment() != comment)
|| (get_title() != title);
}
@@ -3212,6 +3255,7 @@ public abstract class Photo : PhotoSource, Dateable {
double orientation_time = 0.0;
total_timer.start();
+
#endif
// get required fields all at once, to avoid holding the row lock
@@ -3618,7 +3662,7 @@ public abstract class Photo : PhotoSource, Dateable {
debug("Updating metadata of %s", writer.get_filepath());
- if (get_exposure_time() != 0)
+ if (get_exposure_time() != null)
metadata.set_exposure_date_time(new MetadataDateTime(get_exposure_time()));
else
metadata.set_exposure_date_time(null);
@@ -3714,7 +3758,7 @@ public abstract class Photo : PhotoSource, Dateable {
metadata.set_comment(get_comment());
metadata.set_software(Resources.APP_TITLE, Resources.APP_VERSION);
- if (get_exposure_time() != 0)
+ if (get_exposure_time() != null)
metadata.set_exposure_date_time(new MetadataDateTime(get_exposure_time()));
else
metadata.set_exposure_date_time(null);
@@ -3970,15 +4014,15 @@ public abstract class Photo : PhotoSource, Dateable {
return;
}
- TimeVal timestamp = info.get_modification_time();
+ var timestamp = info.get_modification_date_time();
- BackingPhotoTable.get_instance().update_attributes(editable_id, timestamp.tv_sec,
+ BackingPhotoTable.get_instance().update_attributes(editable_id, timestamp,
info.get_size());
lock (row) {
- timestamp_changed = editable.timestamp != timestamp.tv_sec;
+ timestamp_changed = !editable.timestamp.equal(timestamp);
filesize_changed = editable.filesize != info.get_size();
- editable.timestamp = timestamp.tv_sec;
+ editable.timestamp = timestamp;
editable.filesize = info.get_size();
}
} else {
@@ -4057,7 +4101,7 @@ public abstract class Photo : PhotoSource, Dateable {
PhotoTable.get_instance().detach_editable(row);
backing_photo_row = row.master;
}
- } catch (DatabaseError err) {
+ } catch (Error err) {
warning("Unable to remove editable from PhotoTable: %s", err.message);
}
@@ -4976,7 +5020,12 @@ public class LibraryPhoto : Photo, Flaggable, Monitorable {
this.import_keywords = null;
thumbnail_scheduler = new OneShotScheduler("LibraryPhoto", generate_thumbnails);
-
+ // import gps coords of photos imported with prior versions of shotwell
+ if (row.gps_coords.has_gps == -1) {
+ var gps_import_scheduler = new OneShotScheduler("LibraryPhoto", import_gps_metadata);
+ gps_import_scheduler.at_priority_idle(Priority.LOW);
+ }
+
// if marked in a state where they're held in an orphanage, rehydrate their backlinks
if ((row.flags & (FLAG_TRASH | FLAG_OFFLINE)) != 0)
rehydrate_backlinks(global, row.backlinks);
@@ -5097,7 +5146,12 @@ public class LibraryPhoto : Photo, Flaggable, Monitorable {
// fire signal that thumbnails have changed
notify_thumbnail_altered();
}
-
+
+ private void import_gps_metadata() {
+ GpsCoords gps_coords = get_metadata().get_gps_coords();
+ set_gps_coords(gps_coords);
+ }
+
// These keywords are only used during import and should not be relied upon elsewhere.
public Gee.Collection<string>? get_import_keywords() {
return import_keywords;
@@ -5218,7 +5272,7 @@ public class LibraryPhoto : Photo, Flaggable, Monitorable {
if (location != null) {
face.attach(dupe);
FaceLocation.create(face.get_face_id(), dupe.get_photo_id(),
- location.get_serialized_geometry());
+ location.get_face_data());
}
}
}
@@ -5332,10 +5386,14 @@ public class LibraryPhoto : Photo, Flaggable, Monitorable {
PhotoMetadata? metadata = get_metadata();
if (metadata == null)
- return tags != null || tags.size > 0 || get_rating() != Rating.UNRATED;
+ return tags != null || tags.size > 0 || get_rating() != Rating.UNRATED || get_gps_coords().has_gps != 0;
if (get_rating() != metadata.get_rating())
return true;
+
+ var old_coords = metadata.get_gps_coords();
+ if (!get_gps_coords().equals(ref old_coords))
+ return true;
Gee.Set<string>? keywords = metadata.get_keywords();
int tags_count = (tags != null) ? tags.size : 0;
@@ -5366,6 +5424,7 @@ public class LibraryPhoto : Photo, Flaggable, Monitorable {
metadata.set_keywords(null);
metadata.set_rating(get_rating());
+ metadata.set_gps_coords(get_gps_coords());
}
protected override void apply_user_metadata_for_reimport(PhotoMetadata metadata) {