1 /*
2  * frogr-picture.c -- A picture in frogr
3  *
4  * Copyright (C) 2009-2012 Mario Sanchez Prada
5  * Authors: Mario Sanchez Prada <msanchez@gnome.org>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of version 3 of the GNU General Public
9  * License as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>
18  *
19  */
20 
21 #include "frogr-picture.h"
22 
23 #include "frogr-controller.h"
24 #include "frogr-model.h"
25 
26 #include <json-glib/json-glib.h>
27 
28 #define TAGS_DELIMITER " "
29 
30 static void json_serializable_init (JsonSerializableIface *iface);
31 
32 
33 struct _FrogrPicture
34 {
35   GObject parent;
36 
37   gchar *id;
38   gchar *fileuri;
39   gchar *title;
40   gchar *description;
41   gchar *tags_string;
42   GSList *tags_list;
43 
44   gboolean is_public;
45   gboolean is_friend;
46   gboolean is_family;
47 
48   FspSafetyLevel safety_level;
49   FspContentType content_type;
50   FspLicense license;
51   FrogrLocation *location;
52   gboolean show_in_search;
53   gboolean send_location;
54   gboolean replace_date_posted;
55 
56   gboolean is_video;
57 
58   guint filesize; /* In KB */
59   gchar *datetime; /* ASCII, locale dependent, string */
60 
61   GSList *photosets;
62   GSList *groups;
63 
64   GdkPixbuf *pixbuf;
65 };
66 
67 G_DEFINE_TYPE_EXTENDED (FrogrPicture, frogr_picture, G_TYPE_OBJECT, 0,
68                         G_IMPLEMENT_INTERFACE (JSON_TYPE_SERIALIZABLE,
69                                                json_serializable_init))
70 
71 
72 /* Properties */
73 enum  {
74   PROP_0,
75   PROP_ID,
76   PROP_FILEURI,
77   PROP_TITLE,
78   PROP_DESCRIPTION,
79   PROP_TAGS_STRING,
80   PROP_IS_PUBLIC,
81   PROP_IS_FAMILY,
82   PROP_IS_FRIEND,
83   PROP_SAFETY_LEVEL,
84   PROP_CONTENT_TYPE,
85   PROP_LICENSE,
86   PROP_LOCATION,
87   PROP_SHOW_IN_SEARCH,
88   PROP_SEND_LOCATION,
89   PROP_REPLACE_DATE_POSTED,
90   PROP_IS_VIDEO,
91   PROP_FILESIZE,
92   PROP_DATETIME,
93   PROP_PHOTOSETS,
94   PROP_GROUPS
95 };
96 
97 /* Prototypes */
98 
99 static gboolean _tag_is_set (FrogrPicture *self, const gchar *tag);
100 static void _add_tags_to_tags_list (FrogrPicture *self,
101                                     const gchar *tags_string);
102 static void _update_tags_string (FrogrPicture *self);
103 
104 static JsonNode *_serialize_list (GSList *objects_list, GType g_type);
105 static gboolean _deserialize_list (JsonNode *node, GValue *value, GType g_type);
106 
107 static JsonNode *_serialize_property (JsonSerializable *serializable,
108                                       const gchar *name,
109                                       const GValue *value,
110                                       GParamSpec *pspec);
111 static gboolean _deserialize_property (JsonSerializable *serializable,
112                                        const gchar *name,
113                                        GValue *value,
114                                        GParamSpec *pspec,
115                                        JsonNode *node);
116 /* Private API */
117 
118 static gboolean
_tag_is_set(FrogrPicture * self,const gchar * tag)119 _tag_is_set (FrogrPicture *self, const gchar *tag)
120 {
121   GSList *item;
122   gboolean tag_found = FALSE;
123 
124   for (item = self->tags_list; item; item = g_slist_next (item))
125     {
126       if (g_str_equal ((gchar *)item->data, tag))
127         {
128           tag_found = TRUE;
129           break;
130         }
131     }
132 
133   return tag_found;
134 }
135 
136 static void
_add_tags_to_tags_list(FrogrPicture * self,const gchar * tags_string)137 _add_tags_to_tags_list (FrogrPicture *self,
138                         const gchar *tags_string)
139 {
140   /* Check if valid data is passed to the function */
141   if (tags_string != NULL)
142     {
143       g_autofree gchar *stripped_tags = g_strstrip (g_strdup (tags_string));
144       if (!g_str_equal (stripped_tags, ""))
145         {
146           g_auto(GStrv) tags_array = NULL;
147           gchar *tag;
148           gint i;
149 
150           /* Now iterate over every token, adding it to the list */
151           tags_array = g_strsplit (stripped_tags, TAGS_DELIMITER, -1);
152           for (i = 0; tags_array[i]; i++)
153             {
154               /* add stripped tag if not already set*/
155               tag = g_strstrip(g_strdup (tags_array[i]));
156               if (!g_str_equal (tag, "") && !_tag_is_set (self, tag))
157                 self->tags_list = g_slist_append (self->tags_list, tag);
158             }
159         }
160 
161       /* Update internal tags string */
162       _update_tags_string (self);
163     }
164 }
165 
166 static void
_update_tags_string(FrogrPicture * self)167 _update_tags_string (FrogrPicture *self)
168 {
169   /* Reset previous tags string */
170   g_free (self->tags_string);
171   self->tags_string = NULL;
172 
173   /* Set the tags_string value, if needed */
174   if (self->tags_list != NULL)
175     {
176       GSList *item = NULL;
177       gchar *new_str = NULL;
178       gchar *tmp_str = NULL;
179 
180       /* Init new_string to first item */
181       new_str = g_strdup ((gchar *)self->tags_list->data);
182 
183       /* Continue with the remaining tags */
184       for (item = g_slist_next (self->tags_list);
185            item != NULL;
186            item = g_slist_next (item))
187         {
188           tmp_str = g_strconcat (new_str, " ", (gchar *)item->data, NULL);
189           g_free (new_str);
190 
191           /* Update pointer */
192           new_str = tmp_str;
193         }
194 
195       /* Store final result */
196       self->tags_string = new_str;
197     }
198 }
199 
200 static JsonNode *
_serialize_list(GSList * objects_list,GType g_type)201 _serialize_list (GSList *objects_list, GType g_type)
202 {
203   JsonArray *json_array = NULL;
204   JsonNode *list_node = NULL;
205   GObject *object = NULL;
206   GSList *item = NULL;
207   const gchar *id = NULL;
208 
209   json_array = json_array_new ();
210   for (item = objects_list; item; item = g_slist_next (item))
211     {
212       /* We just serialize the ID of the group / set */
213       object = G_OBJECT (item->data);
214       if (g_type == FROGR_TYPE_PHOTOSET)
215         {
216           id = frogr_photoset_get_id (FROGR_PHOTOSET (object));
217           if (!id)
218             id = frogr_photoset_get_local_id (FROGR_PHOTOSET (object));
219         }
220       else if (g_type == FROGR_TYPE_GROUP)
221         id = frogr_group_get_id (FROGR_GROUP (object));
222 
223       if (id)
224         json_array_add_string_element (json_array, id);
225     }
226 
227   list_node = json_node_new(JSON_NODE_ARRAY);
228   json_node_set_array (list_node, json_array);
229 
230   return list_node;
231 }
232 
233 static gboolean
_deserialize_list(JsonNode * node,GValue * value,GType g_type)234 _deserialize_list (JsonNode *node, GValue *value, GType g_type)
235 {
236   FrogrModel *model = NULL;
237   JsonArray *array = NULL;
238   GSList *objects = NULL;
239   GObject *object = NULL;
240   guint n_elements = 0;
241   guint i = 0;
242 
243   g_return_val_if_fail (node != NULL, FALSE);
244   g_return_val_if_fail (JSON_NODE_HOLDS_ARRAY (node), FALSE);
245 
246   array = json_node_get_array (node);
247   n_elements = json_array_get_length (array);
248   if (!n_elements)
249     return TRUE;
250 
251   /* We need to get the groups and sets from the model by ID, so it's
252      mandatory to have imported those first for this to work OK */
253   model = frogr_controller_get_model (frogr_controller_get_instance ());
254   for (i = 0; i < n_elements; i++)
255     {
256       const gchar *id = NULL;
257       id = json_array_get_string_element (array, i);
258       if (g_type == FROGR_TYPE_PHOTOSET)
259         object = G_OBJECT (frogr_model_get_photoset_by_id (model, id));
260       if (g_type == FROGR_TYPE_GROUP)
261         object = G_OBJECT (frogr_model_get_group_by_id (model, id));
262 
263       if (object)
264         objects = g_slist_prepend (objects, g_object_ref (object));
265     }
266   g_value_set_pointer (value, g_slist_reverse (objects));
267 
268   return TRUE;
269 }
270 
271 static JsonNode *
_serialize_property(JsonSerializable * serializable,const gchar * name,const GValue * value,GParamSpec * pspec)272 _serialize_property (JsonSerializable *serializable,
273                      const gchar *name,
274                      const GValue *value,
275                      GParamSpec *pspec)
276 {
277   FrogrPicture *self = NULL;
278   JsonNode *json_node = NULL;
279 
280   g_return_val_if_fail (FROGR_IS_PICTURE (serializable), FALSE);
281 
282   self = FROGR_PICTURE (serializable);
283   if (g_str_equal (name, "photosets"))
284     json_node = _serialize_list (self->photosets, FROGR_TYPE_PHOTOSET);
285   else if (g_str_equal (name, "groups"))
286     json_node = _serialize_list (self->groups, FROGR_TYPE_GROUP);
287   else
288     {
289       /* Default serialization here */
290       JsonSerializableIface *iface = NULL;
291       iface = g_type_default_interface_peek (JSON_TYPE_SERIALIZABLE);
292       json_node = iface->serialize_property (serializable,
293                                              name,
294                                              value,
295                                              pspec);
296     }
297 
298   return json_node; /* NULL indicates default deserializer */
299 }
300 
301 static gboolean
_deserialize_property(JsonSerializable * serializable,const gchar * name,GValue * value,GParamSpec * pspec,JsonNode * node)302 _deserialize_property (JsonSerializable *serializable,
303                        const gchar *name,
304                        GValue *value,
305                        GParamSpec *pspec,
306                        JsonNode *node)
307 {
308   gboolean result = FALSE;
309 
310   g_return_val_if_fail (node != NULL, FALSE);
311 
312   if (g_str_equal (name, "photosets"))
313     result = _deserialize_list (node, value, FROGR_TYPE_PHOTOSET);
314   else if (g_str_equal (name, "groups"))
315     result = _deserialize_list (node, value, FROGR_TYPE_GROUP);
316   else
317     {
318       /* Default deserialization */
319       JsonSerializableIface *iface = NULL;
320 
321       iface = g_type_default_interface_peek (JSON_TYPE_SERIALIZABLE);
322       result = iface->deserialize_property (serializable,
323                                             name,
324                                             value,
325                                             pspec,
326                                             node);
327     }
328 
329   return result;
330 }
331 
332 static void
json_serializable_init(JsonSerializableIface * iface)333 json_serializable_init (JsonSerializableIface *iface)
334 {
335   iface->serialize_property = _serialize_property;
336   iface->deserialize_property = _deserialize_property;
337 }
338 
339 static void
_frogr_picture_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)340 _frogr_picture_set_property (GObject *object,
341                              guint prop_id,
342                              const GValue *value,
343                              GParamSpec *pspec)
344 {
345   FrogrPicture *self = FROGR_PICTURE (object);
346 
347   switch (prop_id)
348     {
349     case PROP_ID:
350       frogr_picture_set_id (self, g_value_get_string (value));
351       break;
352     case PROP_FILEURI:
353       self->fileuri = g_value_dup_string (value);
354       break;
355     case PROP_TITLE:
356       frogr_picture_set_title (self, g_value_get_string (value));
357       break;
358     case PROP_DESCRIPTION:
359       frogr_picture_set_description (self, g_value_get_string (value));
360       break;
361     case PROP_TAGS_STRING:
362       frogr_picture_set_tags (self, g_value_get_string (value));
363       break;
364     case PROP_IS_PUBLIC:
365       frogr_picture_set_public (self, g_value_get_boolean (value));
366       break;
367     case PROP_IS_FAMILY:
368       frogr_picture_set_family (self, g_value_get_boolean (value));
369       break;
370     case PROP_IS_FRIEND:
371       frogr_picture_set_friend (self, g_value_get_boolean (value));
372       break;
373     case PROP_SAFETY_LEVEL:
374       frogr_picture_set_safety_level (self, g_value_get_int (value));
375       break;
376     case PROP_CONTENT_TYPE:
377       frogr_picture_set_content_type (self, g_value_get_int (value));
378       break;
379     case PROP_LICENSE:
380       frogr_picture_set_license (self, g_value_get_int (value));
381       break;
382     case PROP_LOCATION:
383       frogr_picture_set_location (self, FROGR_LOCATION (g_value_get_object (value)));
384       break;
385     case PROP_SHOW_IN_SEARCH:
386       frogr_picture_set_show_in_search (self, g_value_get_boolean (value));
387       break;
388     case PROP_SEND_LOCATION:
389       frogr_picture_set_send_location (self, g_value_get_boolean (value));
390       break;
391     case PROP_REPLACE_DATE_POSTED:
392       frogr_picture_set_replace_date_posted (self, g_value_get_boolean (value));
393       break;
394     case PROP_IS_VIDEO:
395       self->is_video = g_value_get_boolean (value);
396       break;
397     case PROP_FILESIZE:
398       frogr_picture_set_filesize (self, g_value_get_uint (value));
399       break;
400     case PROP_DATETIME:
401       frogr_picture_set_datetime (self, g_value_get_string (value));
402       break;
403     case PROP_PHOTOSETS:
404       frogr_picture_set_photosets (self, (GSList *) g_value_get_pointer (value));
405       break;
406     case PROP_GROUPS:
407       frogr_picture_set_groups (self, (GSList *) g_value_get_pointer (value));
408       break;
409     default:
410       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
411       break;
412     }
413 }
414 
415 static void
_frogr_picture_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)416 _frogr_picture_get_property (GObject *object,
417                              guint prop_id,
418                              GValue *value,
419                              GParamSpec *pspec)
420 {
421   FrogrPicture *self = FROGR_PICTURE (object);
422 
423   switch (prop_id)
424     {
425     case PROP_ID:
426       g_value_set_string (value, self->id);
427       break;
428     case PROP_FILEURI:
429       g_value_set_string (value, self->fileuri);
430       break;
431     case PROP_TITLE:
432       g_value_set_string (value, self->title);
433       break;
434     case PROP_DESCRIPTION:
435       g_value_set_string (value, self->description);
436       break;
437     case PROP_TAGS_STRING:
438       g_value_set_string (value, self->tags_string);
439       break;
440     case PROP_IS_PUBLIC:
441       g_value_set_boolean (value, self->is_public);
442       break;
443     case PROP_IS_FAMILY:
444       g_value_set_boolean (value, self->is_family);
445       break;
446     case PROP_IS_FRIEND:
447       g_value_set_boolean (value, self->is_friend);
448       break;
449     case PROP_SAFETY_LEVEL:
450       g_value_set_int (value, self->safety_level);
451       break;
452     case PROP_CONTENT_TYPE:
453       g_value_set_int (value, self->content_type);
454       break;
455     case PROP_LICENSE:
456       g_value_set_int (value, self->license);
457       break;
458     case PROP_LOCATION:
459       g_value_set_object (value, self->location);
460       break;
461     case PROP_SHOW_IN_SEARCH:
462       g_value_set_boolean (value, self->show_in_search);
463       break;
464     case PROP_SEND_LOCATION:
465       g_value_set_boolean (value, self->send_location);
466       break;
467     case PROP_REPLACE_DATE_POSTED:
468       g_value_set_boolean (value, self->replace_date_posted);
469       break;
470     case PROP_IS_VIDEO:
471       g_value_set_boolean (value, self->is_video);
472       break;
473     case PROP_FILESIZE:
474       g_value_set_uint (value, self->filesize);
475       break;
476     case PROP_DATETIME:
477       g_value_set_string (value, self->datetime);
478       break;
479     case PROP_PHOTOSETS:
480       g_value_set_pointer (value, self->photosets);
481       break;
482     case PROP_GROUPS:
483       g_value_set_pointer (value, self->groups);
484       break;
485     default:
486       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
487       break;
488     }
489 }
490 
491 static void
_frogr_picture_dispose(GObject * object)492 _frogr_picture_dispose (GObject* object)
493 {
494   FrogrPicture *self = FROGR_PICTURE (object);
495 
496   g_clear_object (&self->pixbuf);
497   g_clear_object (&self->location);
498 
499   if (self->photosets)
500     {
501       g_slist_free_full (self->photosets, g_object_unref);
502       self->photosets = NULL;
503     }
504 
505   if (self->groups)
506     {
507       g_slist_free_full (self->groups, g_object_unref);
508       self->groups = NULL;
509     }
510 
511   /* call super class */
512   G_OBJECT_CLASS (frogr_picture_parent_class)->dispose(object);
513 }
514 
515 static void
_frogr_picture_finalize(GObject * object)516 _frogr_picture_finalize (GObject* object)
517 {
518   FrogrPicture *self = FROGR_PICTURE (object);
519 
520   /* free strings */
521   g_free (self->id);
522   g_free (self->fileuri);
523   g_free (self->title);
524   g_free (self->description);
525   g_free (self->tags_string);
526   g_free (self->datetime);
527 
528   /* free GSList of tags */
529   g_slist_free_full (self->tags_list, g_free);
530 
531   /* call super class */
532   G_OBJECT_CLASS (frogr_picture_parent_class)->finalize(object);
533 }
534 
535 static void
frogr_picture_class_init(FrogrPictureClass * klass)536 frogr_picture_class_init(FrogrPictureClass *klass)
537 {
538   GObjectClass *obj_class = G_OBJECT_CLASS(klass);
539 
540   /* GtkObject signals */
541   obj_class->set_property = _frogr_picture_set_property;
542   obj_class->get_property = _frogr_picture_get_property;
543   obj_class->dispose = _frogr_picture_dispose;
544   obj_class->finalize = _frogr_picture_finalize;
545 
546   /* Install properties */
547   g_object_class_install_property (obj_class,
548                                    PROP_ID,
549                                    g_param_spec_string ("id",
550                                                         "id",
551                                                         "Photo ID from flickr",
552                                                         NULL,
553                                                         G_PARAM_READWRITE));
554 
555   g_object_class_install_property (obj_class,
556                                    PROP_FILEURI,
557                                    g_param_spec_string ("fileuri",
558                                                         "fileuri",
559                                                         "Full fileuri at disk "
560                                                         "for the picture",
561                                                         NULL,
562                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
563   g_object_class_install_property (obj_class,
564                                    PROP_TITLE,
565                                    g_param_spec_string ("title",
566                                                         "title",
567                                                         "Picture's title",
568                                                         NULL,
569                                                         G_PARAM_READWRITE));
570   g_object_class_install_property (obj_class,
571                                    PROP_DESCRIPTION,
572                                    g_param_spec_string ("description",
573                                                         "description",
574                                                         "Picture's description",
575                                                         NULL,
576                                                         G_PARAM_READWRITE));
577   g_object_class_install_property (obj_class,
578                                    PROP_TAGS_STRING,
579                                    g_param_spec_string ("tags-string",
580                                                         "tags-string",
581                                                         "List of tags separated"
582                                                         " with blanks between",
583                                                         NULL,
584                                                         G_PARAM_READWRITE));
585   g_object_class_install_property (obj_class,
586                                    PROP_IS_PUBLIC,
587                                    g_param_spec_boolean ("is-public",
588                                                          "is-public",
589                                                          "Whether the picture "
590                                                          "is public or not",
591                                                          FALSE,
592                                                          G_PARAM_READWRITE));
593   g_object_class_install_property (obj_class,
594                                    PROP_IS_FAMILY,
595                                    g_param_spec_boolean ("is-family",
596                                                          "is-family",
597                                                          "Whether the picture "
598                                                          "will be seen by "
599                                                          "relatives or not",
600                                                          FALSE,
601                                                          G_PARAM_READWRITE));
602   g_object_class_install_property (obj_class,
603                                    PROP_IS_FRIEND,
604                                    g_param_spec_boolean ("is-friend",
605                                                          "is-friend",
606                                                          "Whether the picture "
607                                                          "will be seen by "
608                                                          "friends or not",
609                                                          FALSE,
610                                                          G_PARAM_READWRITE));
611   g_object_class_install_property (obj_class,
612                                    PROP_SAFETY_LEVEL,
613                                    g_param_spec_int ("safety-level",
614                                                      "safety-level",
615                                                      "Safety level for this picture "
616                                                      "(safe/moderate/restricted)",
617                                                      FSP_SAFETY_LEVEL_NONE,
618                                                      FSP_SAFETY_LEVEL_RESTRICTED,
619                                                      FSP_SAFETY_LEVEL_SAFE,
620                                                      G_PARAM_READWRITE));
621   g_object_class_install_property (obj_class,
622                                    PROP_CONTENT_TYPE,
623                                    g_param_spec_int ("content-type",
624                                                      "content-type",
625                                                      "Content type for this picture "
626                                                      "(photo/screenshot/other)",
627                                                      FSP_CONTENT_TYPE_NONE,
628                                                      FSP_CONTENT_TYPE_OTHER,
629                                                      FSP_CONTENT_TYPE_PHOTO,
630                                                      G_PARAM_READWRITE));
631   g_object_class_install_property (obj_class,
632                                    PROP_LICENSE,
633                                    g_param_spec_int ("license",
634                                                      "license",
635                                                      "License for this picture",
636                                                      FSP_LICENSE_NONE,
637                                                      FSP_LICENSE_ND,
638                                                      FSP_LICENSE_NONE,
639                                                      G_PARAM_READWRITE));
640   g_object_class_install_property (obj_class,
641                                    PROP_LOCATION,
642                                    g_param_spec_object ("location",
643                                                         "location",
644                                                         "Location for this picture",
645                                                         FROGR_TYPE_LOCATION,
646                                                         G_PARAM_READWRITE));
647   g_object_class_install_property (obj_class,
648                                    PROP_SHOW_IN_SEARCH,
649                                    g_param_spec_boolean ("show-in-search",
650                                                          "show-in-search",
651                                                          "Whether to show the "
652                                                          "picture in global "
653                                                          "search results",
654                                                          FALSE,
655                                                          G_PARAM_READWRITE));
656   g_object_class_install_property (obj_class,
657                                    PROP_SEND_LOCATION,
658                                    g_param_spec_boolean ("send-location",
659                                                          "send-location",
660                                                          "Whether to send the location "
661                                                          "to flickr, if available",
662                                                          FALSE,
663                                                          G_PARAM_READWRITE));
664   g_object_class_install_property (obj_class,
665                                    PROP_REPLACE_DATE_POSTED,
666                                    g_param_spec_boolean ("replace-date-posted",
667                                                          "replace-date-posted",
668                                                          "Whether to replace the 'date posted'"
669                                                          "with 'date taken', after uploading",
670                                                          FALSE,
671                                                          G_PARAM_READWRITE));
672   g_object_class_install_property (obj_class,
673                                    PROP_FILESIZE,
674                                    g_param_spec_uint ("filesize",
675                                                       "filesize",
676                                                       "Filesize in KB for the file",
677                                                       0,
678                                                       G_MAXUINT,
679                                                       0,
680                                                       G_PARAM_READWRITE));
681   g_object_class_install_property (obj_class,
682                                    PROP_IS_VIDEO,
683                                    g_param_spec_boolean ("is-video",
684                                                          "is-video",
685                                                          "Whether FrogrPicture represents a video",
686                                                          FALSE,
687                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
688   g_object_class_install_property (obj_class,
689                                    PROP_DATETIME,
690                                    g_param_spec_string ("datetime",
691                                                         "datetime",
692                                                         "Date and time string for the file",
693                                                         NULL,
694                                                         G_PARAM_READWRITE));
695   g_object_class_install_property (obj_class,
696                                    PROP_PHOTOSETS,
697                                    g_param_spec_pointer ("photosets",
698                                                          "photosets",
699                                                          "List of sets the picture is in",
700                                                          G_PARAM_READWRITE));
701   g_object_class_install_property (obj_class,
702                                    PROP_GROUPS,
703                                    g_param_spec_pointer ("groups",
704                                                          "groups",
705                                                          "List of groups the picture is in",
706                                                          G_PARAM_READWRITE));
707 }
708 
709 static void
frogr_picture_init(FrogrPicture * self)710 frogr_picture_init (FrogrPicture *self)
711 {
712   /* Default values */
713   self->id = NULL;
714   self->fileuri = NULL;
715   self->title = NULL;
716   self->description = NULL;
717   self->tags_string = NULL;
718   self->tags_list = NULL;
719 
720   self->is_public = FALSE;
721   self->is_friend = FALSE;
722   self->is_family = FALSE;
723 
724   self->safety_level = FSP_SAFETY_LEVEL_SAFE;
725   self->content_type = FSP_CONTENT_TYPE_PHOTO;
726   self->license = FSP_LICENSE_NONE;
727   self->location = NULL;
728 
729   self->show_in_search = TRUE;
730   self->send_location = FALSE;
731   self->replace_date_posted = FALSE;
732 
733   self->is_video = FALSE;
734 
735   self->filesize = 0;
736   self->datetime = NULL;
737 
738   self->photosets = NULL;
739   self->groups = NULL;
740 
741   self->pixbuf = NULL;
742 }
743 
744 
745 /* Public API */
746 
747 FrogrPicture *
frogr_picture_new(const gchar * fileuri,const gchar * title,gboolean public,gboolean family,gboolean friend,gboolean is_video)748 frogr_picture_new (const gchar *fileuri,
749                    const gchar *title,
750                    gboolean public,
751                    gboolean family,
752                    gboolean friend,
753                    gboolean is_video)
754 {
755   g_return_val_if_fail (fileuri, NULL);
756   g_return_val_if_fail (title, NULL);
757 
758   return FROGR_PICTURE (g_object_new(FROGR_TYPE_PICTURE,
759                                      "fileuri", fileuri,
760                                      "title", title,
761                                      "is-public", public,
762                                      "is-family", family,
763                                      "is-friend", friend,
764                                      "is-video", is_video,
765                                      NULL));
766 }
767 
768 
769 /* Data Managing functions */
770 
771 const gchar *
frogr_picture_get_id(FrogrPicture * self)772 frogr_picture_get_id (FrogrPicture *self)
773 {
774   g_return_val_if_fail(FROGR_IS_PICTURE(self), NULL);
775   return (const gchar *)self->id;
776 }
777 
778 void
frogr_picture_set_id(FrogrPicture * self,const gchar * id)779 frogr_picture_set_id (FrogrPicture *self,
780                       const gchar *id)
781 {
782   g_return_if_fail(FROGR_IS_PICTURE(self));
783   g_free (self->id);
784   self->id = g_strdup (id);
785 }
786 
787 const gchar *
frogr_picture_get_fileuri(FrogrPicture * self)788 frogr_picture_get_fileuri (FrogrPicture *self)
789 {
790   g_return_val_if_fail(FROGR_IS_PICTURE(self), NULL);
791   return (const gchar *)self->fileuri;
792 }
793 
794 const gchar *
frogr_picture_get_title(FrogrPicture * self)795 frogr_picture_get_title (FrogrPicture *self)
796 {
797   g_return_val_if_fail(FROGR_IS_PICTURE(self), NULL);
798   return (const gchar *)self->title;
799 }
800 
801 void
frogr_picture_set_title(FrogrPicture * self,const gchar * title)802 frogr_picture_set_title (FrogrPicture *self,
803                          const gchar *title)
804 {
805   g_return_if_fail(FROGR_IS_PICTURE(self));
806   g_return_if_fail(title != NULL);
807 
808   g_free (self->title);
809   self->title = g_strdup (title);
810 }
811 
812 const gchar *
frogr_picture_get_description(FrogrPicture * self)813 frogr_picture_get_description (FrogrPicture *self)
814 {
815   g_return_val_if_fail(FROGR_IS_PICTURE(self), NULL);
816   return (const gchar *)self->description;
817 }
818 
819 void
frogr_picture_set_description(FrogrPicture * self,const gchar * description)820 frogr_picture_set_description (FrogrPicture *self,
821                                const gchar *description)
822 {
823   g_return_if_fail(FROGR_IS_PICTURE(self));
824   g_free (self->description);
825   self->description = g_strdup (description);
826 }
827 
828 const GSList *
frogr_picture_get_tags_list(FrogrPicture * self)829 frogr_picture_get_tags_list (FrogrPicture *self)
830 {
831   g_return_val_if_fail(FROGR_IS_PICTURE(self), NULL);
832   return self->tags_list;
833 }
834 
835 const gchar *
frogr_picture_get_tags(FrogrPicture * self)836 frogr_picture_get_tags (FrogrPicture *self)
837 {
838   g_return_val_if_fail(FROGR_IS_PICTURE(self), NULL);
839   return self->tags_string;
840 }
841 
842 void
frogr_picture_set_tags(FrogrPicture * self,const gchar * tags_string)843 frogr_picture_set_tags (FrogrPicture *self, const gchar *tags_string)
844 {
845   g_return_if_fail(FROGR_IS_PICTURE(self));
846 
847   /* First remove all the previous tags list */
848   g_slist_free_full (self->tags_list, g_free);
849   self->tags_list = NULL;
850 
851   /* Add to internal tags_list */
852   _add_tags_to_tags_list (self, tags_string);
853 }
854 
855 void
frogr_picture_add_tags(FrogrPicture * self,const gchar * tags_string)856 frogr_picture_add_tags (FrogrPicture *self, const gchar *tags_string)
857 {
858   g_return_if_fail(FROGR_IS_PICTURE(self));
859 
860   _add_tags_to_tags_list (self, tags_string);
861 }
862 
863 void
frogr_picture_remove_tags(FrogrPicture * self)864 frogr_picture_remove_tags (FrogrPicture *self)
865 {
866   g_return_if_fail(FROGR_IS_PICTURE(self));
867 
868   frogr_picture_set_tags (self, NULL);
869 }
870 
871 gboolean
frogr_picture_is_public(FrogrPicture * self)872 frogr_picture_is_public (FrogrPicture *self)
873 {
874   g_return_val_if_fail(FROGR_IS_PICTURE(self), FALSE);
875   return self->is_public;
876 }
877 
878 void
frogr_picture_set_public(FrogrPicture * self,gboolean public)879 frogr_picture_set_public (FrogrPicture *self,
880                           gboolean public)
881 {
882   g_return_if_fail(FROGR_IS_PICTURE(self));
883   self->is_public = public;
884 }
885 
886 gboolean
frogr_picture_is_friend(FrogrPicture * self)887 frogr_picture_is_friend (FrogrPicture *self)
888 {
889   g_return_val_if_fail(FROGR_IS_PICTURE(self), FALSE);
890   return self->is_friend;
891 }
892 
893 void
frogr_picture_set_friend(FrogrPicture * self,gboolean friend)894 frogr_picture_set_friend (FrogrPicture *self,
895                           gboolean friend)
896 {
897   g_return_if_fail(FROGR_IS_PICTURE(self));
898   self->is_friend = friend;
899 }
900 
901 gboolean
frogr_picture_is_family(FrogrPicture * self)902 frogr_picture_is_family (FrogrPicture *self)
903 {
904   g_return_val_if_fail(FROGR_IS_PICTURE(self), FALSE);
905   return self->is_family;
906 }
907 
908 void
frogr_picture_set_family(FrogrPicture * self,gboolean family)909 frogr_picture_set_family (FrogrPicture *self,
910                           gboolean family)
911 {
912   g_return_if_fail(FROGR_IS_PICTURE(self));
913   self->is_family = family;
914 }
915 
916 FspSafetyLevel
frogr_picture_get_safety_level(FrogrPicture * self)917 frogr_picture_get_safety_level (FrogrPicture *self)
918 {
919   g_return_val_if_fail(FROGR_IS_PICTURE(self), FALSE);
920   return self->safety_level;
921 }
922 
923 void
frogr_picture_set_safety_level(FrogrPicture * self,FspSafetyLevel safety_level)924 frogr_picture_set_safety_level (FrogrPicture *self,
925                                 FspSafetyLevel safety_level)
926 {
927   g_return_if_fail(FROGR_IS_PICTURE(self));
928   self->safety_level = safety_level;
929 }
930 
931 FspContentType
frogr_picture_get_content_type(FrogrPicture * self)932 frogr_picture_get_content_type (FrogrPicture *self)
933 {
934   g_return_val_if_fail(FROGR_IS_PICTURE(self), FALSE);
935   return self->content_type;
936 }
937 
938 void
frogr_picture_set_content_type(FrogrPicture * self,FspContentType content_type)939 frogr_picture_set_content_type (FrogrPicture *self,
940                                 FspContentType content_type)
941 {
942   g_return_if_fail(FROGR_IS_PICTURE(self));
943   self->content_type = content_type;
944 }
945 
946 FspLicense
frogr_picture_get_license(FrogrPicture * self)947 frogr_picture_get_license (FrogrPicture *self)
948 {
949   g_return_val_if_fail(FROGR_IS_PICTURE(self), FALSE);
950   return self->license;
951 }
952 
953 void
frogr_picture_set_license(FrogrPicture * self,FspLicense license)954 frogr_picture_set_license (FrogrPicture *self, FspLicense license)
955 {
956   g_return_if_fail(FROGR_IS_PICTURE(self));
957   self->license = license;
958 }
959 
960 FrogrLocation *
frogr_picture_get_location(FrogrPicture * self)961 frogr_picture_get_location (FrogrPicture *self)
962 {
963   g_return_val_if_fail(FROGR_IS_PICTURE(self), FALSE);
964   return self->location;
965 }
966 
967 void
frogr_picture_set_location(FrogrPicture * self,FrogrLocation * location)968 frogr_picture_set_location (FrogrPicture *self, FrogrLocation *location)
969 {
970   g_return_if_fail(FROGR_IS_PICTURE(self));
971 
972   if (self->location)
973     g_object_unref (self->location);
974 
975   self->location = FROGR_IS_LOCATION (location) ? g_object_ref (location) : NULL;
976 }
977 
978 gboolean
frogr_picture_show_in_search(FrogrPicture * self)979 frogr_picture_show_in_search (FrogrPicture *self)
980 {
981   g_return_val_if_fail(FROGR_IS_PICTURE(self), FALSE);
982   return self->show_in_search;
983 }
984 
985 void
frogr_picture_set_show_in_search(FrogrPicture * self,gboolean show_in_search)986 frogr_picture_set_show_in_search (FrogrPicture *self,
987                                   gboolean show_in_search)
988 {
989   g_return_if_fail(FROGR_IS_PICTURE(self));
990   self->show_in_search = show_in_search;
991 }
992 
993 gboolean
frogr_picture_send_location(FrogrPicture * self)994 frogr_picture_send_location (FrogrPicture *self)
995 {
996   g_return_val_if_fail(FROGR_IS_PICTURE(self), FALSE);
997   return self->send_location;
998 }
999 
1000 void
frogr_picture_set_send_location(FrogrPicture * self,gboolean send_location)1001 frogr_picture_set_send_location (FrogrPicture *self, gboolean send_location)
1002 {
1003   g_return_if_fail(FROGR_IS_PICTURE(self));
1004   self->send_location = send_location;
1005 }
1006 
1007 gboolean
frogr_picture_replace_date_posted(FrogrPicture * self)1008 frogr_picture_replace_date_posted (FrogrPicture *self)
1009 {
1010   g_return_val_if_fail(FROGR_IS_PICTURE(self), FALSE);
1011   return self->replace_date_posted;
1012 }
1013 
1014 void
frogr_picture_set_replace_date_posted(FrogrPicture * self,gboolean replace_date_posted)1015 frogr_picture_set_replace_date_posted (FrogrPicture *self, gboolean replace_date_posted)
1016 {
1017   g_return_if_fail(FROGR_IS_PICTURE(self));
1018   self->replace_date_posted = replace_date_posted;
1019 }
1020 
1021 GdkPixbuf *
frogr_picture_get_pixbuf(FrogrPicture * self)1022 frogr_picture_get_pixbuf (FrogrPicture *self)
1023 {
1024   g_return_val_if_fail(FROGR_IS_PICTURE(self), NULL);
1025   return self->pixbuf;
1026 }
1027 
1028 void
frogr_picture_set_pixbuf(FrogrPicture * self,GdkPixbuf * pixbuf)1029 frogr_picture_set_pixbuf (FrogrPicture *self,
1030                           GdkPixbuf *pixbuf)
1031 {
1032   g_return_if_fail(FROGR_IS_PICTURE(self));
1033 
1034   if (self->pixbuf)
1035     g_object_unref (self->pixbuf);
1036 
1037   self->pixbuf = GDK_IS_PIXBUF (pixbuf) ? g_object_ref (pixbuf) : NULL;
1038 }
1039 
frogr_picture_is_video(FrogrPicture * self)1040 gboolean frogr_picture_is_video (FrogrPicture *self)
1041 {
1042   g_return_val_if_fail(FROGR_IS_PICTURE(self), FALSE);
1043   return self->is_video;
1044 }
1045 
frogr_picture_get_filesize(FrogrPicture * self)1046 guint frogr_picture_get_filesize (FrogrPicture *self)
1047 {
1048   g_return_val_if_fail(FROGR_IS_PICTURE(self), 0);
1049   return self->filesize;
1050 }
1051 
frogr_picture_set_filesize(FrogrPicture * self,guint filesize)1052 void frogr_picture_set_filesize (FrogrPicture *self, guint filesize)
1053 {
1054   g_return_if_fail(FROGR_IS_PICTURE(self));
1055   self->filesize = filesize;
1056 }
1057 
1058 void
frogr_picture_set_datetime(FrogrPicture * self,const gchar * datetime)1059 frogr_picture_set_datetime (FrogrPicture *self, const gchar *datetime)
1060 {
1061   g_return_if_fail(FROGR_IS_PICTURE(self));
1062   g_free (self->datetime);
1063   self->datetime = g_strdup (datetime);
1064 }
1065 
1066 const gchar *
frogr_picture_get_datetime(FrogrPicture * self)1067 frogr_picture_get_datetime (FrogrPicture *self)
1068 {
1069   g_return_val_if_fail(FROGR_IS_PICTURE(self), 0);
1070   return (const gchar *)self->datetime;
1071 }
1072 
1073 GSList *
frogr_picture_get_photosets(FrogrPicture * self)1074 frogr_picture_get_photosets (FrogrPicture *self)
1075 {
1076   g_return_val_if_fail(FROGR_IS_PICTURE(self), NULL);
1077   return self->photosets;
1078 }
1079 
1080 void
frogr_picture_set_photosets(FrogrPicture * self,GSList * photosets)1081 frogr_picture_set_photosets (FrogrPicture *self, GSList *photosets)
1082 {
1083   g_return_if_fail(FROGR_IS_PICTURE(self));
1084 
1085   /* First remove all the previous sets list */
1086   g_slist_free_full (self->photosets, g_object_unref);
1087   self->photosets = photosets;
1088 }
1089 
1090 void
frogr_picture_add_photoset(FrogrPicture * self,FrogrPhotoSet * photoset)1091 frogr_picture_add_photoset (FrogrPicture *self, FrogrPhotoSet *photoset)
1092 {
1093   g_return_if_fail(FROGR_IS_PICTURE(self));
1094   g_return_if_fail(FROGR_IS_PHOTOSET(photoset));
1095 
1096   /* Do not add the same set twice */
1097   if (frogr_picture_in_photoset (self, photoset))
1098     return;
1099 
1100   self->photosets = g_slist_append (self->photosets, g_object_ref (photoset));
1101 }
1102 
1103 void
frogr_picture_remove_photosets(FrogrPicture * self)1104 frogr_picture_remove_photosets (FrogrPicture *self)
1105 {
1106   g_return_if_fail(FROGR_IS_PICTURE(self));
1107 
1108   frogr_picture_set_photosets (self, NULL);
1109 }
1110 
1111 gboolean
frogr_picture_in_photoset(FrogrPicture * self,FrogrPhotoSet * photoset)1112 frogr_picture_in_photoset (FrogrPicture *self, FrogrPhotoSet *photoset)
1113 {
1114   g_return_val_if_fail(FROGR_IS_PICTURE(self), FALSE);
1115 
1116   if (g_slist_find_custom (self->photosets, photoset,
1117                            (GCompareFunc)frogr_photoset_compare))
1118     return TRUE;
1119 
1120   return FALSE;
1121 }
1122 
1123 GSList *
frogr_picture_get_groups(FrogrPicture * self)1124 frogr_picture_get_groups (FrogrPicture *self)
1125 {
1126   g_return_val_if_fail(FROGR_IS_PICTURE(self), NULL);
1127   return self->groups;
1128 }
1129 
1130 void
frogr_picture_set_groups(FrogrPicture * self,GSList * groups)1131 frogr_picture_set_groups (FrogrPicture *self, GSList *groups)
1132 {
1133   g_return_if_fail(FROGR_IS_PICTURE(self));
1134 
1135   /* First remove all the previous groups list */
1136   g_slist_free_full (self->groups, g_object_unref);
1137   self->groups = groups;
1138 }
1139 
1140 void
frogr_picture_add_group(FrogrPicture * self,FrogrGroup * group)1141 frogr_picture_add_group (FrogrPicture *self, FrogrGroup *group)
1142 {
1143   g_return_if_fail(FROGR_IS_PICTURE(self));
1144   g_return_if_fail(FROGR_IS_GROUP(group));
1145 
1146   /* Do not add the same set twice */
1147   if (frogr_picture_in_group (self, group))
1148     return;
1149 
1150   self->groups = g_slist_append (self->groups, g_object_ref (group));
1151 }
1152 
1153 void
frogr_picture_remove_groups(FrogrPicture * self)1154 frogr_picture_remove_groups (FrogrPicture *self)
1155 {
1156   g_return_if_fail(FROGR_IS_PICTURE(self));
1157 
1158   frogr_picture_set_groups (self, NULL);
1159 }
1160 
1161 gboolean
frogr_picture_in_group(FrogrPicture * self,FrogrGroup * group)1162 frogr_picture_in_group (FrogrPicture *self, FrogrGroup *group)
1163 {
1164   g_return_val_if_fail(FROGR_IS_PICTURE(self), FALSE);
1165 
1166   if (g_slist_find_custom (self->groups, group,
1167                            (GCompareFunc)frogr_group_compare))
1168     return TRUE;
1169 
1170   return FALSE;
1171 }
1172 
1173 gint
frogr_picture_compare_by_property(FrogrPicture * self,FrogrPicture * other,const gchar * property_name)1174 frogr_picture_compare_by_property (FrogrPicture *self, FrogrPicture *other,
1175                                    const gchar *property_name)
1176 {
1177   GParamSpec *pspec1 = NULL;
1178   GParamSpec *pspec2 = NULL;
1179   GValue value1 = { 0 };
1180   GValue value2 = { 0 };
1181   gint result = 0;
1182 
1183   g_return_val_if_fail (FROGR_IS_PICTURE (self), 1);
1184   g_return_val_if_fail (FROGR_IS_PICTURE (other), -1);
1185 
1186   pspec1 = g_object_class_find_property (G_OBJECT_GET_CLASS (self), property_name);
1187   pspec2 = g_object_class_find_property (G_OBJECT_GET_CLASS (other), property_name);
1188 
1189   /* They should be the same! */
1190   if (pspec1->value_type != pspec2->value_type)
1191     return 0;
1192 
1193   g_value_init (&value1, pspec1->value_type);
1194   g_value_init (&value2, pspec1->value_type);
1195 
1196   g_object_get_property (G_OBJECT (self), property_name, &value1);
1197   g_object_get_property (G_OBJECT (other), property_name, &value2);
1198 
1199   if (G_VALUE_HOLDS_BOOLEAN (&value1))
1200     result = g_value_get_boolean (&value1) - g_value_get_boolean (&value2);
1201   else if (G_VALUE_HOLDS_INT (&value1))
1202     result = g_value_get_int (&value1) - g_value_get_int (&value2);
1203   else if (G_VALUE_HOLDS_UINT (&value1))
1204     result = g_value_get_uint (&value1) - g_value_get_uint (&value2);
1205   else if (G_VALUE_HOLDS_LONG (&value1))
1206     result = g_value_get_long (&value1) - g_value_get_long (&value2);
1207   else if (G_VALUE_HOLDS_STRING (&value1))
1208     {
1209       const gchar *str1 = NULL;
1210       const gchar *str2 = NULL;
1211       g_autofree gchar *str1_cf = NULL;
1212       g_autofree gchar *str2_cf = NULL;
1213 
1214       /* Comparison of strings require some additional work to take
1215          into account the different rules for each locale */
1216       str1 = g_value_get_string (&value1);
1217       str2 = g_value_get_string (&value2);
1218 
1219       str1_cf = g_utf8_casefold (str1 ? str1 : "", -1);
1220       str2_cf = g_utf8_casefold (str2 ? str2 : "", -1);
1221 
1222       result = g_utf8_collate (str1_cf, str2_cf);
1223     }
1224   else
1225     g_warning ("Unsupported type for property used for sorting");
1226 
1227   g_value_unset (&value1);
1228   g_value_unset (&value2);
1229 
1230   return result;
1231 }
1232