1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /*
4 * GThumb
5 *
6 * Copyright (C) 2009 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
23 #include <config.h>
24 #include <glib/gi18n.h>
25 #include <glib.h>
26 #include <gthumb.h>
27 #include "gth-catalog.h"
28
29
30 #define CATALOG_FORMAT "1.0"
31
32
33 struct _GthCatalogPrivate {
34 GthCatalogType type;
35 GFile *file;
36 GList *file_list;
37 GHashTable *file_hash; /* used to avoid duplicates */
38 char *name;
39 GthDateTime *date_time;
40 gboolean active;
41 char *order;
42 gboolean order_inverse;
43 };
44
45
G_DEFINE_TYPE_WITH_CODE(GthCatalog,gth_catalog,G_TYPE_OBJECT,G_ADD_PRIVATE (GthCatalog))46 G_DEFINE_TYPE_WITH_CODE (GthCatalog,
47 gth_catalog,
48 G_TYPE_OBJECT,
49 G_ADD_PRIVATE (GthCatalog))
50
51
52 static void
53 gth_catalog_finalize (GObject *object)
54 {
55 GthCatalog *catalog = GTH_CATALOG (object);
56
57 g_value_hash_unref (catalog->attributes);
58
59 if (catalog->priv->file != NULL)
60 g_object_unref (catalog->priv->file);
61 g_free (catalog->priv->name);
62 _g_object_list_unref (catalog->priv->file_list);
63 g_hash_table_destroy (catalog->priv->file_hash);
64 gth_datetime_free (catalog->priv->date_time);
65 g_free (catalog->priv->order);
66
67 G_OBJECT_CLASS (gth_catalog_parent_class)->finalize (object);
68 }
69
70
71 static DomElement *
base_create_root(GthCatalog * catalog,DomDocument * doc)72 base_create_root (GthCatalog *catalog,
73 DomDocument *doc)
74 {
75 return dom_document_create_element (doc, "catalog",
76 "version", CATALOG_FORMAT,
77 NULL);
78 }
79
80
81 static void
base_read_from_doc(GthCatalog * catalog,DomElement * root)82 base_read_from_doc (GthCatalog *catalog,
83 DomElement *root)
84 {
85 GList *file_list;
86 DomElement *child;
87
88 file_list = NULL;
89 for (child = root->first_child; child; child = child->next_sibling) {
90 if (g_strcmp0 (child->tag_name, "files") == 0) {
91 DomElement *file;
92
93 for (file = child->first_child; file; file = file->next_sibling) {
94 const char *uri;
95
96 uri = dom_element_get_attribute (file, "uri");
97 if (uri != NULL)
98 file_list = g_list_prepend (file_list, g_file_new_for_uri (uri));
99 }
100 file_list = g_list_reverse (file_list);
101 }
102 if (g_strcmp0 (child->tag_name, "order") == 0)
103 gth_catalog_set_order (catalog,
104 dom_element_get_attribute (child, "type"),
105 g_strcmp0 (dom_element_get_attribute (child, "inverse"), "1") == 0);
106 if (g_strcmp0 (child->tag_name, "date") == 0)
107 gth_datetime_from_exif_date (catalog->priv->date_time, dom_element_get_inner_text (child));
108 if (g_strcmp0 (child->tag_name, "name") == 0)
109 gth_catalog_set_name (catalog, dom_element_get_inner_text (child));
110 }
111 gth_catalog_set_file_list (catalog, file_list);
112
113 gth_hook_invoke ("gth-catalog-read-from-doc", catalog, root);
114
115 _g_object_list_unref (file_list);
116 }
117
118
119 static void
read_catalog_data_from_xml(GthCatalog * catalog,const char * buffer,gsize count,GError ** error)120 read_catalog_data_from_xml (GthCatalog *catalog,
121 const char *buffer,
122 gsize count,
123 GError **error)
124 {
125 DomDocument *doc;
126
127 doc = dom_document_new ();
128 if (dom_document_load (doc, buffer, count, error))
129 GTH_CATALOG_GET_CLASS (catalog)->read_from_doc (catalog, DOM_ELEMENT (doc)->first_child);
130
131 g_object_unref (doc);
132 }
133
134
135 static void
read_catalog_data_old_format(GthCatalog * catalog,const char * buffer,gsize count)136 read_catalog_data_old_format (GthCatalog *catalog,
137 const char *buffer,
138 gsize count)
139 {
140 GInputStream *mem_stream;
141 GDataInputStream *data_stream;
142 gboolean is_search;
143 int list_start;
144 int n_line;
145 char *line;
146
147 mem_stream = g_memory_input_stream_new_from_data (buffer, count, NULL);
148 data_stream = g_data_input_stream_new (mem_stream);
149
150 is_search = (strncmp (buffer, "# Search", 8) == 0);
151 if (is_search)
152 list_start = 10;
153 else
154 list_start = 1;
155
156 gth_catalog_set_file_list (catalog, NULL);
157
158 n_line = 0;
159 while ((line = g_data_input_stream_read_line (data_stream, NULL, NULL, NULL)) != NULL) {
160 n_line++;
161
162 if (is_search) {
163 /* FIXME: read the search metadata here. */
164 }
165
166 if (n_line > list_start) {
167 char *uri;
168
169 uri = g_strndup (line + 1, strlen (line) - 2);
170 catalog->priv->file_list = g_list_prepend (catalog->priv->file_list, g_file_new_for_uri (uri));
171
172 g_free (uri);
173 }
174 g_free (line);
175 }
176
177 catalog->priv->file_list = g_list_reverse (catalog->priv->file_list);
178
179 g_object_unref (data_stream);
180 g_object_unref (mem_stream);
181 }
182
183
184 static void
base_write_to_doc(GthCatalog * catalog,DomDocument * doc,DomElement * root)185 base_write_to_doc (GthCatalog *catalog,
186 DomDocument *doc,
187 DomElement *root)
188 {
189 if (catalog->priv->name != NULL)
190 dom_element_append_child (root, dom_document_create_element_with_text (doc, catalog->priv->name, "name", NULL));
191
192 if (gth_datetime_valid_date (catalog->priv->date_time)) {
193 char *s;
194
195 s = gth_datetime_to_exif_date (catalog->priv->date_time);
196 dom_element_append_child (root, dom_document_create_element_with_text (doc, s, "date", NULL));
197 g_free (s);
198 }
199
200 if (catalog->priv->order != NULL)
201 dom_element_append_child (root, dom_document_create_element (doc, "order",
202 "type", catalog->priv->order,
203 "inverse", (catalog->priv->order_inverse ? "1" : "0"),
204 NULL));
205
206 if (catalog->priv->file_list != NULL) {
207 DomElement *node;
208 GList *scan;
209
210 node = dom_document_create_element (doc, "files", NULL);
211 dom_element_append_child (root, node);
212
213 for (scan = catalog->priv->file_list; scan; scan = scan->next) {
214 GFile *file = scan->data;
215 char *uri;
216
217 uri = g_file_get_uri (file);
218 dom_element_append_child (node, dom_document_create_element (doc, "file", "uri", uri, NULL));
219
220 g_free (uri);
221 }
222 }
223
224 gth_hook_invoke ("gth-catalog-write-to-doc", catalog, doc, root);
225 }
226
227
228 static void
gth_catalog_class_init(GthCatalogClass * class)229 gth_catalog_class_init (GthCatalogClass *class)
230 {
231 GObjectClass *object_class;
232
233 object_class = (GObjectClass*) class;
234 object_class->finalize = gth_catalog_finalize;
235
236 class->create_root = base_create_root;
237 class->read_from_doc = base_read_from_doc;
238 class->write_to_doc = base_write_to_doc;
239 }
240
241
242 static void
gth_catalog_init(GthCatalog * catalog)243 gth_catalog_init (GthCatalog *catalog)
244 {
245 catalog->attributes = g_value_hash_new ();
246 catalog->priv = gth_catalog_get_instance_private (catalog);
247 catalog->priv->type = GTH_CATALOG_TYPE_INVALID;
248 catalog->priv->file = NULL;
249 catalog->priv->file_list = NULL;
250 catalog->priv->file_hash = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, NULL, NULL);
251 catalog->priv->name = NULL;
252 catalog->priv->date_time = gth_datetime_new ();
253 catalog->priv->order = NULL;
254 catalog->priv->order_inverse = FALSE;
255 }
256
257
258 GthCatalog *
gth_catalog_new(void)259 gth_catalog_new (void)
260 {
261 return (GthCatalog *) g_object_new (GTH_TYPE_CATALOG, NULL);
262 }
263
264
265 GthCatalog *
gth_catalog_new_for_file(GFile * file)266 gth_catalog_new_for_file (GFile *file)
267 {
268 char *uri;
269 GthCatalog *catalog;
270
271 if (file == NULL)
272 return NULL;
273
274 uri = g_file_get_uri (file);
275 catalog = gth_hook_invoke_get ("gth-catalog-new-for-uri", uri);
276
277 g_free (uri);
278
279 return catalog;
280 }
281
282
283 GthCatalog *
gth_catalog_new_from_data(const void * buffer,gsize count,GError ** error)284 gth_catalog_new_from_data (const void *buffer,
285 gsize count,
286 GError **error)
287 {
288 char *text_buffer;
289 GthCatalog *catalog = NULL;
290
291 text_buffer = (char *) buffer;
292 if ((text_buffer == NULL) || (*text_buffer == 0))
293 return NULL;
294
295 if (strncmp (text_buffer, "<?xml ", 6) == 0) {
296 catalog = gth_hook_invoke_get ("gth-catalog-load-from-data", (gpointer) buffer);
297 if (catalog != NULL)
298 read_catalog_data_from_xml (catalog, text_buffer, count, error);
299 else
300 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Invalid file format"));
301 }
302 else {
303 catalog = gth_catalog_new ();
304 read_catalog_data_old_format (catalog, text_buffer, count);
305 }
306
307 return catalog;
308 }
309
310
311 void
gth_catalog_set_file(GthCatalog * catalog,GFile * file)312 gth_catalog_set_file (GthCatalog *catalog,
313 GFile *file)
314 {
315 if (catalog->priv->file != NULL) {
316 g_object_unref (catalog->priv->file);
317 catalog->priv->file = NULL;
318 }
319
320 if (file != NULL)
321 catalog->priv->file = g_file_dup (file);
322
323 catalog->priv->type = GTH_CATALOG_TYPE_CATALOG;
324 }
325
326
327 GFile *
gth_catalog_get_file(GthCatalog * catalog)328 gth_catalog_get_file (GthCatalog *catalog)
329 {
330 return catalog->priv->file;
331 }
332
333
334 void
gth_catalog_set_name(GthCatalog * catalog,const char * name)335 gth_catalog_set_name (GthCatalog *catalog,
336 const char *name)
337 {
338 g_free (catalog->priv->name);
339 catalog->priv->name = NULL;
340 if ((name != NULL) && (strcmp (name, "") != 0))
341 catalog->priv->name = g_strdup (name);;
342 }
343
344
345 const char *
gth_catalog_get_name(GthCatalog * catalog)346 gth_catalog_get_name (GthCatalog *catalog)
347 {
348 return catalog->priv->name;
349 }
350
351
352 void
gth_catalog_set_date(GthCatalog * catalog,GthDateTime * date_time)353 gth_catalog_set_date (GthCatalog *catalog,
354 GthDateTime *date_time)
355 {
356 if (g_date_valid (date_time->date))
357 g_date_set_dmy (catalog->priv->date_time->date,
358 g_date_get_day (date_time->date),
359 g_date_get_month (date_time->date),
360 g_date_get_year (date_time->date));
361 else
362 g_date_clear (catalog->priv->date_time->date, 1);
363 gth_time_set_hms (catalog->priv->date_time->time, 0, 0, 0, 0);
364 }
365
366
367 GthDateTime *
gth_catalog_get_date(GthCatalog * catalog)368 gth_catalog_get_date (GthCatalog *catalog)
369 {
370 return catalog->priv->date_time;
371 }
372
373
374 void
gth_catalog_set_order(GthCatalog * catalog,const char * order,gboolean inverse)375 gth_catalog_set_order (GthCatalog *catalog,
376 const char *order,
377 gboolean inverse)
378 {
379 g_free (catalog->priv->order);
380 catalog->priv->order = NULL;
381
382 if (order != NULL)
383 catalog->priv->order = g_strdup (order);
384 catalog->priv->order_inverse = inverse;
385 }
386
387
388 const char *
gth_catalog_get_order(GthCatalog * catalog,gboolean * inverse)389 gth_catalog_get_order (GthCatalog *catalog,
390 gboolean *inverse)
391 {
392 *inverse = catalog->priv->order_inverse;
393 return catalog->priv->order;
394 }
395
396
397 char *
gth_catalog_to_data(GthCatalog * catalog,gsize * length)398 gth_catalog_to_data (GthCatalog *catalog,
399 gsize *length)
400 {
401 DomDocument *doc;
402 DomElement *root;
403 char *data;
404
405 doc = dom_document_new ();
406 root = GTH_CATALOG_GET_CLASS (catalog)->create_root (catalog, doc);
407 dom_element_append_child (DOM_ELEMENT (doc), root);
408 GTH_CATALOG_GET_CLASS (catalog)->write_to_doc (catalog, doc, root);
409 data = dom_document_dump (doc, length);
410
411 g_object_unref (doc);
412
413 return data;
414 }
415
416
417 void
gth_catalog_set_file_list(GthCatalog * catalog,GList * file_list)418 gth_catalog_set_file_list (GthCatalog *catalog,
419 GList *file_list)
420 {
421 _g_object_list_unref (catalog->priv->file_list);
422 catalog->priv->file_list = NULL;
423 g_hash_table_remove_all (catalog->priv->file_hash);
424
425 if (file_list != NULL) {
426 GList *list;
427 GList *scan;
428
429 list = NULL;
430 for (scan = file_list; scan; scan = scan->next) {
431 GFile *file = scan->data;
432
433 if (g_hash_table_lookup (catalog->priv->file_hash, file) != NULL)
434 continue;
435 file = g_file_dup (file);
436 list = g_list_prepend (list, file);
437 g_hash_table_insert (catalog->priv->file_hash, file, GINT_TO_POINTER (1));
438 }
439 catalog->priv->file_list = g_list_reverse (list);
440 }
441 }
442
443
444 GList *
gth_catalog_get_file_list(GthCatalog * catalog)445 gth_catalog_get_file_list (GthCatalog *catalog)
446 {
447 return catalog->priv->file_list;
448 }
449
450
451 gboolean
gth_catalog_insert_file(GthCatalog * catalog,GFile * file,int pos)452 gth_catalog_insert_file (GthCatalog *catalog,
453 GFile *file,
454 int pos)
455 {
456 if (g_hash_table_lookup (catalog->priv->file_hash, file) != NULL)
457 return FALSE;
458
459 file = g_file_dup (file);
460 catalog->priv->file_list = g_list_insert (catalog->priv->file_list, file, pos);
461 g_hash_table_insert (catalog->priv->file_hash, file, GINT_TO_POINTER (1));
462
463 return TRUE;
464 }
465
466
467 int
gth_catalog_remove_file(GthCatalog * catalog,GFile * file)468 gth_catalog_remove_file (GthCatalog *catalog,
469 GFile *file)
470 {
471 GList *scan;
472 int i = 0;
473
474 g_return_val_if_fail (catalog != NULL, -1);
475 g_return_val_if_fail (file != NULL, -1);
476
477 for (scan = catalog->priv->file_list; scan; scan = scan->next, i++)
478 if (g_file_equal ((GFile *) scan->data, file))
479 break;
480
481 if (scan == NULL)
482 return -1;
483
484 catalog->priv->file_list = g_list_remove_link (catalog->priv->file_list, scan);
485 g_hash_table_remove (catalog->priv->file_hash, file);
486
487 _g_object_list_unref (scan);
488
489 return i;
490 }
491
492
493 static char *
get_display_name(GFile * file,const char * name,GthDateTime * date_time)494 get_display_name (GFile *file,
495 const char *name,
496 GthDateTime *date_time)
497 {
498 GString *display_name;
499 char *basename;
500
501 display_name = g_string_new ("");
502 basename = g_file_get_basename (file);
503 if ((basename == NULL) || (strcmp (basename, "/") == 0)) {
504 g_string_append (display_name, _("Catalogs"));
505 }
506 else {
507 if ((name == NULL) && ! gth_datetime_valid_date (date_time)) {
508 char *name;
509 char *utf8_name;
510
511 name = _g_path_remove_extension (basename);
512 utf8_name = g_filename_to_utf8 (name, -1, NULL, NULL, NULL);
513 g_string_append (display_name, utf8_name);
514
515 g_free (utf8_name);
516 g_free (name);
517 }
518 else {
519 if (name != NULL)
520 g_string_append (display_name, name);
521
522 if (gth_datetime_valid_date (date_time)) {
523 char *formatted;
524
525 formatted = gth_datetime_strftime (date_time, "%x");
526 if ((name == NULL) || (strstr (name, formatted) == NULL)) {
527 if (name != NULL)
528 g_string_append (display_name, " (");
529 g_string_append (display_name, formatted);
530 if (name != NULL)
531 g_string_append (display_name, ")");
532 }
533 g_free (formatted);
534 }
535 }
536 }
537
538 g_free (basename);
539
540 return g_string_free (display_name, FALSE);
541 }
542
543
544 static char *
get_edit_name(GFile * file,const char * name,GthDateTime * date_time)545 get_edit_name (GFile *file,
546 const char *name,
547 GthDateTime *date_time)
548 {
549 GString *display_name;
550 char *basename;
551
552 display_name = g_string_new ("");
553 basename = g_file_get_basename (file);
554 if ((basename == NULL) || (strcmp (basename, "/") == 0)) {
555 g_string_append (display_name, _("Catalogs"));
556 }
557 else {
558 if (name == NULL) {
559 char *name;
560 char *utf8_name;
561
562 name = _g_path_remove_extension (basename);
563 utf8_name = g_filename_to_utf8 (name, -1, NULL, NULL, NULL);
564 g_string_append (display_name, utf8_name);
565
566 g_free (utf8_name);
567 g_free (name);
568 }
569 else
570 g_string_append (display_name, name);
571 }
572
573 g_free (basename);
574
575 return g_string_free (display_name, FALSE);
576 }
577
578
579 static void
update_standard_attributes(GFile * file,GFileInfo * info,const char * name,GthDateTime * date_time)580 update_standard_attributes (GFile *file,
581 GFileInfo *info,
582 const char *name,
583 GthDateTime *date_time)
584 {
585 char *display_name;
586 char *edit_name;
587
588 if (gth_datetime_valid_date (date_time)) {
589 char *sort_order_s;
590
591 sort_order_s = gth_datetime_strftime (date_time, "%Y%m%d");
592 _g_file_info_set_secondary_sort_order (info, atoi (sort_order_s));
593
594 g_free (sort_order_s);
595 }
596 else
597 g_file_info_remove_attribute (info, "gth::standard::secondary-sort-order");
598
599 display_name = get_display_name (file, name, date_time);
600 if (display_name != NULL) {
601 g_file_info_set_display_name (info, display_name);
602 g_free (display_name);
603 }
604
605 edit_name = get_edit_name (file, name, date_time);
606 if (edit_name != NULL) {
607 g_file_info_set_edit_name (info, edit_name);
608 g_free (edit_name);
609 }
610 }
611
612
613 void
gth_catalog_update_metadata(GthCatalog * catalog,GthFileData * file_data)614 gth_catalog_update_metadata (GthCatalog *catalog,
615 GthFileData *file_data)
616 {
617 const char *sort_type;
618 gboolean sort_inverse;
619
620 /* sort::type,sort::inverse */
621
622 sort_type = gth_catalog_get_order (catalog, &sort_inverse);
623 if (sort_type != NULL) {
624 g_file_info_set_attribute_string (file_data->info, "sort::type", sort_type);
625 g_file_info_set_attribute_boolean (file_data->info, "sort::inverse", sort_inverse);
626 }
627 else {
628 g_file_info_remove_attribute (file_data->info, "sort::type");
629 g_file_info_remove_attribute (file_data->info, "sort::inverse");
630 }
631
632 /* general::event-date */
633
634 if (gth_datetime_valid_date (catalog->priv->date_time)) {
635 GObject *metadata;
636 char *raw;
637 char *formatted;
638 char *sort_order_s;
639
640 metadata = (GObject *) gth_metadata_new ();
641 raw = gth_datetime_to_exif_date (catalog->priv->date_time);
642 formatted = gth_datetime_strftime (catalog->priv->date_time, "%x");
643 g_object_set (metadata,
644 "id", "general::event-date",
645 "raw", raw,
646 "formatted", formatted,
647 NULL);
648 g_file_info_set_attribute_object (file_data->info, "general::event-date", metadata);
649
650 sort_order_s = gth_datetime_strftime (catalog->priv->date_time, "%Y%m%d");
651 _g_file_info_set_secondary_sort_order (file_data->info, atoi (sort_order_s));
652
653 g_free (formatted);
654 g_free (raw);
655 g_object_unref (metadata);
656 }
657 else {
658 g_file_info_remove_attribute (file_data->info, "general::event-date");
659 g_file_info_remove_attribute (file_data->info, "gth::standard::secondary-sort-order");
660 }
661
662 /* standard::display-name,standard::sort-order */
663
664 update_standard_attributes (file_data->file,
665 file_data->info,
666 catalog->priv->name,
667 catalog->priv->date_time);
668
669 gth_hook_invoke ("gth-catalog-write-metadata", catalog, file_data);
670 }
671
672
673 int
gth_catalog_get_size(GthCatalog * catalog)674 gth_catalog_get_size (GthCatalog *catalog)
675 {
676 return g_hash_table_size (catalog->priv->file_hash);
677 }
678
679
680 /* utils */
681
682
683 GFile *
gth_catalog_get_base(void)684 gth_catalog_get_base (void)
685 {
686 return gth_user_dir_get_file_for_read (GTH_DIR_DATA, GTHUMB_DIR, "catalogs", NULL);
687 }
688
689
690 GFile *
gth_catalog_file_to_gio_file(GFile * file)691 gth_catalog_file_to_gio_file (GFile *file)
692 {
693 GFile *gio_file = NULL;
694 char *uri;
695 UriParts file_parts;
696
697 if (! g_file_has_uri_scheme (file, "catalog"))
698 return g_file_dup (file);
699
700 uri = g_file_get_uri (file);
701 if (! _g_uri_split (uri, &file_parts))
702 return NULL;
703
704 if (file_parts.query != NULL) {
705 char *new_uri;
706
707 new_uri = g_uri_unescape_string (file_parts.query, NULL);
708 gio_file = g_file_new_for_uri (new_uri);
709
710 g_free (new_uri);
711 }
712 else {
713 GFile *base;
714 char *base_uri;
715 char *new_uri;
716
717 base = gth_catalog_get_base ();
718 base_uri = g_file_get_uri (base);
719 new_uri = _g_uri_append_path (base_uri, file_parts.path);
720 gio_file = g_file_new_for_uri (new_uri);
721
722 g_free (new_uri);
723 g_free (base_uri);
724 g_object_unref (base);
725 }
726
727 g_free (uri);
728
729 return gio_file;
730 }
731
732
733 GFile *
gth_catalog_file_from_gio_file(GFile * gio_file,GFile * catalog)734 gth_catalog_file_from_gio_file (GFile *gio_file,
735 GFile *catalog)
736 {
737 GFile *gio_base;
738 GFile *file = NULL;
739 char *path;
740
741 gio_base = gth_catalog_get_base ();
742 if (g_file_equal (gio_base, gio_file)) {
743 g_object_unref (gio_base);
744 return g_file_new_for_uri ("catalog:///");
745 }
746
747 path = g_file_get_relative_path (gio_base, gio_file);
748 if (path != NULL) {
749 GFile *base;
750
751 base = g_file_new_for_uri ("catalog:///");
752 file = _g_file_append_path (base, path);
753
754 g_object_unref (base);
755 }
756 else if (catalog != NULL) {
757 char *catalog_uri;
758 char *file_uri;
759 char *query;
760 char *uri;
761
762 catalog_uri = g_file_get_uri (catalog);
763 file_uri = g_file_get_uri (gio_file);
764 query = g_uri_escape_string (file_uri, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, FALSE);
765 uri = g_strconcat (file_uri, "?", query, NULL);
766 file = g_file_new_for_uri (uri);
767
768 g_free (uri);
769 g_free (query);
770 g_free (file_uri);
771 g_free (catalog_uri);
772 }
773
774 g_free (path);
775 g_object_unref (gio_base);
776
777 return file;
778 }
779
780
781 GFile *
gth_catalog_file_from_relative_path(const char * name,const char * file_extension)782 gth_catalog_file_from_relative_path (const char *name,
783 const char *file_extension)
784 {
785 char *path;
786 char *uri;
787 GFile *file;
788
789 path = g_strconcat (name, file_extension, NULL);
790 uri = _g_uri_append_path ("catalog:///", path);
791 file = g_file_new_for_uri (uri);
792
793 g_free (uri);
794 g_free (path);
795
796 return file;
797 }
798
799
800 char *
gth_catalog_get_relative_path(GFile * file)801 gth_catalog_get_relative_path (GFile *file)
802 {
803 GFile *base;
804 char *path;
805
806 base = gth_catalog_get_base ();
807 path = g_file_get_relative_path (base, file);
808
809 g_object_unref (base);
810
811 return path;
812 }
813
814
815 GIcon *
gth_catalog_get_icon(GFile * file)816 gth_catalog_get_icon (GFile *file)
817 {
818 char *uri;
819 GIcon *icon;
820
821 uri = g_file_get_uri (file);
822 if (g_str_has_suffix (uri, ".catalog"))
823 icon = g_themed_icon_new ("file-catalog-symbolic");
824 else
825 icon = g_themed_icon_new ("file-library-symbolic");
826
827 g_free (uri);
828
829 return icon;
830 }
831
832
833 static char *
get_tag_value(const char * buffer,const char * tag_start,const char * tag_end)834 get_tag_value (const char *buffer,
835 const char *tag_start,
836 const char *tag_end)
837 {
838 char *value;
839 char *begin_tag;
840
841 value = NULL;
842 begin_tag = strstr (buffer, tag_start);
843 if (begin_tag != NULL) {
844 char *end_tag;
845 char *xml;
846 DomDocument *doc;
847
848 end_tag = strstr (begin_tag, tag_end);
849 xml = g_strndup (begin_tag, (end_tag - begin_tag) + strlen (tag_end));
850 doc = dom_document_new ();
851 if (dom_document_load (doc, xml, strlen (xml), NULL))
852 value = g_strdup (dom_element_get_inner_text (DOM_ELEMENT (doc)->first_child));
853
854 g_object_unref (doc);
855 g_free (xml);
856 }
857
858 return value;
859 }
860
861
862 void
gth_catalog_update_standard_attributes(GFile * file,GFileInfo * info)863 gth_catalog_update_standard_attributes (GFile *file,
864 GFileInfo *info)
865 {
866 char *display_name = NULL;
867 char *edit_name = NULL;
868 char *basename;
869
870 basename = g_file_get_basename (file);
871 if ((basename != NULL) && (strcmp (basename, "/") != 0)) {
872 char *name;
873 GthDateTime *date_time;
874
875 name = NULL;
876 date_time = gth_datetime_new ();
877 {
878 GFile *gio_file;
879 GFileInputStream *istream;
880 const int buffer_size = 256;
881 char buffer[buffer_size];
882
883 gio_file = gth_catalog_file_to_gio_file (file);
884 istream = g_file_read (gio_file, NULL, NULL);
885 if (istream != NULL) {
886 gsize bytes_read;
887
888 if (g_input_stream_read_all (G_INPUT_STREAM (istream),
889 buffer,
890 buffer_size - 1,
891 &bytes_read,
892 NULL,
893 NULL))
894 {
895 char *exif_date;
896
897 buffer[bytes_read] = '\0';
898 name = get_tag_value (buffer, "<name>", "</name>");
899 exif_date = get_tag_value (buffer, "<date>", "</date>");
900 if (exif_date != NULL)
901 gth_datetime_from_exif_date (date_time, exif_date);
902
903 g_free (exif_date);
904 }
905 g_object_unref (istream);
906 }
907 g_object_unref (gio_file);
908 }
909
910 update_standard_attributes (file, info, name, date_time);
911
912 gth_datetime_free (date_time);
913 g_free (name);
914 }
915 else {
916 display_name = g_strdup (_("Catalogs"));
917 edit_name = g_strdup (_("Catalogs"));
918 }
919
920 if (display_name != NULL)
921 g_file_info_set_display_name (info, display_name);
922 if (edit_name != NULL)
923 g_file_info_set_edit_name (info, edit_name);
924
925 g_free (edit_name);
926 g_free (display_name);
927 g_free (basename);
928 }
929
930
931 /* -- gth_catalog_load_from_file --*/
932
933
934 typedef struct {
935 GFile *file;
936 ReadyCallback ready_func;
937 gpointer user_data;
938 } LoadData;
939
940
941 static void
load__catalog_buffer_ready_cb(void ** buffer,gsize count,GError * error,gpointer user_data)942 load__catalog_buffer_ready_cb (void **buffer,
943 gsize count,
944 GError *error,
945 gpointer user_data)
946 {
947 LoadData *load_data = user_data;
948 GthCatalog *catalog;
949
950 if (error == NULL) {
951 catalog = gth_catalog_new_from_data (*buffer, count, &error);
952 if (catalog == NULL)
953 catalog = gth_catalog_new_for_file (load_data->file);
954 }
955 else
956 catalog = NULL;
957 load_data->ready_func (G_OBJECT (catalog), error, load_data->user_data);
958
959 g_object_unref (load_data->file);
960 g_free (load_data);
961 }
962
963
964 void
gth_catalog_load_from_file_async(GFile * file,GCancellable * cancellable,ReadyCallback ready_func,gpointer user_data)965 gth_catalog_load_from_file_async (GFile *file,
966 GCancellable *cancellable,
967 ReadyCallback ready_func,
968 gpointer user_data)
969 {
970 LoadData *load_data;
971 GFile *gio_file;
972
973 load_data = g_new0 (LoadData, 1);
974 load_data->file = g_object_ref (file);
975 load_data->ready_func = ready_func;
976 load_data->user_data = user_data;
977
978 gio_file = gth_catalog_file_to_gio_file (file);
979 _g_file_load_async (gio_file,
980 G_PRIORITY_DEFAULT,
981 cancellable,
982 load__catalog_buffer_ready_cb,
983 load_data);
984
985 g_object_unref (gio_file);
986 }
987
988
989 GFile *
gth_catalog_get_file_for_date(GthDateTime * date_time,const char * extension)990 gth_catalog_get_file_for_date (GthDateTime *date_time,
991 const char *extension)
992 {
993 char *year;
994 char *uri;
995 char *display_name;
996 GFile *catalog_file;
997
998 year = gth_datetime_strftime (date_time, "%Y");
999 uri = g_strconcat ("catalog:///", year, "/", NULL);
1000 display_name = gth_datetime_strftime (date_time, "%Y-%m-%d");
1001 catalog_file = _g_file_new_for_display_name (uri, display_name, extension);
1002
1003 g_free (display_name);
1004 g_free (uri);
1005 g_free (year);
1006
1007 return catalog_file;
1008 }
1009
1010
1011 GFile *
gth_catalog_get_file_for_tag(const char * tag,const char * extension)1012 gth_catalog_get_file_for_tag (const char *tag,
1013 const char *extension)
1014 {
1015 char *uri;
1016 GFile *catalog_file;
1017
1018 uri = g_strconcat ("catalog:///", _("Tags"), "/", NULL);
1019 catalog_file = _g_file_new_for_display_name (uri, tag, extension);
1020
1021 g_free (uri);
1022
1023 return catalog_file;
1024 }
1025
1026
1027 GthCatalog *
gth_catalog_load_from_file(GFile * file)1028 gth_catalog_load_from_file (GFile *file)
1029 {
1030 GthCatalog *catalog;
1031 GFile *gio_file;
1032 void *buffer;
1033 gsize buffer_size;
1034
1035 gio_file = gth_catalog_file_to_gio_file (file);
1036 if (! _g_file_load_in_buffer (gio_file, &buffer, &buffer_size, NULL, NULL))
1037 return NULL;
1038
1039 catalog = gth_catalog_new_from_data (buffer, buffer_size, NULL);
1040
1041 g_free (buffer);
1042 g_object_unref (gio_file);
1043
1044 return catalog;
1045 }
1046
1047
1048 void
gth_catalog_save(GthCatalog * catalog)1049 gth_catalog_save (GthCatalog *catalog)
1050 {
1051 GFile *file;
1052 GFile *gio_file;
1053 GFile *gio_parent;
1054 char *data;
1055 gsize size;
1056 GError *error = NULL;
1057
1058 file = gth_catalog_get_file (catalog);
1059 gio_file = gth_catalog_file_to_gio_file (file);
1060
1061 gio_parent = g_file_get_parent (gio_file);
1062 if (gio_parent != NULL)
1063 g_file_make_directory_with_parents (gio_parent, NULL, NULL);
1064 data = gth_catalog_to_data (catalog, &size);
1065 if (! _g_file_write (gio_file,
1066 FALSE,
1067 G_FILE_CREATE_NONE,
1068 data,
1069 size,
1070 NULL,
1071 &error))
1072 {
1073 g_warning ("%s", error->message);
1074 g_clear_error (&error);
1075 }
1076 else {
1077 GFile *parent_parent;
1078 GFile *parent;
1079 GList *list;
1080
1081 parent = g_file_get_parent (file);
1082 parent_parent = g_file_get_parent (parent);
1083 if (parent_parent != NULL) {
1084 list = g_list_append (NULL, parent);
1085 gth_monitor_folder_changed (gth_main_get_default_monitor (),
1086 parent_parent,
1087 list,
1088 GTH_MONITOR_EVENT_CREATED);
1089 g_list_free (list);
1090 }
1091
1092 list = g_list_append (NULL, file);
1093 gth_monitor_folder_changed (gth_main_get_default_monitor (),
1094 parent,
1095 list,
1096 GTH_MONITOR_EVENT_CREATED);
1097
1098 g_list_free (list);
1099 g_object_unref (parent);
1100 }
1101
1102 g_free (data);
1103 _g_object_unref (gio_parent);
1104 g_object_unref (gio_file);
1105 }
1106
1107
1108 /* -- gth_catalog_list_async -- */
1109
1110
1111 typedef struct {
1112 GthCatalog *catalog;
1113 const char *attributes;
1114 CatalogReadyCallback list_ready_func;
1115 gpointer user_data;
1116 GList *current_file;
1117 GList *files;
1118 GCancellable *cancellable;
1119 } ListData;
1120
1121
1122 static void
gth_catalog_list_done(ListData * list_data,GError * error)1123 gth_catalog_list_done (ListData *list_data,
1124 GError *error)
1125 {
1126 if (list_data->list_ready_func != NULL) {
1127 list_data->files = g_list_reverse (list_data->files);
1128 list_data->list_ready_func (list_data->catalog, list_data->files, error, list_data->user_data);
1129 }
1130
1131 _g_object_list_unref (list_data->files);
1132 _g_object_unref (list_data->cancellable);
1133 _g_object_unref (list_data->catalog);
1134 g_free (list_data);
1135 }
1136
1137
1138 static void
catalog_file_info_ready_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)1139 catalog_file_info_ready_cb (GObject *source_object,
1140 GAsyncResult *result,
1141 gpointer user_data)
1142 {
1143 ListData *list_data = user_data;
1144 GFile *file;
1145 GFileInfo *info;
1146
1147 file = (GFile*) source_object;
1148 info = g_file_query_info_finish (file, result, NULL);
1149 if (info != NULL) {
1150 list_data->files = g_list_prepend (list_data->files, gth_file_data_new (file, info));
1151 g_object_unref (info);
1152 }
1153
1154 list_data->current_file = list_data->current_file->next;
1155 if (list_data->current_file == NULL) {
1156 gth_catalog_list_done (list_data, NULL);
1157 return;
1158 }
1159
1160 g_file_query_info_async ((GFile *) list_data->current_file->data,
1161 list_data->attributes,
1162 0,
1163 G_PRIORITY_DEFAULT,
1164 list_data->cancellable,
1165 catalog_file_info_ready_cb,
1166 list_data);
1167 }
1168
1169
1170 static void
list__catalog_buffer_ready_cb(void ** buffer,gsize count,GError * error,gpointer user_data)1171 list__catalog_buffer_ready_cb (void **buffer,
1172 gsize count,
1173 GError *error,
1174 gpointer user_data)
1175 {
1176 ListData *list_data = user_data;
1177
1178 if ((error == NULL) && (*buffer != NULL)) {
1179 list_data->catalog = gth_catalog_new_from_data (*buffer, count, &error);
1180 if (list_data->catalog == NULL) {
1181 gth_catalog_list_done (list_data, error);
1182 return;
1183 }
1184
1185 list_data->current_file = list_data->catalog->priv->file_list;
1186 if (list_data->current_file == NULL) {
1187 gth_catalog_list_done (list_data, NULL);
1188 return;
1189 }
1190
1191 g_file_query_info_async ((GFile *) list_data->current_file->data,
1192 list_data->attributes,
1193 0,
1194 G_PRIORITY_DEFAULT,
1195 list_data->cancellable,
1196 catalog_file_info_ready_cb,
1197 list_data);
1198 }
1199 else
1200 gth_catalog_list_done (list_data, error);
1201 }
1202
1203
1204 void
gth_catalog_list_async(GFile * file,const char * attributes,GCancellable * cancellable,CatalogReadyCallback ready_func,gpointer user_data)1205 gth_catalog_list_async (GFile *file,
1206 const char *attributes,
1207 GCancellable *cancellable,
1208 CatalogReadyCallback ready_func,
1209 gpointer user_data)
1210 {
1211 ListData *list_data;
1212
1213 list_data = g_new0 (ListData, 1);
1214 list_data->attributes = attributes;
1215 list_data->list_ready_func = ready_func;
1216 list_data->user_data = user_data;
1217 list_data->cancellable = _g_object_ref (cancellable);
1218
1219 _g_file_load_async (file,
1220 G_PRIORITY_DEFAULT,
1221 list_data->cancellable,
1222 list__catalog_buffer_ready_cb,
1223 list_data);
1224 }
1225
1226