1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /*
4 * GThumb
5 *
6 * Copyright (C) 2010 Free Software Foundation, Inc.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include <config.h>
23 #include "gth-change-date-task.h"
24
25
26 struct _GthChangeDateTaskPrivate {
27 GFile *location;
28 GList *files; /* GFile */
29 GList *file_list; /* GthFileData */
30 GthChangeFields fields;
31 GthChangeType change_type;
32 GthDateTime *date_time;
33 int time_offset;
34 int n_files;
35 int n_current;
36 GList *current;
37 };
38
39
G_DEFINE_TYPE_WITH_CODE(GthChangeDateTask,gth_change_date_task,GTH_TYPE_TASK,G_ADD_PRIVATE (GthChangeDateTask))40 G_DEFINE_TYPE_WITH_CODE (GthChangeDateTask,
41 gth_change_date_task,
42 GTH_TYPE_TASK,
43 G_ADD_PRIVATE (GthChangeDateTask))
44
45
46 static void
47 gth_change_date_task_finalize (GObject *object)
48 {
49 GthChangeDateTask *self;
50
51 self = GTH_CHANGE_DATE_TASK (object);
52
53 _g_object_unref (self->priv->location);
54 _g_object_list_unref (self->priv->file_list);
55 _g_object_list_unref (self->priv->files);
56 gth_datetime_free (self->priv->date_time);
57
58 G_OBJECT_CLASS (gth_change_date_task_parent_class)->finalize (object);
59 }
60
61
62 static void
set_date_time_from_change_type(GthChangeDateTask * self,GthDateTime * date_time,GthChangeType change_type,GthFileData * file_data)63 set_date_time_from_change_type (GthChangeDateTask *self,
64 GthDateTime *date_time,
65 GthChangeType change_type,
66 GthFileData *file_data)
67 {
68 if (change_type == GTH_CHANGE_TO_FOLLOWING_DATE) {
69 gth_datetime_copy (self->priv->date_time, date_time);
70 }
71 else if (change_type == GTH_CHANGE_TO_FILE_MODIFIED_DATE) {
72 gth_datetime_from_timeval (date_time, gth_file_data_get_modification_time (file_data));
73 }
74 else if (change_type == GTH_CHANGE_TO_FILE_CREATION_DATE) {
75 gth_datetime_from_timeval (date_time, gth_file_data_get_creation_time (file_data));
76 }
77 else if (change_type == GTH_CHANGE_TO_PHOTO_ORIGINAL_DATE) {
78 GTimeVal time_val;
79
80 if (gth_file_data_get_digitalization_time (file_data, &time_val))
81 gth_datetime_from_timeval (date_time, &time_val);
82 }
83 }
84
85
86 static void
update_modification_time(GthChangeDateTask * self)87 update_modification_time (GthChangeDateTask *self)
88 {
89
90 GList *scan;
91 GError *error = NULL;
92 GthMonitor *monitor;
93 GList *files;
94
95 if ((self->priv->fields & GTH_CHANGE_LAST_MODIFIED_DATE) == GTH_CHANGE_LAST_MODIFIED_DATE) {
96 GthDateTime *date_time;
97
98 date_time = gth_datetime_new ();
99 for (scan = self->priv->file_list; scan; scan = scan->next) {
100 GthFileData *file_data = scan->data;
101 GTimeVal timeval;
102
103 gth_datetime_clear (date_time);
104 if (self->priv->change_type == GTH_CHANGE_ADJUST_TIME)
105 set_date_time_from_change_type (self, date_time, GTH_CHANGE_TO_FILE_MODIFIED_DATE, file_data);
106 else
107 set_date_time_from_change_type (self, date_time, self->priv->change_type, file_data);
108
109 /* Read the time from the attribute if it's not valid in
110 * date_time. */
111
112 if (! gth_time_valid (date_time->time)) {
113 GTimeVal *original_modification_time;
114 GthDateTime *original_date_time;
115
116 original_modification_time = gth_file_data_get_modification_time (file_data);
117 original_date_time = gth_datetime_new ();
118 gth_datetime_from_timeval (original_date_time, original_modification_time);
119 *date_time->time = *original_date_time->time;
120
121 gth_datetime_free (original_date_time);
122 }
123
124 if (! gth_datetime_valid (date_time))
125 continue;
126
127 if (gth_datetime_to_timeval (date_time, &timeval)) {
128 if (self->priv->change_type == GTH_CHANGE_ADJUST_TIME)
129 timeval.tv_sec += self->priv->time_offset;
130 if (! _g_file_set_modification_time (file_data->file,
131 &timeval,
132 gth_task_get_cancellable (GTH_TASK (self)),
133 &error))
134 {
135 break;
136 }
137 }
138 }
139
140 gth_datetime_free (date_time);
141 }
142
143 monitor = gth_main_get_default_monitor ();
144 files = gth_file_data_list_to_file_list (self->priv->file_list);
145 gth_monitor_folder_changed (monitor,
146 self->priv->location,
147 files,
148 GTH_MONITOR_EVENT_CHANGED);
149
150 gth_task_completed (GTH_TASK (self), error);
151
152 _g_object_list_unref (files);
153 }
154
155
156 static void
write_metadata_ready_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)157 write_metadata_ready_cb (GObject *source_object,
158 GAsyncResult *result,
159 gpointer user_data)
160 {
161 GthChangeDateTask *self = user_data;
162 GError *error = NULL;
163
164 if (! _g_write_metadata_finish (result, &error)) {
165 gth_task_completed (GTH_TASK (self), error);
166 return;
167 }
168
169 if (g_cancellable_set_error_if_cancelled (gth_task_get_cancellable (GTH_TASK (self)), &error)) {
170 gth_task_completed (GTH_TASK (self), error);
171 return;
172 }
173
174 update_modification_time (self);
175 }
176
177
178 static void
set_date_metadata(GthFileData * file_data,const char * attribute,GthDateTime * date_time,int time_offset)179 set_date_metadata (GthFileData *file_data,
180 const char *attribute,
181 GthDateTime *date_time,
182 int time_offset)
183 {
184 GthDateTime *new_date_time;
185
186 new_date_time = gth_datetime_new ();
187 gth_datetime_copy (date_time, new_date_time);
188
189 if (time_offset != 0) {
190 GTimeVal timeval;
191
192 gth_datetime_to_timeval (new_date_time, &timeval);
193 timeval.tv_sec += time_offset;
194 gth_datetime_from_timeval (new_date_time, &timeval);
195 }
196 else {
197 /* Read the time from the attribute if it's not valid in
198 * date_time. */
199
200 if (! gth_time_valid (new_date_time->time)) {
201 GthMetadata *original_date;
202
203 original_date = (GthMetadata *) g_file_info_get_attribute_object (file_data->info, attribute);
204 if (original_date != NULL) {
205 GthDateTime *original_date_time;
206
207 original_date_time = gth_datetime_new ();
208 gth_datetime_from_exif_date (original_date_time, gth_metadata_get_raw (original_date));
209 *new_date_time->time = *original_date_time->time;
210
211 gth_datetime_free (original_date_time);
212 }
213 }
214 }
215
216 if (gth_datetime_valid (new_date_time)) {
217 char *raw;
218 char *formatted;
219 GObject *metadata;
220
221 raw = gth_datetime_to_exif_date (new_date_time);
222 formatted = gth_datetime_strftime (new_date_time, "%x");
223 metadata = (GObject *) gth_metadata_new ();
224 g_object_set (metadata,
225 "id", attribute,
226 "raw", raw,
227 "formatted", formatted,
228 NULL);
229 g_file_info_set_attribute_object (file_data->info, attribute, metadata);
230
231 g_object_unref (metadata);
232 g_free (formatted);
233 g_free (raw);
234 }
235
236 gth_datetime_free (new_date_time);
237 }
238
239
240 static void
set_date_time_from_field(GthChangeDateTask * self,GthDateTime * date_time,GthChangeFields field,GthFileData * file_data)241 set_date_time_from_field (GthChangeDateTask *self,
242 GthDateTime *date_time,
243 GthChangeFields field,
244 GthFileData *file_data)
245 {
246 if (field & GTH_CHANGE_LAST_MODIFIED_DATE) {
247 gth_datetime_from_timeval (date_time, gth_file_data_get_modification_time (file_data));
248 }
249 else if (field & GTH_CHANGE_COMMENT_DATE) {
250 GthMetadata *m;
251 GTimeVal time_val;
252
253 m = (GthMetadata *) g_file_info_get_attribute_object (file_data->info, "general::datetime");
254 if ((m != NULL) && _g_time_val_from_exif_date (gth_metadata_get_raw (m), &time_val))
255 gth_datetime_from_timeval (date_time, &time_val);
256 }
257 }
258
259
260 static void
info_ready_cb(GList * files,GError * error,gpointer user_data)261 info_ready_cb (GList *files,
262 GError *error,
263 gpointer user_data)
264 {
265 GthChangeDateTask *self = user_data;
266 GthDateTime *date_time;
267 GList *scan;
268 GPtrArray *attribute_v;
269
270 if (error != NULL) {
271 gth_task_completed (GTH_TASK (self), error);
272 return;
273 }
274
275 if (g_cancellable_set_error_if_cancelled (gth_task_get_cancellable (GTH_TASK (self)), &error)) {
276 gth_task_completed (GTH_TASK (self), error);
277 return;
278 }
279
280 date_time = gth_datetime_new ();
281 self->priv->file_list = _g_object_list_ref (files);
282 for (scan = self->priv->file_list; scan; scan = scan->next) {
283 GthFileData *file_data = scan->data;
284
285 if (self->priv->change_type == GTH_CHANGE_ADJUST_TIME) {
286 if (self->priv->fields & GTH_CHANGE_COMMENT_DATE) {
287 gth_datetime_clear (date_time);
288 set_date_time_from_field (self, date_time, GTH_CHANGE_COMMENT_DATE, file_data);
289 if (gth_datetime_valid (date_time))
290 set_date_metadata (file_data, "general::datetime", date_time, self->priv->time_offset);
291 }
292 }
293 else {
294 gth_datetime_clear (date_time);
295 set_date_time_from_change_type (self, date_time, self->priv->change_type, file_data);
296 if (g_date_valid (date_time->date)) {
297 if (self->priv->fields & GTH_CHANGE_COMMENT_DATE) {
298 set_date_metadata (file_data, "general::datetime", date_time, 0);
299 }
300 }
301 }
302 }
303
304 attribute_v = g_ptr_array_new ();
305 if (self->priv->fields & GTH_CHANGE_COMMENT_DATE)
306 g_ptr_array_add (attribute_v, "general::datetime");
307 if (attribute_v->len > 0) {
308 char *attributes;
309
310 attributes = _g_string_array_join (attribute_v, ",");
311 _g_write_metadata_async (self->priv->file_list,
312 GTH_METADATA_WRITE_DEFAULT,
313 attributes,
314 gth_task_get_cancellable (GTH_TASK (self)),
315 write_metadata_ready_cb,
316 self);
317
318 g_free (attributes);
319 }
320 else
321 update_modification_time (self);
322
323 g_ptr_array_unref (attribute_v);
324 gth_datetime_free (date_time);
325 }
326
327
328 static void
gth_change_date_task_exec(GthTask * task)329 gth_change_date_task_exec (GthTask *task)
330 {
331 GthChangeDateTask *self = GTH_CHANGE_DATE_TASK (task);
332
333 _g_query_all_metadata_async (self->priv->files,
334 GTH_LIST_DEFAULT,
335 "*",
336 gth_task_get_cancellable (task),
337 info_ready_cb,
338 self);
339 }
340
341
342 static void
gth_change_date_task_class_init(GthChangeDateTaskClass * klass)343 gth_change_date_task_class_init (GthChangeDateTaskClass *klass)
344 {
345 GObjectClass *object_class;
346 GthTaskClass *task_class;
347
348 object_class = G_OBJECT_CLASS (klass);
349 object_class->finalize = gth_change_date_task_finalize;
350
351 task_class = GTH_TASK_CLASS (klass);
352 task_class->exec = gth_change_date_task_exec;
353 }
354
355
356 static void
gth_change_date_task_init(GthChangeDateTask * self)357 gth_change_date_task_init (GthChangeDateTask *self)
358 {
359 self->priv = gth_change_date_task_get_instance_private (self);
360 self->priv->date_time = gth_datetime_new ();
361 }
362
363
364 GthTask *
gth_change_date_task_new(GFile * location,GList * files,GthChangeFields fields,GthChangeType change_type,GthDateTime * date_time,int time_offset)365 gth_change_date_task_new (GFile *location,
366 GList *files, /* GthFileData */
367 GthChangeFields fields,
368 GthChangeType change_type,
369 GthDateTime *date_time,
370 int time_offset)
371 {
372 GthChangeDateTask *self;
373
374 self = GTH_CHANGE_DATE_TASK (g_object_new (GTH_TYPE_CHANGE_DATE_TASK, NULL));
375 self->priv->location = g_file_dup (location);
376 self->priv->files = gth_file_data_list_to_file_list (files);
377 self->priv->fields = fields;
378 self->priv->change_type = change_type;
379 if (date_time != NULL)
380 gth_datetime_copy (date_time, self->priv->date_time);
381 self->priv->time_offset = time_offset;
382
383 return (GthTask *) self;
384 }
385