1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /*
4 * GThumb
5 *
6 * Copyright (C) 2009 The 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 <string.h>
24 #include <stdlib.h>
25 #include "glib-utils.h"
26 #include "gth-duplicable.h"
27 #include "gth-metadata.h"
28 #include "gth-file-data.h"
29 #include "gth-string-list.h"
30
31
32 const char *FileDataDigitalizationTags[] = {
33 "Exif::Photo::DateTimeOriginal",
34 "Xmp::exif::DateTimeOriginal",
35 "Exif::Photo::DateTimeDigitized",
36 "Xmp::exif::DateTimeDigitized",
37 "Xmp::xmp::CreateDate",
38 "Xmp::photoshop::DateCreated",
39 "Xmp::xmp::ModifyDate",
40 "Xmp::xmp::MetadataDate",
41 NULL
42 };
43
44
45 struct _GthFileDataPrivate {
46 GTimeVal ctime; /* creation time */
47 GTimeVal mtime; /* modification time */
48 GTimeVal dtime; /* digitalization time */
49 char *sort_key;
50 };
51
52
53 static void gth_file_data_gth_duplicable_interface_init (GthDuplicableInterface *iface);
54
55
G_DEFINE_TYPE_WITH_CODE(GthFileData,gth_file_data,G_TYPE_OBJECT,G_ADD_PRIVATE (GthFileData)G_IMPLEMENT_INTERFACE (GTH_TYPE_DUPLICABLE,gth_file_data_gth_duplicable_interface_init))56 G_DEFINE_TYPE_WITH_CODE (GthFileData,
57 gth_file_data,
58 G_TYPE_OBJECT,
59 G_ADD_PRIVATE (GthFileData)
60 G_IMPLEMENT_INTERFACE (GTH_TYPE_DUPLICABLE,
61 gth_file_data_gth_duplicable_interface_init))
62
63
64 static void
65 gth_file_data_finalize (GObject *obj)
66 {
67 GthFileData *self = GTH_FILE_DATA (obj);
68
69 _g_object_unref (self->file);
70 _g_object_unref (self->info);
71 g_free (self->priv->sort_key);
72
73 G_OBJECT_CLASS (gth_file_data_parent_class)->finalize (obj);
74 }
75
76
77 static void
gth_file_data_class_init(GthFileDataClass * klass)78 gth_file_data_class_init (GthFileDataClass *klass)
79 {
80 G_OBJECT_CLASS (klass)->finalize = gth_file_data_finalize;
81 }
82
83
84 static GObject *
gth_file_data_real_duplicate(GthDuplicable * base)85 gth_file_data_real_duplicate (GthDuplicable *base)
86 {
87 return (GObject *) gth_file_data_dup ((GthFileData*) base);
88 }
89
90
91 static void
gth_file_data_gth_duplicable_interface_init(GthDuplicableInterface * iface)92 gth_file_data_gth_duplicable_interface_init (GthDuplicableInterface *iface)
93 {
94 iface->duplicate = gth_file_data_real_duplicate;
95 }
96
97
98 static void
gth_file_data_init(GthFileData * self)99 gth_file_data_init (GthFileData *self)
100 {
101 self->priv = gth_file_data_get_instance_private (self);
102 self->priv->dtime.tv_sec = 0;
103 self->priv->sort_key = NULL;
104 self->file = NULL;
105 self->info = NULL;
106 }
107
108
109 GthFileData *
gth_file_data_new(GFile * file,GFileInfo * info)110 gth_file_data_new (GFile *file,
111 GFileInfo *info)
112 {
113 GthFileData *self;
114
115 self = g_object_new (GTH_TYPE_FILE_DATA, NULL);
116 gth_file_data_set_file (self, file);
117 gth_file_data_set_info (self, info);
118
119 return self;
120 }
121
122
123 GthFileData *
gth_file_data_new_for_uri(const char * uri,const char * mime_type)124 gth_file_data_new_for_uri (const char *uri,
125 const char *mime_type)
126 {
127 GFile *file;
128 GthFileData *file_data;
129
130 file = g_file_new_for_uri (uri);
131 file_data = gth_file_data_new (file, NULL);
132 gth_file_data_set_mime_type (file_data, mime_type);
133
134 g_object_unref (file);
135
136 return file_data;
137 }
138
139
140 GthFileData *
gth_file_data_dup(GthFileData * self)141 gth_file_data_dup (GthFileData *self)
142 {
143 GthFileData *file;
144
145 if (self == NULL)
146 return NULL;
147
148 file = g_object_new (GTH_TYPE_FILE_DATA, NULL);
149 file->file = g_file_dup (self->file);
150 file->info = g_file_info_dup (self->info);
151
152 return file;
153 }
154
155
156 void
gth_file_data_set_file(GthFileData * self,GFile * file)157 gth_file_data_set_file (GthFileData *self,
158 GFile *file)
159 {
160 if (file != NULL)
161 g_object_ref (file);
162
163 if (self->file != NULL) {
164 g_object_unref (self->file);
165 self->file = NULL;
166 }
167
168 if (file != NULL)
169 self->file = file;
170 }
171
172
173 void
gth_file_data_set_info(GthFileData * self,GFileInfo * info)174 gth_file_data_set_info (GthFileData *self,
175 GFileInfo *info)
176 {
177 if (info != NULL)
178 g_object_ref (info);
179
180 if (self->info != NULL)
181 g_object_unref (self->info);
182
183 if (info != NULL)
184 self->info = info;
185 else
186 self->info = g_file_info_new ();
187
188 self->priv->dtime.tv_sec = 0;
189 }
190
191
192 void
gth_file_data_set_mime_type(GthFileData * self,const char * mime_type)193 gth_file_data_set_mime_type (GthFileData *self,
194 const char *mime_type)
195 {
196 if (mime_type != NULL) {
197 g_file_info_set_content_type (self->info, _g_str_get_static (mime_type));
198 g_file_info_set_attribute_string (self->info, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, _g_str_get_static (mime_type));
199 g_file_info_set_attribute_string (self->info, G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE, _g_str_get_static (mime_type));
200 }
201 }
202
203
204 const char *
gth_file_data_get_mime_type(GthFileData * self)205 gth_file_data_get_mime_type (GthFileData *self)
206 {
207 const char *content_type;
208
209 if (self->info == NULL)
210 return NULL;
211
212 content_type = g_file_info_get_attribute_string (self->info, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
213 if (content_type == NULL)
214 content_type = g_file_info_get_attribute_string (self->info, G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE);
215
216 if (content_type == NULL) {
217 char *filename;
218
219 if (self->file == NULL)
220 return NULL;
221
222 filename = g_file_get_basename (self->file);
223 if (filename == NULL)
224 return NULL;
225
226 content_type = _g_content_type_guess_from_name (filename);
227 g_file_info_set_attribute_string (self->info, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, content_type);
228
229 g_free (filename);
230 }
231
232 return _g_str_get_static (content_type);
233 }
234
235
236 const char *
gth_file_data_get_mime_type_from_content(GthFileData * self,GCancellable * cancellable)237 gth_file_data_get_mime_type_from_content (GthFileData *self,
238 GCancellable *cancellable)
239 {
240 const char *content_type;
241
242 if (self->info == NULL)
243 return NULL;
244
245 content_type = g_file_info_get_attribute_string (self->info, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
246 if (content_type == NULL) {
247 GInputStream *istream;
248 GError *error = NULL;
249
250 if (self->file == NULL)
251 return NULL;
252
253 istream = (GInputStream *) g_file_read (self->file, cancellable, &error);
254 if (istream == NULL) {
255 g_warning ("%s", error->message);
256 g_clear_error (&error);
257 return NULL;
258 }
259
260 content_type = _g_content_type_get_from_stream (istream, self->file, cancellable, &error);
261 g_file_info_set_attribute_string (self->info, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, content_type);
262
263 g_object_unref (istream);
264 }
265
266 return _g_str_get_static (content_type);
267 }
268
269
270 const char *
gth_file_data_get_filename_sort_key(GthFileData * self)271 gth_file_data_get_filename_sort_key (GthFileData *self)
272 {
273 if (self->info == NULL)
274 return NULL;
275
276 if (self->priv->sort_key == NULL)
277 self->priv->sort_key = g_utf8_collate_key_for_filename (g_file_info_get_display_name (self->info), -1);
278
279 return self->priv->sort_key;
280 }
281
282
283 time_t
gth_file_data_get_mtime(GthFileData * self)284 gth_file_data_get_mtime (GthFileData *self)
285 {
286 g_file_info_get_modification_time (self->info, &self->priv->mtime);
287 return (time_t) self->priv->mtime.tv_sec;
288 }
289
290
291 GTimeVal *
gth_file_data_get_modification_time(GthFileData * self)292 gth_file_data_get_modification_time (GthFileData *self)
293 {
294 g_file_info_get_modification_time (self->info, &self->priv->mtime);
295 return &self->priv->mtime;
296 }
297
298
299 GTimeVal *
gth_file_data_get_creation_time(GthFileData * self)300 gth_file_data_get_creation_time (GthFileData *self)
301 {
302 self->priv->ctime.tv_sec = g_file_info_get_attribute_uint64 (self->info, G_FILE_ATTRIBUTE_TIME_CREATED);
303 self->priv->ctime.tv_usec = g_file_info_get_attribute_uint32 (self->info, G_FILE_ATTRIBUTE_TIME_CREATED_USEC);
304 return &self->priv->ctime;
305 }
306
307
308 gboolean
gth_file_data_get_digitalization_time(GthFileData * self,GTimeVal * _time)309 gth_file_data_get_digitalization_time (GthFileData *self,
310 GTimeVal *_time)
311 {
312 int i;
313
314 if (self->priv->dtime.tv_sec != 0) {
315 *_time = self->priv->dtime;
316 return TRUE;
317 }
318
319 for (i = 0; FileDataDigitalizationTags[i] != NULL; i++) {
320 GthMetadata *m;
321
322 m = (GthMetadata *) g_file_info_get_attribute_object (self->info, FileDataDigitalizationTags[i]);
323 if (m == NULL)
324 continue;
325
326 if (! _g_time_val_from_exif_date (gth_metadata_get_raw (m), &self->priv->dtime))
327 continue;
328
329 *_time = self->priv->dtime;
330 return TRUE;
331 }
332
333 return FALSE;
334 }
335
336
337 gboolean
gth_file_data_is_readable(GthFileData * self)338 gth_file_data_is_readable (GthFileData *self)
339 {
340 return g_file_info_get_attribute_boolean (self->info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ);
341 }
342
343
344 void
gth_file_data_update_info(GthFileData * fd,const char * attributes)345 gth_file_data_update_info (GthFileData *fd,
346 const char *attributes)
347 {
348 if (attributes == NULL)
349 attributes = GFILE_STANDARD_ATTRIBUTES;
350 if (fd->info != NULL)
351 g_object_unref (fd->info);
352
353 fd->info = g_file_query_info (fd->file, attributes, G_FILE_QUERY_INFO_NONE, NULL, NULL);
354
355 if (fd->info == NULL)
356 fd->info = g_file_info_new ();
357
358 fd->priv->dtime.tv_sec = 0;
359 }
360
361
362 void
gth_file_data_update_mime_type(GthFileData * fd,gboolean fast)363 gth_file_data_update_mime_type (GthFileData *fd,
364 gboolean fast)
365 {
366 gth_file_data_set_mime_type (fd, _g_file_query_mime_type (fd->file, fast || ! g_file_is_native (fd->file)));
367 }
368
369
370 void
gth_file_data_update_all(GthFileData * fd,gboolean fast)371 gth_file_data_update_all (GthFileData *fd,
372 gboolean fast)
373 {
374 gth_file_data_update_info (fd, NULL);
375 gth_file_data_update_mime_type (fd, fast);
376 }
377
378
379 GList*
gth_file_data_list_dup(GList * list)380 gth_file_data_list_dup (GList *list)
381 {
382 GList *result = NULL;
383 GList *scan;
384
385 for (scan = list; scan; scan = scan->next) {
386 GthFileData *file_data = scan->data;
387
388 result = g_list_prepend (result, gth_file_data_dup (file_data));
389 }
390
391 return g_list_reverse (result);
392 }
393
394
395 GList*
gth_file_data_list_from_uri_list(GList * list)396 gth_file_data_list_from_uri_list (GList *list)
397 {
398 GList *result = NULL;
399 GList *scan;
400
401 for (scan = list; scan; scan = scan->next) {
402 char *uri = scan->data;
403 GFile *file;
404
405 file = g_file_new_for_uri (uri);
406 result = g_list_prepend (result, gth_file_data_new (file, NULL));
407 g_object_unref (file);
408 }
409
410 return g_list_reverse (result);
411 }
412
413
414 GList*
gth_file_data_list_to_uri_list(GList * list)415 gth_file_data_list_to_uri_list (GList *list)
416 {
417 GList *result = NULL;
418 GList *scan;
419
420 for (scan = list; scan; scan = scan->next) {
421 GthFileData *file = scan->data;
422 result = g_list_prepend (result, g_file_get_uri (file->file));
423 }
424
425 return g_list_reverse (result);
426 }
427
428
429 GList *
gth_file_data_list_to_file_list(GList * list)430 gth_file_data_list_to_file_list (GList *list)
431 {
432 GList *result = NULL;
433 GList *scan;
434
435 for (scan = list; scan; scan = scan->next) {
436 GthFileData *file = scan->data;
437 result = g_list_prepend (result, g_file_dup (file->file));
438 }
439
440 return g_list_reverse (result);
441 }
442
443
444 GList *
gth_file_data_list_find_file(GList * list,GFile * file)445 gth_file_data_list_find_file (GList *list,
446 GFile *file)
447 {
448 GList *scan;
449
450 for (scan = list; scan; scan = scan->next) {
451 GthFileData *file_data = scan->data;
452
453 if (g_file_equal (file_data->file, file))
454 return scan;
455 }
456
457 return NULL;
458 }
459
460
461 GList *
gth_file_data_list_find_uri(GList * list,const char * uri)462 gth_file_data_list_find_uri (GList *list,
463 const char *uri)
464 {
465 GList *scan;
466
467 for (scan = list; scan; scan = scan->next) {
468 GthFileData *file = scan->data;
469 char *file_uri;
470
471 file_uri = g_file_get_uri (file->file);
472 if (strcmp (file_uri, uri) == 0) {
473 g_free (file_uri);
474 return scan;
475 }
476 g_free (file_uri);
477 }
478
479 return NULL;
480 }
481
482
483 typedef struct {
484 GthFileData *file_data;
485 GthFileDataFunc ready_func;
486 gpointer user_data;
487 GError *error;
488 guint id;
489 } ReadyData;
490
491
492 static gboolean
exec_ready_func(gpointer user_data)493 exec_ready_func (gpointer user_data)
494 {
495 ReadyData *data = user_data;
496
497 g_source_remove (data->id);
498 data->ready_func (data->file_data, data->error, data->user_data);
499
500 _g_object_unref (data->file_data);
501 g_free (data);
502
503 return FALSE;
504 }
505
506
507 void
gth_file_data_ready_with_error(GthFileData * file_data,GthFileDataFunc ready_func,gpointer user_data,GError * error)508 gth_file_data_ready_with_error (GthFileData *file_data,
509 GthFileDataFunc ready_func,
510 gpointer user_data,
511 GError *error)
512 {
513 ReadyData *data;
514
515 data = g_new0 (ReadyData, 1);
516 if (file_data != NULL)
517 data->file_data = g_object_ref (file_data);
518 data->ready_func = ready_func;
519 data->user_data = user_data;
520 data->error = error;
521 data->id = g_idle_add (exec_ready_func, data);
522 }
523
524
525 char *
gth_file_data_get_attribute_as_string(GthFileData * file_data,const char * id)526 gth_file_data_get_attribute_as_string (GthFileData *file_data,
527 const char *id)
528 {
529 GObject *obj;
530 char *value = NULL;
531
532 switch (g_file_info_get_attribute_type (file_data->info, id)) {
533 case G_FILE_ATTRIBUTE_TYPE_OBJECT:
534 obj = g_file_info_get_attribute_object (file_data->info, id);
535 if (GTH_IS_METADATA (obj)) {
536 switch (gth_metadata_get_data_type (GTH_METADATA (obj))) {
537 case GTH_METADATA_TYPE_STRING:
538 if (strcmp (id, "general::rating") == 0) {
539 int n = atoi (gth_metadata_get_formatted (GTH_METADATA (obj)));
540 if ((n >= 0) && (n <= 5)) {
541 GString *str = g_string_new ("");
542 int i;
543 for (i = 1; i <= n; i++)
544 g_string_append (str, "⭐");
545 value = g_string_free (str, FALSE);
546 }
547 }
548 if (value == NULL)
549 value = g_strdup (gth_metadata_get_formatted (GTH_METADATA (obj)));
550 break;
551 case GTH_METADATA_TYPE_STRING_LIST:
552 value = gth_string_list_join (GTH_STRING_LIST (gth_metadata_get_string_list (GTH_METADATA (obj))), " ");
553 break;
554 }
555 }
556 else if (GTH_IS_STRING_LIST (obj))
557 value = gth_string_list_join (GTH_STRING_LIST (obj), " ");
558 else
559 value = g_file_info_get_attribute_as_string (file_data->info, id);
560 break;
561 default:
562 value = g_file_info_get_attribute_as_string (file_data->info, id);
563 break;
564 }
565
566 return value;
567 }
568
569
570 GFileInfo *
gth_file_data_list_get_common_info(GList * file_data_list,const char * attribtues)571 gth_file_data_list_get_common_info (GList *file_data_list,
572 const char *attribtues)
573 {
574 GFileInfo *info;
575 char **attributes_v;
576 int i;
577
578 info = g_file_info_new ();
579
580 if (file_data_list == NULL)
581 return info;
582
583 g_file_info_copy_into (((GthFileData *) file_data_list->data)->info, info);
584
585 attributes_v = g_strsplit (attribtues, ",", -1);
586 for (i = 0; attributes_v[i] != NULL; i++) {
587 char *attribute = attributes_v[i];
588 char *first_value;
589 GList *scan;
590
591 first_value = gth_file_data_get_attribute_as_string ((GthFileData *) file_data_list->data, attribute);
592 for (scan = file_data_list->next; (first_value != NULL) && scan; scan = scan->next) {
593 GthFileData *file_data = scan->data;
594 char *value;
595
596 value = gth_file_data_get_attribute_as_string (file_data, attribute);
597 if (g_strcmp0 (first_value, value) != 0) {
598 g_free (first_value);
599 first_value = NULL;
600 }
601
602 g_free (value);
603 }
604
605 if (first_value == NULL)
606 g_file_info_remove_attribute (info, attribute);
607
608 g_free (first_value);
609 }
610
611 g_strfreev (attributes_v);
612
613 return info;
614 }
615
616
617 gboolean
gth_file_data_attribute_equal(GthFileData * file_data,const char * attribute,const char * value)618 gth_file_data_attribute_equal (GthFileData *file_data,
619 const char *attribute,
620 const char *value)
621 {
622 char *v;
623 gboolean result;
624
625 /* a NULL pointer is equal to an empty string. */
626
627 if (g_strcmp0 (value, "") == 0)
628 value = NULL;
629
630 v = gth_file_data_get_attribute_as_string (file_data, attribute);
631 if (g_strcmp0 (v, "") == 0) {
632 g_free (v);
633 v = NULL;
634 }
635
636 result = g_strcmp0 (v, value) == 0;
637
638 g_free (v);
639
640 return result;
641 }
642
643
644 gboolean
gth_file_data_attribute_equal_int(GthFileData * file_data,const char * attribute,const char * value)645 gth_file_data_attribute_equal_int (GthFileData *file_data,
646 const char *attribute,
647 const char *value)
648 {
649 char *v;
650 gboolean result;
651
652 /* a NULL pointer or an empty string is equal to 0. */
653
654 if ((g_strcmp0 (value, "") == 0) || (value == NULL))
655 value = "0";
656
657 v = gth_file_data_get_attribute_as_string (file_data, attribute);
658 if ((g_strcmp0 (v, "") == 0) || (v == NULL)) {
659 g_free (v);
660 v = g_strdup ("0");
661 }
662
663 result = g_strcmp0 (v, value) == 0;
664
665 g_free (v);
666
667 return result;
668 }
669
670
671 gboolean
gth_file_data_attribute_equal_string_list(GthFileData * file_data,const char * attribute,GthStringList * value)672 gth_file_data_attribute_equal_string_list (GthFileData *file_data,
673 const char *attribute,
674 GthStringList *value)
675 {
676 GthStringList *list;
677 GObject *obj;
678
679 list = NULL;
680 obj = g_file_info_get_attribute_object (file_data->info, attribute);
681 if (GTH_IS_METADATA (obj))
682 list = gth_metadata_get_string_list (GTH_METADATA (obj));
683 else if (GTH_IS_STRING_LIST (obj))
684 list = (GthStringList *) obj;
685
686 return gth_string_list_equal (list, value);
687 }
688