1 /*
2  * frogr-file-loader.c -- Asynchronous file loader in frogr
3  *
4  * Copyright (C) 2009-2017 Mario Sanchez Prada
5  * Authors: Mario Sanchez Prada <msanchez@gnome.org>
6  *
7  * Some parts of this file were based on source code from tracker,
8  * licensed under the GNU Lesser General Public License Version 2.1
9  * (Copyright 2009, Nokia Corp.)
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of version 3 of the GNU General Public
13  * License as published by the Free Software Foundation.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, see <http://www.gnu.org/licenses/>
22  *
23  * Parts of this file based on code from GTK+, licensed as GPL version 2
24  * or later (Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.)
25  */
26 
27 #include "frogr-file-loader.h"
28 
29 #include "frogr-config.h"
30 #include "frogr-controller.h"
31 #include "frogr-global-defs.h"
32 #include "frogr-location.h"
33 #include "frogr-main-view.h"
34 #include "frogr-picture.h"
35 #include "frogr-util.h"
36 
37 #include <config.h>
38 #include <libexif/exif-byte-order.h>
39 #include <libexif/exif-data.h>
40 #include <libexif/exif-entry.h>
41 #include <libexif/exif-format.h>
42 #include <libexif/exif-loader.h>
43 #include <libexif/exif-tag.h>
44 #include <glib/gi18n.h>
45 #include <gio/gio.h>
46 
47 
48 typedef enum {
49   LOADING_MODE_FROM_URIS,
50   LOADING_MODE_FROM_PICTURES,
51 } LoadingMode;
52 
53 
54 struct _FrogrFileLoader
55 {
56   GObject parent;
57 
58   FrogrController *controller;
59   FrogrMainView *mainview;
60 
61   LoadingMode loading_mode;
62 
63   GSList *file_uris; /* For URI-based loading */
64   GSList *current_uri;
65 
66   GSList *pictures;  /* For loading the pixbufs in the pictures */
67   GSList *current_picture;
68 
69   guint index;
70   guint n_files;
71 
72   gulong max_picture_size;
73   gulong max_video_size;
74   gboolean keep_file_extensions;
75   gboolean import_tags;
76   gboolean public_visibility;
77   gboolean family_visibility;
78   gboolean friend_visibility;
79   gboolean show_in_search;
80   gboolean send_location;
81   gboolean replace_date_posted;
82   FspLicense license;
83   FspSafetyLevel safety_level;
84   FspContentType content_type;
85 };
86 
87 G_DEFINE_TYPE (FrogrFileLoader, frogr_file_loader, G_TYPE_OBJECT)
88 
89 /* Signals */
90 enum {
91   FILE_LOADED,
92   FILES_LOADED,
93   N_SIGNALS
94 };
95 
96 static guint signals[N_SIGNALS] = { 0 };
97 
98 /* Prototypes */
99 
100 static void _update_status_and_progress (FrogrFileLoader *self);
101 static void _advance_to_next_file (FrogrFileLoader *self);
102 static void _load_current_file (FrogrFileLoader *self);
103 static void _load_current_file_cb (GObject *object,
104 				   GAsyncResult *res,
105 				   gpointer data);
106 
107 static gboolean _is_video_file (GFile *file);
108 static GdkPixbuf *_try_get_pixbuf_for_image (FrogrFileLoader *self,
109 					     GFile *file,
110 					     const gchar *contents,
111 					     gsize length);
112 static GdkPixbuf *_try_get_pixbuf_for_video (FrogrFileLoader *self,
113 					     GFile *file,
114 					     const gchar *contents,
115 					     gsize length);
116 
117 static gboolean get_gps_coordinate (ExifData *exif,
118                                     ExifTag   tag,
119                                     ExifTag   reftag,
120                                     gdouble  *coordinate);
121 static FrogrLocation *get_location_from_exif (ExifData *exif_data);
122 static FrogrPicture* _create_new_picture (FrogrFileLoader *self, GFile *file, GdkPixbuf *pixbuf, gboolean is_video);
123 static void _update_picture_with_exif_data (FrogrFileLoader *self,
124                                             const gchar *contents,
125                                             gsize length,
126                                             FrogrPicture *picture);
127 static gboolean _check_filesize_limits (FrogrFileLoader *self, FrogrPicture *picture);
128 
129 static gchar *remove_spaces_from_keyword (const gchar *keyword);
130 static gchar *import_tags_from_xmp_keywords (const char *buffer, size_t len);
131 static void _finish_task_and_self_destruct (FrogrFileLoader *self);
132 
133 /* Private API */
134 
135 static void
_update_status_and_progress(FrogrFileLoader * self)136 _update_status_and_progress (FrogrFileLoader *self)
137 {
138   g_autofree gchar *status_text = NULL;
139 
140   /* Update progress */
141   if (self->current_uri || self->current_picture)
142     status_text = g_strdup_printf (_("Loading files %d / %d"),
143                                    self->index, self->n_files);
144 
145   frogr_main_view_set_status_text (self->mainview, status_text);
146 }
147 
148 static void
_advance_to_next_file(FrogrFileLoader * self)149 _advance_to_next_file (FrogrFileLoader *self)
150 {
151   /* update internal status and check the next file */
152   if (self->loading_mode == LOADING_MODE_FROM_PICTURES)
153     self->current_picture = g_slist_next (self->current_picture);
154   else
155     self->current_uri = g_slist_next (self->current_uri);
156 
157   self->index++;
158 }
159 
160 static void
_load_current_file(FrogrFileLoader * self)161 _load_current_file (FrogrFileLoader *self)
162 {
163   const gchar *file_uri = NULL;
164 
165   /* Get the uri for the file first */
166   if (self->loading_mode == LOADING_MODE_FROM_URIS && self->current_uri)
167     file_uri = (const gchar *)self->current_uri->data;
168   else if (self->current_picture)
169     {
170       FrogrPicture *picture = FROGR_PICTURE (self->current_picture->data);
171       file_uri = frogr_picture_get_fileuri (picture);
172     }
173 
174   if (file_uri)
175     {
176       GFile *gfile = NULL;
177       g_autoptr(GFileInfo) file_info = NULL;
178       gboolean valid_mime = TRUE;
179 
180       /* Get file info (from this point on, use (file_info != NULL) as
181          a reliable way to know whether the file exists or not */
182       gfile = g_file_new_for_uri (file_uri);
183       file_info = g_file_query_info (gfile,
184                                      G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
185                                      G_FILE_QUERY_INFO_NONE,
186                                      NULL,
187                                      NULL);
188       const gchar *mime_type;
189       gint i;
190 
191       /* This can be NULL (e.g wrong parameter in the command line) */
192       if (file_info)
193         {
194           /* Check mimetype */
195           mime_type = g_file_info_get_content_type (file_info);
196           valid_mime = FALSE;
197 
198           if (mime_type)
199             {
200               const gchar * const *supported_mimetypes = frogr_util_get_supported_mimetypes ();
201               for (i = 0; supported_mimetypes[i]; i++)
202                 {
203                   if (g_str_equal (supported_mimetypes[i], mime_type))
204                     {
205                       valid_mime = TRUE;
206                       break;
207                     }
208                 }
209               DEBUG ("Mime detected: %s", mime_type);
210             }
211         }
212 
213       /* Asynchronously load the file if mime is valid */
214       if (file_info && valid_mime)
215         {
216           g_file_load_contents_async (gfile,
217                                       NULL,
218                                       _load_current_file_cb,
219                                       self);
220           DEBUG ("Adding file %s", file_uri);
221         }
222       else
223         {
224           _advance_to_next_file (self);
225           _load_current_file (self);
226         }
227     }
228   else
229     {
230       /* Update status and progress and finish */
231       _update_status_and_progress (self);
232       _finish_task_and_self_destruct (self);
233     }
234 }
235 
236 static void
_load_current_file_cb(GObject * object,GAsyncResult * res,gpointer data)237 _load_current_file_cb (GObject *object,
238                        GAsyncResult *res,
239                        gpointer data)
240 {
241   FrogrFileLoader *self = NULL;
242   FrogrPicture *picture = NULL;
243   g_autoptr(GFile) file = NULL;
244   g_autoptr(GError) error = NULL;
245   g_autofree gchar *contents = NULL;
246   gsize length = 0;
247   gboolean keep_going = FALSE;
248 
249   self = FROGR_FILE_LOADER (data);
250 
251   file = G_FILE (object);
252   if (g_file_load_contents_finish (file, res, &contents, &length, NULL, &error))
253     {
254       g_autoptr(GdkPixbuf) pixbuf = NULL;
255       gboolean is_video = FALSE;
256 
257       /* Load the pixbuf for the video or the image */
258       is_video = _is_video_file (file);
259       if (is_video)
260 	pixbuf = _try_get_pixbuf_for_video (self, file, contents, length);
261       else
262 	pixbuf = _try_get_pixbuf_for_image (self, file, contents, length);
263 
264       if (pixbuf)
265 	{
266 	  if (self->loading_mode == LOADING_MODE_FROM_PICTURES)
267 	    {
268 	      /* Just update the picture if we are not loading from URIs */
269 	      picture = FROGR_PICTURE (self->current_picture->data);
270 	      frogr_picture_set_pixbuf (picture, pixbuf);
271 	    }
272 	  else
273 	    {
274 	      picture = _create_new_picture (self, file, pixbuf, is_video);
275 	      _update_picture_with_exif_data (self, contents, length, picture);
276 	    }
277 	}
278     }
279   else
280     {
281       /* Not able to load contents */
282       g_autofree gchar *file_name = g_file_get_basename (file);
283       g_warning ("Not able to read contents from %s: %s",
284                  file_name,
285                  error->message);
286     }
287 
288   /* Update internal status */
289   _advance_to_next_file (self);
290 
291   /* Update status and progress */
292   _update_status_and_progress (self);
293 
294   /* We might not have a file loaded (e.g. unsupported format) */
295   if (picture)
296     {
297       /* Check if we must interrupt the process */
298       keep_going = _check_filesize_limits (self, picture);
299       if (keep_going)
300         g_signal_emit (self, signals[FILE_LOADED], 0, picture);
301 
302       /* We only unref the picture if it was created here */
303       if (self->loading_mode != LOADING_MODE_FROM_PICTURES)
304         g_object_unref (picture);
305     }
306 
307   /* Go for the next file, if needed */
308   if (keep_going)
309     _load_current_file (self);
310   else
311     _finish_task_and_self_destruct (self);
312 }
313 
314 static gboolean
_is_video_file(GFile * file)315 _is_video_file (GFile *file)
316 {
317   GFileInfo* file_info = NULL;
318   GError *error = NULL;
319   gboolean is_video = FALSE;
320 
321   file_info = g_file_query_info (file,
322                                  G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
323                                  G_FILE_QUERY_INFO_NONE,
324                                  NULL, &error);
325   if (!error)
326     {
327       const gchar *mime_type = NULL;
328       mime_type = g_file_info_get_content_type (file_info);
329       is_video = !g_str_has_prefix (mime_type, "image");
330     }
331   else
332     {
333       g_warning ("Not able to read file information: %s", error->message);
334       g_error_free (error);
335     }
336 
337   return is_video;
338 }
339 
340 static GdkPixbuf *
_try_get_pixbuf_for_image(FrogrFileLoader * self,GFile * file,const gchar * contents,gsize length)341 _try_get_pixbuf_for_image (FrogrFileLoader *self,
342 			   GFile *file,
343 			   const gchar *contents,
344 			   gsize length)
345 {
346   GdkPixbuf *pixbuf = NULL;
347   g_autofree gchar *path = NULL;
348   g_autoptr(GError) error = NULL;
349 
350   path = g_file_get_path (file);
351   pixbuf = frogr_util_get_pixbuf_from_image_contents ((const guchar *)contents, length,
352 						      IV_THUMB_WIDTH, IV_THUMB_HEIGHT, path, &error);
353   if (error)
354     {
355       g_autofree gchar *file_name = NULL;
356       g_autofree gchar *msg = NULL;
357 
358       file_name = g_file_get_basename (file);
359       msg = g_strdup_printf (_("Unable to load picture %s:\n%s"), file_name, error->message);
360       frogr_util_show_error_dialog (GTK_WINDOW (self->mainview), msg);
361     }
362 
363   return pixbuf;
364 }
365 
366 static GdkPixbuf *
_try_get_pixbuf_for_video(FrogrFileLoader * self,GFile * file,const gchar * contents,gsize length)367 _try_get_pixbuf_for_video (FrogrFileLoader *self,
368 			   GFile *file,
369 			   const gchar *contents,
370 			   gsize length)
371 {
372   GdkPixbuf *pixbuf = NULL;
373   g_autoptr(GError) error = NULL;
374 
375   pixbuf = frogr_util_get_pixbuf_for_video_file (file, IV_THUMB_WIDTH, IV_THUMB_HEIGHT, &error);
376   if (!pixbuf)
377     {
378       g_autofree gchar *file_name = NULL;
379       g_autofree gchar *msg = NULL;
380 
381       /* FIXME: We should integrate with gstreamer's codec
382 	 installer instead of just showing an error message to
383 	 the user, but this is "good enough" for now. */
384       file_name = g_file_get_basename (file);
385       msg = g_strdup_printf (_("Unable to load video %s\n"
386 			       "Please check that you have the right codec installed"), file_name);
387       frogr_util_show_error_dialog (GTK_WINDOW (self->mainview), msg);
388     }
389 
390   return pixbuf;
391 }
392 
393 /* This function was taken from tracker, licensed under the GNU Lesser
394  * General Public License Version 2.1 (Copyright 2009, Nokia Corp.) */
395 static gboolean
get_gps_coordinate(ExifData * exif,ExifTag tag,ExifTag reftag,gdouble * coordinate)396 get_gps_coordinate (ExifData *exif,
397                     ExifTag   tag,
398                     ExifTag   reftag,
399                     gdouble  *coordinate)
400 {
401   ExifEntry *entry = exif_data_get_entry (exif, tag);
402   ExifEntry *refentry = exif_data_get_entry (exif, reftag);
403 
404   g_return_val_if_fail (coordinate != NULL, FALSE);
405 
406   if (entry && refentry)
407     {
408       ExifByteOrder order;
409       ExifRational c1,c2,c3;
410       gfloat f;
411       gchar ref;
412 
413       order = exif_data_get_byte_order (exif);
414       c1 = exif_get_rational (entry->data, order);
415       c2 = exif_get_rational (entry->data+8, order);
416       c3 = exif_get_rational (entry->data+16, order);
417       ref = refentry->data[0];
418 
419       /* Avoid ridiculous values */
420       if (c1.denominator == 0 ||
421           c2.denominator == 0 ||
422           c3.denominator == 0)
423         {
424           return FALSE;
425         }
426 
427       f = (double)c1.numerator/c1.denominator+
428         (double)c2.numerator/(c2.denominator*60)+
429         (double)c3.numerator/(c3.denominator*60*60);
430 
431       if (ref == 'S' || ref == 'W')
432         {
433           f = -1 * f;
434         }
435 
436       *coordinate = f;
437       return TRUE;
438     }
439 
440   return FALSE;
441 }
442 
443 static FrogrLocation *
get_location_from_exif(ExifData * exif_data)444 get_location_from_exif (ExifData *exif_data)
445 {
446   FrogrLocation *location = NULL;
447   gdouble latitude;
448   gdouble longitude;
449   gboolean has_latitude;
450 
451   if (exif_data)
452     {
453       has_latitude = get_gps_coordinate (exif_data, EXIF_TAG_GPS_LATITUDE,
454                                          EXIF_TAG_GPS_LATITUDE_REF, &latitude);
455 
456       /* We need both latitude and longitude */
457       if (has_latitude && get_gps_coordinate (exif_data, EXIF_TAG_GPS_LONGITUDE,
458                                               EXIF_TAG_GPS_LONGITUDE_REF, &longitude))
459         {
460           location = frogr_location_new (latitude, longitude);
461         }
462     }
463 
464   return location;
465 }
466 
467 static FrogrPicture*
_create_new_picture(FrogrFileLoader * self,GFile * file,GdkPixbuf * pixbuf,gboolean is_video)468 _create_new_picture (FrogrFileLoader *self, GFile *file, GdkPixbuf *pixbuf, gboolean is_video)
469 {
470   FrogrPicture *picture = NULL;
471   g_autoptr(GFileInfo) file_info = NULL;
472   g_autofree gchar *file_name = NULL;
473   g_autofree gchar *file_uri = NULL;
474   guint64 filesize = 0;
475   g_autoptr(GError) error = NULL;
476 
477   /* Gather needed information */
478   file_info = g_file_query_info (file,
479                                  G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME
480                                  "," G_FILE_ATTRIBUTE_STANDARD_SIZE,
481                                  G_FILE_QUERY_INFO_NONE,
482                                  NULL, &error);
483   if (!error)
484     {
485       file_name = g_strdup (g_file_info_get_display_name (file_info));
486       filesize = g_file_info_get_size (file_info);
487     }
488   else
489     {
490       g_warning ("Not able to read file information: %s", error->message);
491 
492       /* Fallback if g_file_query_info() failed */
493       file_name = g_file_get_basename (file);
494     }
495 
496   if (!self->keep_file_extensions)
497     {
498       gchar *extension_dot = NULL;
499 
500       /* Remove extension if present */
501       extension_dot = g_strrstr (file_name, ".");
502       if (extension_dot)
503         *extension_dot = '\0';
504     }
505 
506   /* Build the FrogrPicture */
507   file_uri = g_file_get_uri (file);
508   picture = frogr_picture_new (file_uri,
509                                 file_name,
510                                 self->public_visibility,
511                                 self->family_visibility,
512                                 self->friend_visibility,
513                                 is_video);
514 
515   frogr_picture_set_show_in_search (picture, self->show_in_search);
516   frogr_picture_set_send_location (picture, self->send_location);
517   frogr_picture_set_replace_date_posted (picture, self->replace_date_posted);
518   frogr_picture_set_license (picture, self->license);
519   frogr_picture_set_content_type (picture, self->content_type);
520   frogr_picture_set_safety_level (picture, self->safety_level);
521   frogr_picture_set_pixbuf (picture, pixbuf);
522 
523   /* FrogrPicture stores the size in KB */
524   frogr_picture_set_filesize (picture, filesize / 1024);
525 
526   return picture;
527 }
528 
529 static void
_update_picture_with_exif_data(FrogrFileLoader * self,const gchar * contents,gsize length,FrogrPicture * picture)530 _update_picture_with_exif_data (FrogrFileLoader *self,
531                                 const gchar *contents,
532                                 gsize length,
533                                 FrogrPicture *picture)
534 {
535   ExifLoader *exif_loader = NULL;
536   ExifData *exif_data = NULL;
537 
538   /* Set date and time from exif data, if present */
539   exif_loader = exif_loader_new();
540   exif_loader_write (exif_loader, (unsigned char *) contents, length);
541 
542   exif_data = exif_loader_get_data (exif_loader);
543   if (exif_data)
544     {
545       g_autoptr(FrogrLocation) location = NULL;
546       ExifEntry *exif_entry = NULL;
547 
548       /* Date and time for picture taken */
549       exif_entry = exif_data_get_entry (exif_data, EXIF_TAG_DATE_TIME_ORIGINAL);
550       if (exif_entry)
551         {
552           if (exif_entry->format == EXIF_FORMAT_ASCII)
553             {
554               g_autofree gchar *value = g_new0 (char, 20);
555               exif_entry_get_value (exif_entry, value, 20);
556 
557               frogr_picture_set_datetime (picture, value);
558             }
559           else
560             g_warning ("Found DateTime exif tag of invalid type");
561         }
562 
563       /* Import tags from XMP metadata, if required */
564       if (self->import_tags)
565         {
566           g_autofree gchar *imported_tags = NULL;
567 
568           imported_tags = import_tags_from_xmp_keywords (contents, length);
569           if (imported_tags)
570             frogr_picture_set_tags (picture, imported_tags);
571         }
572 
573       /* GPS coordinates */
574       location = get_location_from_exif (exif_data);
575       if (location != NULL)
576         {
577           /* frogr_picture_set_location takes ownership of location */
578           frogr_picture_set_location (picture, location);
579         }
580       exif_data_unref (exif_data);
581     }
582 
583   exif_loader_unref (exif_loader);
584 }
585 
586 static gboolean
_check_filesize_limits(FrogrFileLoader * self,FrogrPicture * picture)587 _check_filesize_limits (FrogrFileLoader *self, FrogrPicture *picture)
588 {
589   gulong picture_filesize = 0;
590   gulong max_filesize = 0;
591   gboolean keep_going = TRUE;
592 
593   max_filesize = frogr_picture_is_video (picture) ? self->max_video_size : self->max_picture_size;
594   picture_filesize = frogr_picture_get_filesize (picture);
595 
596   if (picture_filesize > max_filesize)
597     {
598       g_autofree gchar *msg = NULL;
599 
600       /* First %s is the title of the picture (filename of the file by
601          default). Second %s is the max allowed size for a picture to be
602          uploaded to flickr (different for free and PRO accounts). */
603       msg = g_strdup_printf (_("Can't load file %s:\nSize of file is bigger "
604                                "than the maximum allowed for this account (%s)"),
605                              frogr_picture_get_title (picture),
606                              frogr_util_get_datasize_string (max_filesize));
607 
608       frogr_util_show_error_dialog (GTK_WINDOW (self->mainview), msg);
609 
610       keep_going = FALSE;
611     }
612 
613   return keep_going;
614 }
615 
616 static gchar *
remove_spaces_from_keyword(const gchar * keyword)617 remove_spaces_from_keyword (const gchar *keyword)
618 {
619   gchar *new_keyword = NULL;
620 
621   if (keyword)
622     {
623       int i = 0;
624       int j = 0;
625 
626       new_keyword = g_new0 (gchar, strlen(keyword) + 1);
627       for (i = 0; keyword[i] != '\0'; i++)
628         {
629           if (keyword[i] != ' ')
630             new_keyword[j++] = keyword[i];
631         }
632       new_keyword[j] = '\0';
633     }
634 
635   return new_keyword;
636 }
637 
638 static gchar *
import_tags_from_xmp_keywords(const char * buffer,size_t len)639 import_tags_from_xmp_keywords (const char *buffer, size_t len)
640 {
641   const gchar *keywords_start = NULL;
642   const gchar *keywords_end = NULL;
643   g_autofree gchar *keywords_string = NULL;
644   gchar *start = NULL;
645   gchar *end = NULL;
646   gchar *result = NULL;
647   int i;
648 
649   /* Look for the beginning of the XMP data interesting for as if
650      present, that is, the keywords (aka the 'tags') */
651   for (i = 0; i < len && !keywords_start; i++)
652     {
653       if (buffer[i] != '<')
654         continue;
655 
656       if (!strncmp (&buffer[i], "<dc:subject", 11))
657         keywords_start = &buffer[i];
658     }
659 
660   /* Find the end of the interesting XMP data, if found */
661   if (!keywords_start)
662     return NULL;
663 
664   keywords_end = g_strrstr (keywords_start, "</dc:subject>");
665   if (!keywords_end)
666     return NULL;
667 
668   keywords_string = g_strndup (keywords_start, keywords_end - keywords_start);
669 
670   /* Remove extra not-needed stuff in the string */
671   start = g_strstr_len (keywords_string, -1, "<rdf:li");
672   end = g_strrstr (keywords_string, "</rdf:li>");
673   if (start && end)
674     {
675       g_auto(GStrv) keywords = NULL;
676       gchar *keyword = NULL;
677 
678       /* Get an array of strings with all the keywords */
679       end[0] = '\0';
680       keywords = g_regex_split_simple ("<.?rdf:li(!>)*>", start,
681                                        G_REGEX_DOTALL | G_REGEX_RAW, 0);
682 
683       /* Remove spaces to normalize to flickr tags */
684       for (i = 0; keywords[i]; i++)
685         {
686           keyword = keywords[i];
687           keywords[i] = remove_spaces_from_keyword (keyword);
688           g_free (keyword);
689         }
690 
691       result = g_strjoinv (" ", keywords);
692     }
693 
694   return result;
695 }
696 
697 static void
_finish_task_and_self_destruct(FrogrFileLoader * self)698 _finish_task_and_self_destruct (FrogrFileLoader *self)
699 {
700   g_signal_emit (self, signals[FILES_LOADED], 0);
701   g_object_unref (self);
702 }
703 
704 static void
_frogr_file_loader_dispose(GObject * object)705 _frogr_file_loader_dispose (GObject* object)
706 {
707   FrogrFileLoader *self = FROGR_FILE_LOADER (object);
708   g_clear_object (&self->mainview);
709   g_clear_object (&self->controller);
710   G_OBJECT_CLASS (frogr_file_loader_parent_class)->dispose(object);
711 }
712 
713 static void
_frogr_file_loader_finalize(GObject * object)714 _frogr_file_loader_finalize (GObject* object)
715 {
716   FrogrFileLoader *self = FROGR_FILE_LOADER (object);
717 
718   /* Free */
719   g_slist_free_full (self->file_uris, g_free);
720 
721   G_OBJECT_CLASS (frogr_file_loader_parent_class)->finalize(object);
722 }
723 
724 static void
frogr_file_loader_class_init(FrogrFileLoaderClass * klass)725 frogr_file_loader_class_init(FrogrFileLoaderClass *klass)
726 {
727   GObjectClass *obj_class = G_OBJECT_CLASS(klass);
728 
729   obj_class->dispose = _frogr_file_loader_dispose;
730   obj_class->finalize = _frogr_file_loader_finalize;
731 
732   signals[FILE_LOADED] =
733     g_signal_new ("file-loaded",
734                   G_OBJECT_CLASS_TYPE (klass),
735                   G_SIGNAL_RUN_FIRST,
736                   0, NULL, NULL,
737                   g_cclosure_marshal_VOID__OBJECT,
738                   G_TYPE_NONE, 1, FROGR_TYPE_PICTURE);
739 
740   signals[FILES_LOADED] =
741     g_signal_new ("files-loaded",
742                   G_OBJECT_CLASS_TYPE (klass),
743                   G_SIGNAL_RUN_FIRST,
744                   0, NULL, NULL,
745                   g_cclosure_marshal_VOID__VOID,
746                   G_TYPE_NONE, 0);
747 }
748 
749 static void
frogr_file_loader_init(FrogrFileLoader * self)750 frogr_file_loader_init (FrogrFileLoader *self)
751 {
752   FrogrConfig *config = frogr_config_get_instance ();
753 
754   /* Init private data */
755 
756   /* We need the controller to get the main window */
757   self->controller = g_object_ref (frogr_controller_get_instance ());
758   self->mainview = g_object_ref (frogr_controller_get_main_view (self->controller));
759 
760   /* Initialize values from frogr configuration */
761   self->max_picture_size = G_MAXULONG;
762   self->max_video_size = G_MAXULONG;
763   self->keep_file_extensions = frogr_config_get_keep_file_extensions (config);
764   self->import_tags = frogr_config_get_import_tags_from_metadata (config);
765   self->public_visibility = frogr_config_get_default_public (config);
766   self->family_visibility = frogr_config_get_default_family (config);
767   self->friend_visibility = frogr_config_get_default_friend (config);
768   self->show_in_search = frogr_config_get_default_show_in_search (config);
769   self->send_location = frogr_config_get_default_send_geolocation_data (config);
770   self->replace_date_posted = frogr_config_get_default_replace_date_posted (config);
771   self->license = frogr_config_get_default_license (config);
772   self->safety_level = frogr_config_get_default_safety_level (config);
773   self->content_type = frogr_config_get_default_content_type (config);
774 
775   /* Init the rest of private data */
776   self->file_uris = NULL;
777   self->pictures = NULL;
778   self->current_uri = NULL;
779   self->current_picture = NULL;
780   self->index = -1;
781   self->n_files = 0;
782 }
783 
784 /* Public API */
785 
786 FrogrFileLoader *
frogr_file_loader_new_from_uris(GSList * file_uris,gulong max_picture_size,gulong max_video_size)787 frogr_file_loader_new_from_uris (GSList *file_uris, gulong max_picture_size, gulong max_video_size)
788 {
789   FrogrFileLoader *self = NULL;
790 
791   g_return_val_if_fail (file_uris, NULL);
792 
793   self = FROGR_FILE_LOADER (g_object_new(FROGR_TYPE_FILE_LOADER, NULL));
794 
795   self->loading_mode = LOADING_MODE_FROM_URIS;
796   self->file_uris = file_uris;
797   self->current_uri = self->file_uris;
798   self->index = 0;
799   self->n_files = g_slist_length (self->file_uris);
800   self->max_picture_size = max_picture_size;
801   self->max_video_size = max_video_size;
802 
803   return self;
804 }
805 
806 FrogrFileLoader *
frogr_file_loader_new_from_pictures(GSList * pictures)807 frogr_file_loader_new_from_pictures (GSList *pictures)
808 {
809   FrogrFileLoader *self = NULL;
810 
811   g_return_val_if_fail (pictures, NULL);
812 
813   self = FROGR_FILE_LOADER (g_object_new(FROGR_TYPE_FILE_LOADER, NULL));
814 
815   self->loading_mode = LOADING_MODE_FROM_PICTURES;
816   self->pictures = pictures;
817   self->current_picture = pictures;
818   self->index = 0;
819   self->n_files = g_slist_length (self->pictures);
820 
821   return self;
822 }
823 
824 void
frogr_file_loader_load(FrogrFileLoader * self)825 frogr_file_loader_load (FrogrFileLoader *self)
826 {
827   g_return_if_fail (FROGR_IS_FILE_LOADER (self));
828 
829   /* Check first whether there's something to load */
830   if (!self->n_files)
831     return;
832 
833   /* Update status and progress */
834   _update_status_and_progress (self);
835 
836   /* Trigger the asynchronous process */
837   _load_current_file (self);
838 }
839