1 /*
2  * Photos - access, organize and share your photos on GNOME
3  * Copyright © 2014 – 2015 Pranav Kant
4  * Copyright © 2012 – 2020 Red Hat, Inc.
5  * Copyright © 2016 Umang Jain
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 /* Based on code from:
22  *   + Documents
23  */
24 
25 
26 #include "config.h"
27 
28 #include <stdarg.h>
29 #include <string.h>
30 
31 #include <dazzle.h>
32 #include <gdk/gdk.h>
33 #include <gegl-plugin.h>
34 #include <gexiv2/gexiv2.h>
35 #include <gio/gio.h>
36 #include <glib.h>
37 #include <glib/gi18n.h>
38 #include <libgd/gd.h>
39 #include <tracker-sparql.h>
40 
41 #include "photos-application.h"
42 #include "photos-base-item.h"
43 #include "photos-collection-icon-watcher.h"
44 #include "photos-debug.h"
45 #include "photos-error.h"
46 #include "photos-filterable.h"
47 #include "photos-gegl.h"
48 #include "photos-glib.h"
49 #include "photos-local-item.h"
50 #include "photos-pipeline.h"
51 #include "photos-print-notification.h"
52 #include "photos-print-operation.h"
53 #include "photos-quarks.h"
54 #include "photos-query.h"
55 #include "photos-search-context.h"
56 #include "photos-single-item-job.h"
57 #include "photos-utils.h"
58 
59 
60 struct _PhotosBaseItemPrivate
61 {
62   cairo_surface_t *surface;
63   GAppInfo *default_app;
64   GCancellable *cancellable;
65   GdkPixbuf *original_icon;
66   GeglBuffer *preview_source_buffer;
67   GeglNode *buffer_source;
68   GeglNode *edit_graph;
69   GeglProcessor *processor;
70   GMutex mutex_download;
71   GMutex mutex_save_metadata;
72   GQuark equipment;
73   GQuark flash;
74   GQuark orientation;
75   PhotosCollectionIconWatcher *watcher;
76   TrackerSparqlCursor *cursor;
77   gboolean collection;
78   gboolean failed_thumbnailing;
79   gboolean favorite;
80   gchar *author;
81   gchar *default_app_name;
82   gchar *filename;
83   gchar *id;
84   gchar *identifier;
85   gchar *location;
86   gchar *mime_type;
87   gchar *name;
88   gchar *name_fallback;
89   gchar *rdf_type;
90   gchar *resource_urn;
91   gchar *thumb_path;
92   gchar *type_description;
93   gchar *uri;
94   gdouble exposure_time;
95   gdouble fnumber;
96   gdouble focal_length;
97   gdouble iso_speed;
98   gint64 ctime;
99   gint64 height;
100   gint64 mtime;
101   gint64 width;
102   guint busy_count;
103 };
104 
105 enum
106 {
107   PROP_0,
108   PROP_CURSOR,
109   PROP_FAILED_THUMBNAILING,
110   PROP_ICON,
111   PROP_ID,
112   PROP_MTIME,
113   PROP_PRIMARY_TEXT,
114   PROP_PULSE,
115   PROP_SECONDARY_TEXT,
116   PROP_URI
117 };
118 
119 enum
120 {
121   INFO_UPDATED,
122   LAST_SIGNAL
123 };
124 
125 static guint signals[LAST_SIGNAL] = { 0 };
126 
127 static void photos_base_item_filterable_iface_init (PhotosFilterableInterface *iface);
128 static void photos_base_item_main_box_item_iface_init (GdMainBoxItemInterface *iface);
129 
130 
131 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (PhotosBaseItem, photos_base_item, G_TYPE_OBJECT,
132                                   G_ADD_PRIVATE (PhotosBaseItem)
133                                   G_IMPLEMENT_INTERFACE (GD_TYPE_MAIN_BOX_ITEM,
134                                                          photos_base_item_main_box_item_iface_init)
135                                   G_IMPLEMENT_INTERFACE (PHOTOS_TYPE_FILTERABLE,
136                                                          photos_base_item_filterable_iface_init));
137 DZL_DEFINE_COUNTER (instances, "PhotosBaseItem", "Instances", "Number of PhotosBaseItem instances")
138 
139 
140 typedef struct _PhotosBaseItemMetadataAddSharedData PhotosBaseItemMetadataAddSharedData;
141 typedef struct _PhotosBaseItemQueryInfoData PhotosBaseItemQueryInfoData;
142 typedef struct _PhotosBaseItemSaveData PhotosBaseItemSaveData;
143 typedef struct _PhotosBaseItemSaveBufferData PhotosBaseItemSaveBufferData;
144 typedef struct _PhotosBaseItemSaveToFileData PhotosBaseItemSaveToFileData;
145 typedef struct _PhotosBaseItemSaveToStreamData PhotosBaseItemSaveToStreamData;
146 
147 struct _PhotosBaseItemMetadataAddSharedData
148 {
149   gchar *account_identity;
150   gchar *provider_type;
151   gchar *shared_id;
152 };
153 
154 struct _PhotosBaseItemQueryInfoData
155 {
156   GFileQueryInfoFlags flags;
157   gchar *attributes;
158 };
159 
160 struct _PhotosBaseItemSaveData
161 {
162   GFile *dir;
163   GFile *unique_file;
164   GeglBuffer *buffer;
165   gchar *type;
166   gdouble zoom;
167 };
168 
169 struct _PhotosBaseItemSaveBufferData
170 {
171   GFile *file;
172   GFileOutputStream *stream;
173 };
174 
175 struct _PhotosBaseItemSaveToFileData
176 {
177   GFile *file;
178   GFileCreateFlags flags;
179   GeglBuffer *buffer;
180   gdouble zoom;
181 };
182 
183 struct _PhotosBaseItemSaveToStreamData
184 {
185   GFile *file;
186   GFileIOStream *iostream;
187   GOutputStream *ostream;
188   gdouble zoom;
189 };
190 
191 static DzlTaskCache *pipeline_cache;
192 static GdkPixbuf *failed_icon;
193 static GdkPixbuf *thumbnailing_icon;
194 static GThreadPool *create_thumbnail_pool;
195 static const gint PIXEL_SIZES[] = {2048, 1024};
196 
197 enum
198 {
199   THUMBNAIL_GENERATION = 0
200 };
201 
202 
203 static void photos_base_item_populate_from_cursor (PhotosBaseItem *self, TrackerSparqlCursor *cursor);
204 
205 
206 static PhotosBaseItemMetadataAddSharedData *
photos_base_item_metadata_add_shared_data_new(const gchar * provider_type,const gchar * account_identity,const gchar * shared_id)207 photos_base_item_metadata_add_shared_data_new (const gchar *provider_type,
208                                                const gchar *account_identity,
209                                                const gchar *shared_id)
210 {
211   PhotosBaseItemMetadataAddSharedData *data;
212 
213   data = g_slice_new0 (PhotosBaseItemMetadataAddSharedData);
214   data->account_identity = g_strdup (account_identity);
215   data->provider_type = g_strdup (provider_type);
216   data->shared_id = g_strdup (shared_id);
217 
218   return data;
219 }
220 
221 
222 static void
photos_base_item_metadata_add_shared_data_free(PhotosBaseItemMetadataAddSharedData * data)223 photos_base_item_metadata_add_shared_data_free (PhotosBaseItemMetadataAddSharedData *data)
224 {
225   g_free (data->account_identity);
226   g_free (data->provider_type);
227   g_free (data->shared_id);
228   g_slice_free (PhotosBaseItemMetadataAddSharedData, data);
229 }
230 
231 
232 static PhotosBaseItemQueryInfoData *
photos_base_item_query_info_data_new(const gchar * attributes,GFileQueryInfoFlags flags)233 photos_base_item_query_info_data_new (const gchar *attributes, GFileQueryInfoFlags flags)
234 {
235   PhotosBaseItemQueryInfoData *data;
236 
237   data = g_slice_new0 (PhotosBaseItemQueryInfoData);
238   data->flags = flags;
239   data->attributes = g_strdup (attributes);
240 
241   return data;
242 }
243 
244 
245 static void
photos_base_item_query_info_data_free(PhotosBaseItemQueryInfoData * data)246 photos_base_item_query_info_data_free (PhotosBaseItemQueryInfoData *data)
247 {
248   g_free (data->attributes);
249   g_slice_free (PhotosBaseItemQueryInfoData, data);
250 }
251 
252 
253 static PhotosBaseItemSaveData *
photos_base_item_save_data_new(GFile * dir,GeglBuffer * buffer,const gchar * type,gdouble zoom)254 photos_base_item_save_data_new (GFile *dir, GeglBuffer *buffer, const gchar *type, gdouble zoom)
255 {
256   PhotosBaseItemSaveData *data;
257 
258   data = g_slice_new0 (PhotosBaseItemSaveData);
259 
260   if (dir != NULL)
261     data->dir = g_object_ref (dir);
262 
263   if (buffer != NULL)
264     data->buffer = g_object_ref (buffer);
265 
266   data->type = g_strdup (type);
267   data->zoom = zoom;
268 
269   return data;
270 }
271 
272 
273 static void
photos_base_item_save_data_free(PhotosBaseItemSaveData * data)274 photos_base_item_save_data_free (PhotosBaseItemSaveData *data)
275 {
276   g_clear_object (&data->dir);
277   g_clear_object (&data->unique_file);
278   g_clear_object (&data->buffer);
279   g_free (data->type);
280   g_slice_free (PhotosBaseItemSaveData, data);
281 }
282 
283 
284 static PhotosBaseItemSaveBufferData *
photos_base_item_save_buffer_data_new(GFile * file,GFileOutputStream * stream)285 photos_base_item_save_buffer_data_new (GFile *file, GFileOutputStream *stream)
286 {
287   PhotosBaseItemSaveBufferData *data;
288 
289   data = g_slice_new0 (PhotosBaseItemSaveBufferData);
290   data->file = g_object_ref (file);
291   data->stream = g_object_ref (stream);
292 
293   return data;
294 }
295 
296 
297 static void
photos_base_item_save_buffer_data_free(PhotosBaseItemSaveBufferData * data)298 photos_base_item_save_buffer_data_free (PhotosBaseItemSaveBufferData *data)
299 {
300   g_clear_object (&data->file);
301   g_clear_object (&data->stream);
302   g_slice_free (PhotosBaseItemSaveBufferData, data);
303 }
304 
305 
306 static PhotosBaseItemSaveToFileData *
photos_base_item_save_to_file_data_new(GFile * file,GFileCreateFlags flags,gdouble zoom)307 photos_base_item_save_to_file_data_new (GFile *file, GFileCreateFlags flags, gdouble zoom)
308 {
309   PhotosBaseItemSaveToFileData *data;
310 
311   data = g_slice_new0 (PhotosBaseItemSaveToFileData);
312   data->file = g_object_ref (file);
313   data->flags = flags;
314   data->zoom = zoom;
315 
316   return data;
317 }
318 
319 
320 static void
photos_base_item_save_to_file_data_free(PhotosBaseItemSaveToFileData * data)321 photos_base_item_save_to_file_data_free (PhotosBaseItemSaveToFileData *data)
322 {
323   g_clear_object (&data->file);
324   g_clear_object (&data->buffer);
325   g_slice_free (PhotosBaseItemSaveToFileData, data);
326 }
327 
328 
329 static PhotosBaseItemSaveToStreamData *
photos_base_item_save_to_stream_data_new(GOutputStream * ostream,gdouble zoom)330 photos_base_item_save_to_stream_data_new (GOutputStream *ostream, gdouble zoom)
331 {
332   PhotosBaseItemSaveToStreamData *data;
333 
334   data = g_slice_new0 (PhotosBaseItemSaveToStreamData);
335   data->ostream = g_object_ref (ostream);
336   data->zoom = zoom;
337 
338   return data;
339 }
340 
341 
342 static void
photos_base_item_save_to_stream_data_free(PhotosBaseItemSaveToStreamData * data)343 photos_base_item_save_to_stream_data_free (PhotosBaseItemSaveToStreamData *data)
344 {
345   g_clear_object (&data->file);
346   g_clear_object (&data->iostream);
347   g_clear_object (&data->ostream);
348   g_slice_free (PhotosBaseItemSaveToStreamData, data);
349 }
350 
351 
352 static GdkPixbuf *
photos_base_item_create_placeholder_icon(const gchar * icon_name)353 photos_base_item_create_placeholder_icon (const gchar *icon_name)
354 {
355   GApplication *app;
356   GdkPixbuf *ret_val = NULL;
357   gint icon_size;
358   gint scale;
359 
360   app = g_application_get_default ();
361   icon_size = photos_utils_get_icon_size_unscaled ();
362   scale = photos_application_get_scale_factor (PHOTOS_APPLICATION (app));
363   ret_val = photos_utils_create_placeholder_icon_for_scale (icon_name, icon_size, scale);
364   return ret_val;
365 }
366 
367 
368 static GIcon *
photos_base_item_create_symbolic_emblem(const gchar * name,gint scale)369 photos_base_item_create_symbolic_emblem (const gchar *name, gint scale)
370 {
371   GIcon *pix;
372   gint size;
373 
374   size = photos_utils_get_icon_size_unscaled ();
375   pix = photos_utils_create_symbolic_icon_for_scale (name, size, scale);
376   if (pix == NULL)
377     pix = g_themed_icon_new (name);
378 
379   return pix;
380 }
381 
382 
383 static void
photos_base_item_check_effects_and_update_info(PhotosBaseItem * self)384 photos_base_item_check_effects_and_update_info (PhotosBaseItem *self)
385 {
386   PhotosBaseItemPrivate *priv;
387   GApplication *app;
388   GIcon *pix;
389   GList *emblem_icons = NULL;
390   GList *windows;
391   g_autoptr (GdkPixbuf) emblemed_pixbuf = NULL;
392   GdkWindow *window = NULL;
393   gint scale;
394 
395   priv = photos_base_item_get_instance_private (self);
396 
397   if (priv->original_icon == NULL)
398     goto out;
399 
400   app = g_application_get_default ();
401   scale = photos_application_get_scale_factor (PHOTOS_APPLICATION (app));
402 
403   emblemed_pixbuf = g_object_ref (priv->original_icon);
404 
405   if (priv->favorite)
406     {
407       pix = photos_base_item_create_symbolic_emblem ("starred", scale);
408       emblem_icons = g_list_prepend (emblem_icons, pix);
409     }
410 
411   if (emblem_icons != NULL)
412     {
413       g_autoptr (GIcon) emblemed_icon = NULL;
414       GList *l;
415       g_autoptr (GtkIconInfo) icon_info = NULL;
416       GtkIconTheme *theme;
417       gint height;
418       gint size;
419       gint width;
420 
421       emblem_icons = g_list_reverse (emblem_icons);
422       emblemed_icon = g_emblemed_icon_new (G_ICON (priv->original_icon), NULL);
423       for (l = emblem_icons; l != NULL; l = g_list_next (l))
424         {
425           g_autoptr (GEmblem) emblem = NULL;
426           GIcon *emblem_icon = G_ICON (l->data);
427 
428           emblem = g_emblem_new (emblem_icon);
429           g_emblemed_icon_add_emblem (G_EMBLEMED_ICON (emblemed_icon), emblem);
430         }
431 
432       theme = gtk_icon_theme_get_default ();
433 
434       width = gdk_pixbuf_get_width (priv->original_icon);
435       height = gdk_pixbuf_get_height (priv->original_icon);
436       size = (width > height) ? width : height;
437 
438       icon_info = gtk_icon_theme_lookup_by_gicon (theme, emblemed_icon, size, GTK_ICON_LOOKUP_FORCE_SIZE);
439 
440       if (icon_info != NULL)
441         {
442           g_autoptr (GError) error = NULL;
443           GdkPixbuf *tmp;
444 
445           tmp = gtk_icon_info_load_icon (icon_info, &error);
446           if (error != NULL)
447             {
448               g_warning ("Unable to render the emblem: %s", error->message);
449             }
450           else
451             {
452               g_object_unref (emblemed_pixbuf);
453               emblemed_pixbuf = tmp;
454             }
455         }
456     }
457 
458   g_clear_pointer (&priv->surface, cairo_surface_destroy);
459 
460   windows = gtk_application_get_windows (GTK_APPLICATION (app));
461   if (windows != NULL)
462     window = gtk_widget_get_window (GTK_WIDGET (windows->data));
463 
464   priv->surface = gdk_cairo_surface_create_from_pixbuf (emblemed_pixbuf, scale, window);
465 
466   g_object_notify (G_OBJECT (self), "icon");
467   g_signal_emit (self, signals[INFO_UPDATED], 0);
468 
469  out:
470   g_list_free_full (emblem_icons, g_object_unref);
471 }
472 
473 
474 static void
photos_base_item_clear_pixels(PhotosBaseItem * self)475 photos_base_item_clear_pixels (PhotosBaseItem *self)
476 {
477   PhotosBaseItemPrivate *priv;
478 
479   priv = photos_base_item_get_instance_private (self);
480 
481   priv->buffer_source = NULL;
482   dzl_task_cache_evict (pipeline_cache, self);
483 
484   g_clear_object (&priv->edit_graph);
485   g_clear_object (&priv->preview_source_buffer);
486   g_clear_object (&priv->processor);
487 }
488 
489 
490 static void
photos_base_item_create_thumbnail_in_thread_func(gpointer data,gpointer user_data)491 photos_base_item_create_thumbnail_in_thread_func (gpointer data, gpointer user_data)
492 {
493   g_autoptr (GTask) task = G_TASK (data);
494   PhotosBaseItem *self;
495   GCancellable *cancellable;
496 
497   self = PHOTOS_BASE_ITEM (g_task_get_source_object (task));
498   cancellable = g_task_get_cancellable (task);
499 
500   {
501     g_autoptr (GError) error = NULL;
502 
503     if (!PHOTOS_BASE_ITEM_GET_CLASS (self)->create_thumbnail (self, cancellable, &error))
504       {
505         if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
506           {
507             g_autoptr (GFile) file = NULL;
508             g_autofree gchar *path = NULL;
509 
510             path = photos_base_item_create_thumbnail_path (self);
511             file = g_file_new_for_path (path);
512             g_file_delete (file, NULL, NULL);
513           }
514 
515         g_task_return_error (task, g_steal_pointer (&error));
516         goto out;
517       }
518   }
519 
520   g_task_return_boolean (task, TRUE);
521 
522  out:
523   return;
524 }
525 
526 
527 static gint
photos_base_item_create_thumbnail_sort_func(gconstpointer a,gconstpointer b,gpointer user_data)528 photos_base_item_create_thumbnail_sort_func (gconstpointer a, gconstpointer b, gpointer user_data)
529 {
530   GTask *task_a = G_TASK (a);
531   GTask *task_b = G_TASK (b);
532   PhotosBaseItem *item_a;
533   PhotosBaseItem *item_b;
534   gint ret_val = 0;
535 
536   item_a = PHOTOS_BASE_ITEM (g_task_get_source_object (task_a));
537   item_b = PHOTOS_BASE_ITEM (g_task_get_source_object (task_b));
538 
539   if (PHOTOS_IS_LOCAL_ITEM (item_a))
540     ret_val = -1;
541   else if (PHOTOS_IS_LOCAL_ITEM (item_b))
542     ret_val = 1;
543 
544   return ret_val;
545 }
546 
547 
548 static void
photos_base_item_create_thumbnail_async(PhotosBaseItem * self,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)549 photos_base_item_create_thumbnail_async (PhotosBaseItem *self,
550                                          GCancellable *cancellable,
551                                          GAsyncReadyCallback callback,
552                                          gpointer user_data)
553 {
554   g_autoptr (GTask) task = NULL;
555 
556   task = g_task_new (self, cancellable, callback, user_data);
557   g_task_set_source_tag (task, photos_base_item_create_thumbnail_async);
558 
559   g_thread_pool_push (create_thumbnail_pool, g_object_ref (task), NULL);
560 }
561 
562 
563 static gboolean
photos_base_item_create_thumbnail_finish(PhotosBaseItem * self,GAsyncResult * res,GError ** error)564 photos_base_item_create_thumbnail_finish (PhotosBaseItem *self, GAsyncResult *res, GError **error)
565 {
566   GTask *task;
567 
568   g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
569   task = G_TASK (res);
570 
571   g_return_val_if_fail (g_task_get_source_tag (task) == photos_base_item_create_thumbnail_async, FALSE);
572   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
573 
574   return g_task_propagate_boolean (task, error);
575 }
576 
577 
578 static void
photos_base_item_set_original_icon(PhotosBaseItem * self,GdkPixbuf * icon)579 photos_base_item_set_original_icon (PhotosBaseItem *self, GdkPixbuf *icon)
580 {
581   PhotosBaseItemPrivate *priv;
582 
583   priv = photos_base_item_get_instance_private (self);
584 
585   if (icon != NULL)
586     g_set_object (&priv->original_icon, icon);
587 
588   photos_base_item_check_effects_and_update_info (self);
589 }
590 
591 
592 static void
photos_base_item_set_failed_icon(PhotosBaseItem * self)593 photos_base_item_set_failed_icon (PhotosBaseItem *self)
594 {
595   if (failed_icon == NULL)
596     failed_icon = photos_base_item_create_placeholder_icon ("image-x-generic-symbolic");
597 
598   photos_base_item_set_original_icon (self, failed_icon);
599 }
600 
601 
602 static void
photos_base_item_set_thumbnailing_icon(PhotosBaseItem * self)603 photos_base_item_set_thumbnailing_icon (PhotosBaseItem *self)
604 {
605   if (thumbnailing_icon == NULL)
606     thumbnailing_icon = photos_base_item_create_placeholder_icon ("content-loading-symbolic");
607 
608   photos_base_item_set_original_icon (self, thumbnailing_icon);
609 }
610 
611 
612 static void
photos_base_item_icon_updated(PhotosBaseItem * self,GIcon * icon)613 photos_base_item_icon_updated (PhotosBaseItem *self, GIcon *icon)
614 {
615   if (icon == NULL)
616     return;
617 
618   photos_base_item_set_original_icon (self, GDK_PIXBUF (icon));
619 }
620 
621 
622 static void
photos_base_item_refresh_collection_icon(PhotosBaseItem * self)623 photos_base_item_refresh_collection_icon (PhotosBaseItem *self)
624 {
625   PhotosBaseItemPrivate *priv;
626 
627   priv = photos_base_item_get_instance_private (self);
628 
629   if (priv->watcher == NULL)
630     {
631       priv->watcher = photos_collection_icon_watcher_new (self);
632       g_signal_connect_swapped (priv->watcher, "icon-updated", G_CALLBACK (photos_base_item_icon_updated), self);
633     }
634   else
635     photos_collection_icon_watcher_refresh (priv->watcher);
636 }
637 
638 
639 static void
photos_base_item_refresh_thumb_path_pixbuf(GObject * source_object,GAsyncResult * res,gpointer user_data)640 photos_base_item_refresh_thumb_path_pixbuf (GObject *source_object, GAsyncResult *res, gpointer user_data)
641 {
642   PhotosBaseItem *self;
643   PhotosBaseItemPrivate *priv;
644   GApplication *app;
645   g_autoptr (GdkPixbuf) centered_pixbuf = NULL;
646   g_autoptr (GdkPixbuf) pixbuf = NULL;
647   g_autoptr (GdkPixbuf) scaled_pixbuf = NULL;
648   GInputStream *stream = G_INPUT_STREAM (source_object);
649   gint icon_size;
650   gint scale;
651 
652   {
653     g_autoptr (GError) error = NULL;
654 
655     pixbuf = gdk_pixbuf_new_from_stream_finish (res, &error);
656     if (error != NULL)
657       {
658         GFile *file;
659         g_autofree gchar *uri = NULL;
660 
661         if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
662           goto out;
663 
664         file = G_FILE (g_object_get_data (G_OBJECT (stream), "file"));
665         uri = g_file_get_uri (file);
666         g_warning ("Unable to create pixbuf from %s: %s", uri, error->message);
667         g_file_delete_async (file, G_PRIORITY_DEFAULT, NULL, NULL, NULL);
668       }
669   }
670 
671   self = PHOTOS_BASE_ITEM (user_data);
672   priv = photos_base_item_get_instance_private (self);
673 
674   if (pixbuf == NULL)
675     {
676       priv->failed_thumbnailing = TRUE;
677       g_clear_pointer (&priv->thumb_path, g_free);
678       photos_base_item_set_failed_icon (self);
679       goto out;
680     }
681 
682   app = g_application_get_default ();
683   scale = photos_application_get_scale_factor (PHOTOS_APPLICATION (app));
684   icon_size = photos_utils_get_icon_size_unscaled ();
685   scaled_pixbuf = photos_utils_downscale_pixbuf_for_scale (pixbuf, icon_size, scale);
686 
687   icon_size = photos_utils_get_icon_size ();
688   centered_pixbuf = photos_utils_center_pixbuf (scaled_pixbuf, icon_size);
689   photos_base_item_set_original_icon (self, centered_pixbuf);
690 
691  out:
692   g_input_stream_close_async (stream, G_PRIORITY_DEFAULT, NULL, NULL, NULL);
693 }
694 
695 
696 static void
photos_base_item_refresh_thumb_path_read(GObject * source_object,GAsyncResult * res,gpointer user_data)697 photos_base_item_refresh_thumb_path_read (GObject *source_object, GAsyncResult *res, gpointer user_data)
698 {
699   PhotosBaseItem *self;
700   PhotosBaseItemPrivate *priv;
701   GFile *file = G_FILE (source_object);
702   g_autoptr (GFileInputStream) stream = NULL;
703 
704   {
705     g_autoptr (GError) error = NULL;
706 
707     stream = g_file_read_finish (file, res, &error);
708     if (error != NULL)
709       {
710         g_autofree gchar *uri = NULL;
711 
712         if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
713           goto out;
714 
715         uri = g_file_get_uri (file);
716         g_warning ("Unable to read file at %s: %s", uri, error->message);
717         g_file_delete_async (file, G_PRIORITY_DEFAULT, NULL, NULL, NULL);
718       }
719   }
720 
721   self = PHOTOS_BASE_ITEM (user_data);
722   priv = photos_base_item_get_instance_private (self);
723 
724   if (stream == NULL)
725     {
726       priv->failed_thumbnailing = TRUE;
727       g_clear_pointer (&priv->thumb_path, g_free);
728       photos_base_item_set_failed_icon (self);
729       goto out;
730     }
731 
732   g_object_set_data_full (G_OBJECT (stream), "file", g_object_ref (file), g_object_unref);
733   gdk_pixbuf_new_from_stream_async (G_INPUT_STREAM (stream),
734                                     priv->cancellable,
735                                     photos_base_item_refresh_thumb_path_pixbuf,
736                                     self);
737  out:
738   return;
739 }
740 
741 
742 static void
photos_base_item_refresh_thumb_path(PhotosBaseItem * self)743 photos_base_item_refresh_thumb_path (PhotosBaseItem *self)
744 {
745   PhotosBaseItemPrivate *priv;
746   g_autoptr (GFile) thumb_file = NULL;
747 
748   priv = photos_base_item_get_instance_private (self);
749 
750   thumb_file = g_file_new_for_path (priv->thumb_path);
751   g_file_read_async (thumb_file,
752                      G_PRIORITY_DEFAULT,
753                      priv->cancellable,
754                      photos_base_item_refresh_thumb_path_read,
755                      self);
756 }
757 
758 
759 static void
photos_base_item_thumbnail_path_info(GObject * source_object,GAsyncResult * res,gpointer user_data)760 photos_base_item_thumbnail_path_info (GObject *source_object, GAsyncResult *res, gpointer user_data)
761 {
762   PhotosBaseItem *self = PHOTOS_BASE_ITEM (source_object);
763   PhotosBaseItemPrivate *priv;
764   g_autoptr (GFileInfo) info = NULL;
765 
766   priv = photos_base_item_get_instance_private (self);
767 
768   {
769     g_autoptr (GError) error = NULL;
770 
771     info = photos_base_item_query_info_finish (self, res, &error);
772     if (error != NULL)
773       {
774         if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
775           goto out;
776 
777         g_warning ("Unable to query info for item at %s: %s", priv->uri, error->message);
778       }
779   }
780 
781   g_clear_pointer (&priv->thumb_path, g_free);
782 
783   if (info == NULL)
784     {
785       priv->failed_thumbnailing = TRUE;
786       photos_base_item_set_failed_icon (self);
787       goto out;
788     }
789 
790   priv->thumb_path = g_strdup (g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH));
791   if (priv->thumb_path != NULL)
792     {
793       photos_base_item_refresh_thumb_path (self);
794     }
795   else
796     {
797       priv->failed_thumbnailing = TRUE;
798       photos_base_item_set_failed_icon (self);
799     }
800 
801  out:
802   return;
803 }
804 
805 
806 static void
photos_base_item_create_thumbnail_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)807 photos_base_item_create_thumbnail_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
808 {
809   PhotosBaseItem *self = PHOTOS_BASE_ITEM (source_object);
810   PhotosBaseItemPrivate *priv;
811   gboolean success;
812 
813   {
814     g_autoptr (GError) error = NULL;
815 
816     success = photos_base_item_create_thumbnail_finish (self, res, &error);
817     if (error != NULL)
818       {
819         if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
820           goto out;
821 
822         g_warning ("Unable to create thumbnail: %s", error->message);
823       }
824   }
825 
826   priv = photos_base_item_get_instance_private (self);
827 
828   if (!success)
829     {
830       priv->failed_thumbnailing = TRUE;
831       photos_base_item_set_failed_icon (self);
832       goto out;
833     }
834 
835   photos_base_item_query_info_async (self,
836                                      G_FILE_ATTRIBUTE_THUMBNAIL_PATH,
837                                      G_FILE_QUERY_INFO_NONE,
838                                      G_PRIORITY_DEFAULT,
839                                      priv->cancellable,
840                                      photos_base_item_thumbnail_path_info,
841                                      NULL);
842 
843  out:
844   return;
845 }
846 
847 
848 static void
photos_base_item_file_query_info(GObject * source_object,GAsyncResult * res,gpointer user_data)849 photos_base_item_file_query_info (GObject *source_object, GAsyncResult *res, gpointer user_data)
850 {
851   PhotosBaseItem *self = PHOTOS_BASE_ITEM (source_object);
852   PhotosBaseItemPrivate *priv;
853   g_autoptr (GFileInfo) info = NULL;
854 
855   priv = photos_base_item_get_instance_private (self);
856 
857   {
858     g_autoptr (GError) error = NULL;
859 
860     info = photos_base_item_query_info_finish (self, res, &error);
861     if (error != NULL)
862       {
863         if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
864           goto out;
865 
866         g_warning ("Unable to query info for item at %s: %s", priv->uri, error->message);
867       }
868   }
869 
870   g_clear_pointer (&priv->thumb_path, g_free);
871 
872   if (info == NULL)
873     {
874       priv->failed_thumbnailing = TRUE;
875       photos_base_item_set_failed_icon (self);
876       goto out;
877     }
878 
879   priv->thumb_path = g_strdup (g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH));
880   if (priv->thumb_path != NULL)
881     photos_base_item_refresh_thumb_path (self);
882   else
883     {
884       photos_base_item_create_thumbnail_async (self,
885                                                priv->cancellable,
886                                                photos_base_item_create_thumbnail_cb,
887                                                NULL);
888     }
889 
890  out:
891   return;
892 }
893 
894 
895 static gchar *
photos_base_item_default_create_thumbnail_path(PhotosBaseItem * self)896 photos_base_item_default_create_thumbnail_path (PhotosBaseItem *self)
897 {
898   PhotosBaseItemPrivate *priv;
899   const gchar *cache_dir;
900   g_autofree gchar *filename = NULL;
901   g_autofree gchar *md5 = NULL;
902   gchar *path;
903   g_autofree gchar *thumbnails_subdir = NULL;
904   gint size;
905 
906   priv = photos_base_item_get_instance_private (self);
907 
908   md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, priv->uri, -1);
909   filename = g_strconcat (md5, ".png", NULL);
910 
911   cache_dir = g_get_user_cache_dir ();
912   size = photos_utils_get_icon_size ();
913   thumbnails_subdir = g_strdup_printf ("%d-%d", size, THUMBNAIL_GENERATION);
914 
915   path = g_build_filename (cache_dir,
916                            PACKAGE_TARNAME,
917                            "thumbnails",
918                            thumbnails_subdir,
919                            filename,
920                            NULL);
921 
922   return path;
923 }
924 
925 
926 static void
photos_base_item_default_set_favorite(PhotosBaseItem * self,gboolean favorite)927 photos_base_item_default_set_favorite (PhotosBaseItem *self, gboolean favorite)
928 {
929   PhotosBaseItemPrivate *priv;
930 
931   priv = photos_base_item_get_instance_private (self);
932 
933   if (favorite == priv->favorite)
934     return;
935 
936   priv->favorite = favorite;
937   photos_base_item_check_effects_and_update_info (self);
938   photos_utils_set_favorite (priv->id, favorite);
939 }
940 
941 
942 static gboolean
photos_base_item_default_metadata_add_shared(PhotosBaseItem * self,const gchar * provider_type,const gchar * account_identity,const gchar * shared_id,GCancellable * cancellable,GError ** error)943 photos_base_item_default_metadata_add_shared (PhotosBaseItem  *self,
944                                               const gchar     *provider_type,
945                                               const gchar     *account_identity,
946                                               const gchar     *shared_id,
947                                               GCancellable    *cancellable,
948                                               GError         **error)
949 {
950   return TRUE;
951 }
952 
953 
954 static void
photos_base_item_default_open(PhotosBaseItem * self,GtkWindow * parent,guint32 timestamp)955 photos_base_item_default_open (PhotosBaseItem *self, GtkWindow *parent, guint32 timestamp)
956 {
957   PhotosBaseItemPrivate *priv;
958 
959   priv = photos_base_item_get_instance_private (self);
960 
961   if (priv->default_app_name == NULL)
962     return;
963 
964   /* Without a default_app, launch in the web browser, otherwise use
965    * that system application.
966    */
967 
968   if (priv->default_app != NULL)
969     {
970       g_autoptr (GAppLaunchContext) ctx = NULL;
971 
972       ctx = photos_utils_new_app_launch_context_from_widget (GTK_WIDGET (parent));
973 
974       {
975         g_autoptr (GError) error = NULL;
976 
977         photos_glib_app_info_launch_uri (priv->default_app, priv->uri, ctx, &error);
978         if (error != NULL)
979           g_warning ("Unable to show URI %s: %s", priv->uri, error->message);
980       }
981     }
982   else
983     {
984       g_autoptr (GError) error = NULL;
985 
986       gtk_show_uri_on_window (parent, priv->uri, timestamp, &error);
987       if (error != NULL)
988         g_warning ("Unable to show URI %s: %s", priv->uri, error->message);
989     }
990 }
991 
992 
993 static void
photos_base_item_default_refresh_icon(PhotosBaseItem * self)994 photos_base_item_default_refresh_icon (PhotosBaseItem *self)
995 {
996   PhotosBaseItemPrivate *priv;
997 
998   priv = photos_base_item_get_instance_private (self);
999 
1000   if (priv->thumb_path != NULL)
1001     {
1002       photos_base_item_refresh_thumb_path (self);
1003       return;
1004     }
1005 
1006   photos_base_item_set_thumbnailing_icon (self);
1007 
1008   if (priv->failed_thumbnailing)
1009     return;
1010 
1011   if (priv->collection)
1012     {
1013       photos_base_item_refresh_collection_icon (self);
1014       return;
1015     }
1016 
1017   photos_base_item_query_info_async (self,
1018                                      G_FILE_ATTRIBUTE_THUMBNAIL_PATH,
1019                                      G_FILE_QUERY_INFO_NONE,
1020                                      G_PRIORITY_DEFAULT,
1021                                      priv->cancellable,
1022                                      photos_base_item_file_query_info,
1023                                      NULL);
1024 }
1025 
1026 
1027 static void
photos_base_item_default_update_type_description(PhotosBaseItem * self)1028 photos_base_item_default_update_type_description (PhotosBaseItem *self)
1029 {
1030   PhotosBaseItemPrivate *priv;
1031   gchar *description = NULL;
1032 
1033   priv = photos_base_item_get_instance_private (self);
1034 
1035   if (priv->collection)
1036     description = g_strdup (_("Album"));
1037   else if (priv->mime_type != NULL)
1038     description = g_content_type_get_description (priv->mime_type);
1039 
1040   photos_utils_take_string (&priv->type_description, description);
1041 }
1042 
1043 
1044 static void
photos_base_item_download_in_thread_func(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)1045 photos_base_item_download_in_thread_func (GTask *task,
1046                                           gpointer source_object,
1047                                           gpointer task_data,
1048                                           GCancellable *cancellable)
1049 {
1050   PhotosBaseItem *self = PHOTOS_BASE_ITEM (source_object);
1051   g_autoptr (GFile) file = NULL;
1052 
1053   {
1054     g_autoptr (GError) error = NULL;
1055 
1056     file = photos_base_item_download (self, cancellable, &error);
1057     if (error != NULL)
1058       {
1059         g_task_return_error (task, g_steal_pointer (&error));
1060         goto out;
1061       }
1062   }
1063 
1064   g_task_return_pointer (task, g_object_ref (file), g_object_unref);
1065 
1066  out:
1067   return;
1068 }
1069 
1070 
1071 static const gchar *
photos_base_item_filterable_get_id(PhotosFilterable * filterable)1072 photos_base_item_filterable_get_id (PhotosFilterable *filterable)
1073 {
1074   PhotosBaseItem *self = PHOTOS_BASE_ITEM (filterable);
1075   PhotosBaseItemPrivate *priv;
1076 
1077   priv = photos_base_item_get_instance_private (self);
1078   return priv->id;
1079 }
1080 
1081 
1082 static gboolean
photos_base_item_filterable_is_search_criterion(PhotosFilterable * filterable)1083 photos_base_item_filterable_is_search_criterion (PhotosFilterable *filterable)
1084 {
1085   return FALSE;
1086 }
1087 
1088 
1089 static cairo_surface_t *
photos_base_item_main_box_item_get_icon(GdMainBoxItem * box_item)1090 photos_base_item_main_box_item_get_icon (GdMainBoxItem *box_item)
1091 {
1092   PhotosBaseItem *self = PHOTOS_BASE_ITEM (box_item);
1093   PhotosBaseItemPrivate *priv;
1094 
1095   priv = photos_base_item_get_instance_private (self);
1096   return priv->surface;
1097 }
1098 
1099 
1100 static const gchar *
photos_base_item_main_box_item_get_id(GdMainBoxItem * box_item)1101 photos_base_item_main_box_item_get_id (GdMainBoxItem *box_item)
1102 {
1103   PhotosBaseItem *self = PHOTOS_BASE_ITEM (box_item);
1104   PhotosBaseItemPrivate *priv;
1105 
1106   priv = photos_base_item_get_instance_private (self);
1107   return priv->id;
1108 }
1109 
1110 
1111 static const gchar *
photos_base_item_main_box_item_get_primary_text(GdMainBoxItem * box_item)1112 photos_base_item_main_box_item_get_primary_text (GdMainBoxItem *box_item)
1113 {
1114   PhotosBaseItem *self = PHOTOS_BASE_ITEM (box_item);
1115   PhotosBaseItemPrivate *priv;
1116   const gchar *primary_text;
1117 
1118   priv = photos_base_item_get_instance_private (self);
1119 
1120   if (priv->collection)
1121     primary_text = photos_base_item_get_name (self);
1122   else
1123     primary_text = NULL;
1124 
1125   return primary_text;
1126 }
1127 
1128 
1129 static const gchar *
photos_base_item_main_box_item_get_secondary_text(GdMainBoxItem * box_item)1130 photos_base_item_main_box_item_get_secondary_text (GdMainBoxItem *box_item)
1131 {
1132   const gchar *author;
1133 
1134   author = photos_base_item_get_author (PHOTOS_BASE_ITEM (box_item));
1135   return author;
1136 }
1137 
1138 
1139 static const gchar *
photos_base_item_main_box_item_get_uri(GdMainBoxItem * box_item)1140 photos_base_item_main_box_item_get_uri (GdMainBoxItem *box_item)
1141 {
1142   const gchar *uri;
1143 
1144   uri = photos_base_item_get_uri (PHOTOS_BASE_ITEM (box_item));
1145   return uri;
1146 }
1147 
1148 
1149 static void
photos_base_item_refresh_executed(GObject * source_object,GAsyncResult * res,gpointer user_data)1150 photos_base_item_refresh_executed (GObject *source_object, GAsyncResult *res, gpointer user_data)
1151 {
1152   g_autoptr (PhotosBaseItem) self = PHOTOS_BASE_ITEM (user_data);
1153   PhotosSingleItemJob *job = PHOTOS_SINGLE_ITEM_JOB (source_object);
1154   TrackerSparqlCursor *cursor = NULL; /* TODO: Use g_autoptr */
1155 
1156   {
1157     g_autoptr (GError) error = NULL;
1158 
1159     cursor = photos_single_item_job_finish (job, res, &error);
1160     if (error != NULL)
1161       {
1162         g_warning ("Unable to query single item: %s", error->message);
1163         goto out;
1164       }
1165   }
1166 
1167   if (cursor == NULL)
1168     goto out;
1169 
1170   photos_base_item_populate_from_cursor (self, cursor);
1171 
1172  out:
1173   g_clear_object (&cursor);
1174 }
1175 
1176 
1177 static GeglBuffer *
photos_base_item_get_preview_source_buffer(PhotosBaseItem * self,gint size,gint scale)1178 photos_base_item_get_preview_source_buffer (PhotosBaseItem *self, gint size, gint scale)
1179 {
1180   PhotosBaseItemPrivate *priv;
1181   const Babl *format;
1182   g_autoptr (GeglBuffer) buffer_cropped = NULL;
1183   g_autoptr (GeglBuffer) buffer_orig = NULL;
1184   g_autoptr (GeglBuffer) buffer = NULL;
1185   GeglBuffer *ret_val = NULL;
1186   GeglOperation *op;
1187   GeglRectangle bbox;
1188   GeglRectangle roi;
1189   const gchar *name;
1190   gdouble zoom;
1191   gint bpp;
1192   gint min_dimension;
1193   gint size_scaled;
1194   gint x;
1195   gint y;
1196   gint64 end;
1197   gint64 start;
1198   guchar *buf = NULL;
1199 
1200   priv = photos_base_item_get_instance_private (self);
1201 
1202   g_return_val_if_fail (!priv->collection, NULL);
1203   g_return_val_if_fail (priv->buffer_source != NULL, NULL);
1204   g_return_val_if_fail (priv->edit_graph != NULL, NULL);
1205 
1206   op = gegl_node_get_gegl_operation (priv->buffer_source);
1207   g_return_val_if_fail (op != NULL, NULL);
1208 
1209   name = gegl_operation_get_name (op);
1210   g_return_val_if_fail (g_strcmp0 (name, "gegl:buffer-source") == 0, NULL);
1211 
1212   size_scaled = size * scale;
1213 
1214   if (priv->preview_source_buffer != NULL)
1215     {
1216       bbox = *gegl_buffer_get_extent (priv->preview_source_buffer);
1217       if (bbox.height == size_scaled && bbox.width == size_scaled)
1218         {
1219           ret_val = priv->preview_source_buffer;
1220           goto out;
1221         }
1222       else
1223         {
1224           g_clear_object (&priv->preview_source_buffer);
1225         }
1226     }
1227 
1228   gegl_node_get (priv->buffer_source, "buffer", &buffer_orig, NULL);
1229   buffer = gegl_buffer_dup (buffer_orig);
1230 
1231   bbox = *gegl_buffer_get_extent (buffer);
1232   min_dimension = MIN (bbox.height, bbox.width);
1233   x = (gint) ((gdouble) (bbox.width - min_dimension) / 2.0 + 0.5);
1234   y = (gint) ((gdouble) (bbox.height - min_dimension) / 2.0 + 0.5);
1235   zoom = (gdouble) size_scaled / (gdouble) min_dimension;
1236 
1237   bbox.height = min_dimension;
1238   bbox.width = min_dimension;
1239   bbox.x = x;
1240   bbox.y = y;
1241   buffer_cropped = gegl_buffer_create_sub_buffer (buffer, &bbox);
1242 
1243   roi.height = size_scaled;
1244   roi.width = size_scaled;
1245   roi.x = (gint) ((gdouble) x * zoom + 0.5);
1246   roi.y = (gint) ((gdouble) y * zoom + 0.5);
1247 
1248   format = gegl_buffer_get_format (buffer_cropped);
1249   bpp = babl_format_get_bytes_per_pixel (format);
1250   buf = g_malloc0_n (roi.height * roi.width, bpp);
1251 
1252   start = g_get_monotonic_time ();
1253 
1254   gegl_buffer_get (buffer_cropped, &roi, zoom, format, buf, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
1255 
1256   end = g_get_monotonic_time ();
1257   photos_debug (PHOTOS_DEBUG_GEGL, "Get Preview Buffer: Downscale: %" G_GINT64_FORMAT, end - start);
1258 
1259   roi.x = 0;
1260   roi.y = 0;
1261   priv->preview_source_buffer = gegl_buffer_linear_new_from_data (buf,
1262                                                                   format,
1263                                                                   &roi,
1264                                                                   GEGL_AUTO_ROWSTRIDE,
1265                                                                   g_free,
1266                                                                   NULL);
1267 
1268   ret_val = priv->preview_source_buffer;
1269 
1270  out:
1271   return ret_val;
1272 }
1273 
1274 
1275 static void
photos_base_item_guess_save_sizes_from_buffer(GeglBuffer * buffer,const gchar * mime_type,gsize * out_full_size,gsize * out_reduced_size,GCancellable * cancellable)1276 photos_base_item_guess_save_sizes_from_buffer (GeglBuffer *buffer,
1277                                                const gchar *mime_type,
1278                                                gsize *out_full_size,
1279                                                gsize *out_reduced_size,
1280                                                GCancellable *cancellable)
1281 {
1282   GeglNode *buffer_source;
1283   g_autoptr (GeglNode) graph = NULL;
1284   GeglNode *guess_sizes;
1285   guint64 sizes[2];
1286 
1287   graph = gegl_node_new ();
1288   buffer_source = gegl_node_new_child (graph, "operation", "gegl:buffer-source", "buffer", buffer, NULL);
1289 
1290   if (g_strcmp0 (mime_type, "image/png") == 0)
1291     guess_sizes = gegl_node_new_child (graph,
1292                                        "operation", "photos:png-guess-sizes",
1293                                        "background", FALSE,
1294                                        "bitdepth", 8,
1295                                        "compression", -1,
1296                                        NULL);
1297   else
1298     guess_sizes = gegl_node_new_child (graph,
1299                                        "operation", "photos:jpg-guess-sizes",
1300                                        "optimize", FALSE,
1301                                        "progressive", FALSE,
1302                                        "sampling", TRUE,
1303                                        NULL);
1304 
1305   gegl_node_link (buffer_source, guess_sizes);
1306   gegl_node_process (guess_sizes);
1307 
1308   gegl_node_get (guess_sizes, "size", &sizes[0], "size-1", &sizes[1], NULL);
1309   if (out_full_size != NULL)
1310     *out_full_size = (gsize) sizes[0];
1311   if (out_reduced_size != NULL)
1312     *out_reduced_size = (gsize) sizes[1];
1313 }
1314 
1315 
1316 static void
photos_base_item_guess_save_sizes_in_thread_func(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)1317 photos_base_item_guess_save_sizes_in_thread_func (GTask *task,
1318                                                   gpointer source_object,
1319                                                   gpointer task_data,
1320                                                   GCancellable *cancellable)
1321 {
1322   PhotosBaseItemSaveData *data = (PhotosBaseItemSaveData *) task_data;
1323   gsize *sizes;
1324 
1325   sizes = g_malloc0_n (2, sizeof (gsize));
1326   photos_base_item_guess_save_sizes_from_buffer (data->buffer, data->type, &sizes[0], &sizes[1], cancellable);
1327   g_task_return_pointer (task, sizes, g_free);
1328 }
1329 
1330 
1331 static void
photos_base_item_guess_save_sizes_load(GObject * source_object,GAsyncResult * res,gpointer user_data)1332 photos_base_item_guess_save_sizes_load (GObject *source_object, GAsyncResult *res, gpointer user_data)
1333 {
1334   PhotosBaseItem *self = PHOTOS_BASE_ITEM (source_object);
1335   PhotosBaseItemPrivate *priv;
1336   g_autoptr (GTask) task = G_TASK (user_data);
1337   g_autoptr (GeglBuffer) buffer = NULL;
1338   g_autoptr (GeglNode) graph = NULL;
1339   PhotosBaseItemSaveData *data;
1340 
1341   priv = photos_base_item_get_instance_private (self);
1342 
1343   {
1344     g_autoptr (GError) error = NULL;
1345 
1346     graph = photos_base_item_load_finish (self, res, &error);
1347     if (error != NULL)
1348       {
1349         g_task_return_error (task, g_steal_pointer (&error));
1350         goto out;
1351       }
1352   }
1353 
1354   buffer = photos_gegl_get_buffer_from_node (graph, NULL);
1355   data = photos_base_item_save_data_new (NULL, buffer, priv->mime_type, 0.0);
1356   g_task_set_task_data (task, data, (GDestroyNotify) photos_base_item_save_data_free);
1357 
1358   g_task_run_in_thread (task, photos_base_item_guess_save_sizes_in_thread_func);
1359 
1360  out:
1361   return;
1362 }
1363 
1364 
1365 static void
photos_base_item_process_process(GObject * source_object,GAsyncResult * res,gpointer user_data)1366 photos_base_item_process_process (GObject *source_object, GAsyncResult *res, gpointer user_data)
1367 {
1368   g_autoptr (GTask) task = G_TASK (user_data);
1369   GeglProcessor *processor = GEGL_PROCESSOR (source_object);
1370 
1371   {
1372     g_autoptr (GError) error = NULL;
1373 
1374     if (!photos_gegl_processor_process_finish (processor, res, &error))
1375       {
1376         g_task_return_error (task, g_steal_pointer (&error));
1377         goto out;
1378       }
1379   }
1380 
1381   g_task_return_boolean (task, TRUE);
1382 
1383  out:
1384   return;
1385 }
1386 
1387 
1388 static void
photos_base_item_process_async(PhotosBaseItem * self,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1389 photos_base_item_process_async (PhotosBaseItem *self,
1390                                 GCancellable *cancellable,
1391                                 GAsyncReadyCallback callback,
1392                                 gpointer user_data)
1393 {
1394   PhotosBaseItemPrivate *priv;
1395   g_autoptr (GTask) task = NULL;
1396   PhotosPipeline *pipeline;
1397 
1398   g_return_if_fail (PHOTOS_IS_BASE_ITEM (self));
1399   priv = photos_base_item_get_instance_private (self);
1400 
1401   g_return_if_fail (!priv->collection);
1402 
1403   pipeline = PHOTOS_PIPELINE (dzl_task_cache_peek (pipeline_cache, self));
1404   g_return_if_fail (PHOTOS_IS_PIPELINE (pipeline));
1405 
1406   g_clear_object (&priv->processor);
1407   priv->processor = photos_pipeline_new_processor (pipeline);
1408 
1409   task = g_task_new (self, cancellable, callback, user_data);
1410   g_task_set_source_tag (task, photos_base_item_process_async);
1411 
1412   photos_gegl_processor_process_async (priv->processor,
1413                                        cancellable,
1414                                        photos_base_item_process_process,
1415                                        g_object_ref (task));
1416 }
1417 
1418 
1419 static gboolean
photos_base_item_process_finish(PhotosBaseItem * self,GAsyncResult * res,GError ** error)1420 photos_base_item_process_finish (PhotosBaseItem *self, GAsyncResult *res, GError **error)
1421 {
1422   GTask *task;
1423 
1424   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), FALSE);
1425 
1426   g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
1427   task = G_TASK (res);
1428 
1429   g_return_val_if_fail (g_task_get_source_tag (task) == photos_base_item_process_async, FALSE);
1430   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1431 
1432   return g_task_propagate_boolean (task, error);
1433 }
1434 
1435 
1436 static void
photos_base_item_common_process(GObject * source_object,GAsyncResult * res,gpointer user_data)1437 photos_base_item_common_process (GObject *source_object, GAsyncResult *res, gpointer user_data)
1438 {
1439   PhotosBaseItem *self = PHOTOS_BASE_ITEM (source_object);
1440   g_autoptr (GTask) task = G_TASK (user_data);
1441 
1442   {
1443     g_autoptr (GError) error = NULL;
1444 
1445     if (!photos_base_item_process_finish (self, res, &error))
1446       {
1447         g_task_return_error (task, g_steal_pointer (&error));
1448         goto out;
1449       }
1450   }
1451 
1452   g_task_return_boolean (task, TRUE);
1453 
1454  out:
1455   return;
1456 }
1457 
1458 
1459 static GeglBuffer *
photos_base_item_load_buffer(PhotosBaseItem * self,GCancellable * cancellable,GError ** error)1460 photos_base_item_load_buffer (PhotosBaseItem *self, GCancellable *cancellable, GError **error)
1461 {
1462   PhotosBaseItemPrivate *priv;
1463   g_autoptr (GFile) file = NULL;
1464   g_autoptr (GeglBuffer) buffer = NULL;
1465   GeglBuffer *ret_val = NULL;
1466   GeglNode *buffer_sink;
1467   g_autoptr (GeglNode) graph = NULL;
1468   GeglNode *load;
1469   g_autofree gchar *path = NULL;
1470   gint64 end;
1471   gint64 start;
1472 
1473   priv = photos_base_item_get_instance_private (self);
1474 
1475   file = photos_base_item_download (self, cancellable, error);
1476   if (file == NULL)
1477     goto out;
1478 
1479   path = g_file_get_path (file);
1480   if (!g_utf8_validate (path, -1, NULL))
1481     {
1482       g_set_error (error, PHOTOS_ERROR, 0, "Path is not UTF-8 encoded");
1483       goto out;
1484     }
1485 
1486   graph = gegl_node_new ();
1487   load = gegl_node_new_child (graph, "operation", "gegl:load", "path", path, NULL);
1488   buffer_sink = gegl_node_new_child (graph, "operation", "gegl:buffer-sink", "buffer", &buffer, NULL);
1489 
1490   gegl_node_link (load, buffer_sink);
1491 
1492   start = g_get_monotonic_time ();
1493 
1494   gegl_node_process (buffer_sink);
1495   ret_val = photos_gegl_buffer_apply_orientation (buffer, priv->orientation);
1496 
1497   end = g_get_monotonic_time ();
1498   photos_debug (PHOTOS_DEBUG_GEGL, "Buffer Load: From Local: %" G_GINT64_FORMAT, end - start);
1499 
1500  out:
1501   return ret_val;
1502 }
1503 
1504 
1505 static void
photos_base_item_load_buffer_in_thread_func(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)1506 photos_base_item_load_buffer_in_thread_func (GTask *task,
1507                                              gpointer source_object,
1508                                              gpointer task_data,
1509                                              GCancellable *cancellable)
1510 {
1511   PhotosBaseItem *self = PHOTOS_BASE_ITEM (source_object);
1512   g_autoptr (GeglBuffer) buffer = NULL;
1513 
1514   {
1515     g_autoptr (GError) error = NULL;
1516 
1517     buffer = photos_base_item_load_buffer (self, cancellable, &error);
1518     if (error != NULL)
1519       {
1520         g_task_return_error (task, g_steal_pointer (&error));
1521         goto out;
1522       }
1523   }
1524 
1525   g_task_return_pointer (task, g_object_ref (buffer), g_object_unref);
1526 
1527  out:
1528   return;
1529 }
1530 
1531 
1532 static void
photos_base_item_load_buffer_async(PhotosBaseItem * self,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1533 photos_base_item_load_buffer_async (PhotosBaseItem *self,
1534                                     GCancellable *cancellable,
1535                                     GAsyncReadyCallback callback,
1536                                     gpointer user_data)
1537 {
1538   g_autoptr (GTask) task = NULL;
1539 
1540   g_return_if_fail (PHOTOS_IS_BASE_ITEM (self));
1541 
1542   task = g_task_new (self, cancellable, callback, user_data);
1543   g_task_set_return_on_cancel (task, TRUE);
1544   g_task_set_source_tag (task, photos_base_item_load_buffer_async);
1545 
1546   g_task_run_in_thread (task, photos_base_item_load_buffer_in_thread_func);
1547 }
1548 
1549 
1550 static GeglBuffer *
photos_base_item_load_buffer_finish(PhotosBaseItem * self,GAsyncResult * res,GError ** error)1551 photos_base_item_load_buffer_finish (PhotosBaseItem *self, GAsyncResult *res, GError **error)
1552 {
1553   GTask *task;
1554 
1555   g_return_val_if_fail (g_task_is_valid (res, self), NULL);
1556   task = G_TASK (res);
1557 
1558   g_return_val_if_fail (g_task_get_source_tag (task) == photos_base_item_load_buffer_async, NULL);
1559   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1560 
1561   return g_task_propagate_pointer (task, error);
1562 }
1563 
1564 
1565 static void
photos_base_item_load_pipeline_task_cache_populate_new(GObject * source_object,GAsyncResult * res,gpointer user_data)1566 photos_base_item_load_pipeline_task_cache_populate_new (GObject *source_object,
1567                                                         GAsyncResult *res,
1568                                                         gpointer user_data)
1569 {
1570   g_autoptr (GTask) task = G_TASK (user_data);
1571   g_autoptr (PhotosPipeline) pipeline = NULL;
1572 
1573   {
1574     g_autoptr (GError) error = NULL;
1575 
1576     pipeline = photos_pipeline_new_finish (res, &error);
1577     if (error != NULL)
1578       {
1579         g_task_return_error (task, g_steal_pointer (&error));
1580         goto out;
1581       }
1582   }
1583 
1584   g_task_return_pointer (task, g_object_ref (pipeline), g_object_unref);
1585 
1586  out:
1587   return;
1588 }
1589 
1590 
1591 static void
photos_base_item_load_pipeline_task_cache_populate(DzlTaskCache * cache,gconstpointer key,GTask * task,gpointer user_data)1592 photos_base_item_load_pipeline_task_cache_populate (DzlTaskCache *cache,
1593                                                     gconstpointer key,
1594                                                     GTask *task,
1595                                                     gpointer user_data)
1596 {
1597   PhotosBaseItem *self = PHOTOS_BASE_ITEM ((gpointer) key);
1598   PhotosBaseItemClass *class;
1599   GCancellable *cancellable;
1600   g_auto (GStrv) uris = NULL;
1601 
1602   cancellable = g_task_get_cancellable (task);
1603 
1604   class = PHOTOS_BASE_ITEM_GET_CLASS (self);
1605   if (class->create_pipeline_paths != NULL)
1606     {
1607       g_auto (GStrv) paths = NULL;
1608 
1609       paths = photos_base_item_create_pipeline_paths (self);
1610       uris = photos_utils_convert_paths_to_uris ((const gchar *const *) paths);
1611     }
1612 
1613   photos_pipeline_new_async (NULL,
1614                              (const gchar *const *) uris,
1615                              cancellable,
1616                              photos_base_item_load_pipeline_task_cache_populate_new,
1617                              g_object_ref (task));
1618 }
1619 
1620 
1621 static void
photos_base_item_load_pipeline_task_cache_get(GObject * source_object,GAsyncResult * res,gpointer user_data)1622 photos_base_item_load_pipeline_task_cache_get (GObject *source_object, GAsyncResult *res, gpointer user_data)
1623 {
1624   g_autoptr (GTask) task = G_TASK (user_data);
1625   DzlTaskCache *cache = DZL_TASK_CACHE (source_object);
1626   g_autoptr (PhotosPipeline) pipeline = NULL;
1627 
1628   g_assert_true (cache == pipeline_cache);
1629 
1630   {
1631     g_autoptr (GError) error = NULL;
1632 
1633     pipeline = dzl_task_cache_get_finish (cache, res, &error);
1634     if (error != NULL)
1635       {
1636         g_task_return_error (task, g_steal_pointer (&error));
1637         goto out;
1638       }
1639   }
1640 
1641   g_task_return_pointer (task, g_object_ref (pipeline), g_object_unref);
1642 
1643  out:
1644   return;
1645 }
1646 
1647 
1648 static void
photos_base_item_load_pipeline_async(PhotosBaseItem * self,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1649 photos_base_item_load_pipeline_async (PhotosBaseItem *self,
1650                                       GCancellable *cancellable,
1651                                       GAsyncReadyCallback callback,
1652                                       gpointer user_data)
1653 {
1654   PhotosBaseItemPrivate *priv;
1655   g_autoptr (GTask) task = NULL;
1656 
1657   g_return_if_fail (PHOTOS_IS_BASE_ITEM (self));
1658   priv = photos_base_item_get_instance_private (self);
1659 
1660   g_return_if_fail (!priv->collection);
1661 
1662   task = g_task_new (self, cancellable, callback, user_data);
1663   g_task_set_source_tag (task, photos_base_item_load_pipeline_async);
1664 
1665   dzl_task_cache_get_async (pipeline_cache,
1666                             self,
1667                             FALSE,
1668                             cancellable,
1669                             photos_base_item_load_pipeline_task_cache_get,
1670                             g_object_ref (task));
1671 }
1672 
1673 
1674 static PhotosPipeline *
photos_base_item_load_pipeline_finish(PhotosBaseItem * self,GAsyncResult * res,GError ** error)1675 photos_base_item_load_pipeline_finish (PhotosBaseItem *self, GAsyncResult *res, GError **error)
1676 {
1677   GTask *task;
1678 
1679   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), NULL);
1680 
1681   g_return_val_if_fail (g_task_is_valid (res, self), NULL);
1682   task = G_TASK (res);
1683 
1684   g_return_val_if_fail (g_task_get_source_tag (task) == photos_base_item_load_pipeline_async, NULL);
1685   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1686 
1687   return g_task_propagate_pointer (task, error);
1688 }
1689 
1690 
1691 static void
photos_base_item_load_process(GObject * source_object,GAsyncResult * res,gpointer user_data)1692 photos_base_item_load_process (GObject *source_object, GAsyncResult *res, gpointer user_data)
1693 {
1694   g_autoptr (GTask) task = G_TASK (user_data);
1695   PhotosBaseItem *self;
1696   GeglNode *graph;
1697   PhotosPipeline *pipeline;
1698 
1699   self = PHOTOS_BASE_ITEM (g_task_get_source_object (task));
1700 
1701   {
1702     g_autoptr (GError) error = NULL;
1703 
1704     photos_base_item_process_finish (self, res, &error);
1705     if (error != NULL)
1706       {
1707         photos_base_item_clear_pixels (self);
1708         g_task_return_error (task, g_steal_pointer (&error));
1709         goto out;
1710       }
1711   }
1712 
1713   pipeline = PHOTOS_PIPELINE (dzl_task_cache_peek (pipeline_cache, self));
1714   g_assert_true (PHOTOS_IS_PIPELINE (pipeline));
1715 
1716   graph = photos_pipeline_get_graph (pipeline);
1717   g_task_return_pointer (task, g_object_ref (graph), g_object_unref);
1718 
1719  out:
1720   return;
1721 }
1722 
1723 
1724 static void
photos_base_item_load_load_buffer(GObject * source_object,GAsyncResult * res,gpointer user_data)1725 photos_base_item_load_load_buffer (GObject *source_object, GAsyncResult *res, gpointer user_data)
1726 {
1727   g_autoptr (GTask) task = G_TASK (user_data);
1728   PhotosBaseItem *self;
1729   PhotosBaseItemPrivate *priv;
1730   const Babl *format;
1731   GCancellable *cancellable;
1732   g_autoptr (GeglBuffer) buffer = NULL;
1733   GeglRectangle bbox;
1734   const gchar *format_name;
1735 
1736   self = PHOTOS_BASE_ITEM (g_task_get_source_object (task));
1737   priv = photos_base_item_get_instance_private (self);
1738 
1739   cancellable = g_task_get_cancellable (task);
1740 
1741   {
1742     g_autoptr (GError) error = NULL;
1743 
1744     buffer = photos_base_item_load_buffer_finish (self, res, &error);
1745     if (error != NULL)
1746       {
1747         photos_base_item_clear_pixels (self);
1748         g_task_return_error (task, g_steal_pointer (&error));
1749         goto out;
1750       }
1751   }
1752 
1753   bbox = *gegl_buffer_get_extent (buffer);
1754   format = gegl_buffer_get_format (buffer);
1755   format_name = babl_get_name (format);
1756   photos_debug (PHOTOS_DEBUG_GEGL, "Buffer loaded: %d, %d, %d×%d, %s",
1757                 bbox.x,
1758                 bbox.y,
1759                 bbox.width,
1760                 bbox.height,
1761                 format_name);
1762 
1763   gegl_node_set (priv->buffer_source, "buffer", buffer, NULL);
1764 
1765   photos_base_item_process_async (self, cancellable, photos_base_item_load_process, g_object_ref (task));
1766 
1767  out:
1768   return;
1769 }
1770 
1771 
1772 static void
photos_base_item_load_load_pipeline(GObject * source_object,GAsyncResult * res,gpointer user_data)1773 photos_base_item_load_load_pipeline (GObject *source_object, GAsyncResult *res, gpointer user_data)
1774 {
1775   g_autoptr (GTask) task = G_TASK (user_data);
1776   PhotosBaseItem *self;
1777   PhotosBaseItemPrivate *priv;
1778   GeglNode *graph;
1779   GCancellable *cancellable;
1780   g_autoptr (PhotosPipeline) pipeline = NULL;
1781 
1782   self = PHOTOS_BASE_ITEM (g_task_get_source_object (task));
1783   priv = photos_base_item_get_instance_private (self);
1784 
1785   cancellable = g_task_get_cancellable (task);
1786 
1787   {
1788     g_autoptr (GError) error = NULL;
1789 
1790     pipeline = photos_base_item_load_pipeline_finish (self, res, &error);
1791     if (error != NULL)
1792       {
1793         photos_base_item_clear_pixels (self);
1794         g_task_return_error (task, g_steal_pointer (&error));
1795         goto out;
1796       }
1797   }
1798 
1799   if (priv->edit_graph == NULL)
1800     priv->edit_graph = gegl_node_new ();
1801 
1802   photos_pipeline_set_parent (pipeline, priv->edit_graph);
1803 
1804   priv->buffer_source = gegl_node_new_child (priv->edit_graph, "operation", "gegl:buffer-source", NULL);
1805   graph = photos_pipeline_get_graph (pipeline);
1806   gegl_node_link (priv->buffer_source, graph);
1807 
1808   photos_base_item_load_buffer_async (self, cancellable, photos_base_item_load_load_buffer, g_object_ref (task));
1809 
1810  out:
1811   return;
1812 }
1813 
1814 
1815 static void
photos_base_item_metadata_add_shared_in_thread_func(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)1816 photos_base_item_metadata_add_shared_in_thread_func (GTask *task,
1817                                                      gpointer source_object,
1818                                                      gpointer task_data,
1819                                                      GCancellable *cancellable)
1820 {
1821   PhotosBaseItem *self = PHOTOS_BASE_ITEM (source_object);
1822   PhotosBaseItemMetadataAddSharedData *data = (PhotosBaseItemMetadataAddSharedData *) task_data;
1823   gboolean result;
1824 
1825   {
1826     g_autoptr (GError) error = NULL;
1827 
1828     result = PHOTOS_BASE_ITEM_GET_CLASS (self)->metadata_add_shared (self,
1829                                                                      data->provider_type,
1830                                                                      data->account_identity,
1831                                                                      data->shared_id,
1832                                                                      cancellable,
1833                                                                      &error);
1834     if (error != NULL)
1835       {
1836         g_task_return_error (task, g_steal_pointer (&error));
1837         return;
1838       }
1839   }
1840 
1841   g_task_return_boolean (task, result);
1842 }
1843 
1844 
1845 static void
photos_base_item_pipeline_is_edited_load_pipeline(GObject * source_object,GAsyncResult * res,gpointer user_data)1846 photos_base_item_pipeline_is_edited_load_pipeline (GObject *source_object, GAsyncResult *res, gpointer user_data)
1847 {
1848   PhotosBaseItem *self = PHOTOS_BASE_ITEM (source_object);
1849   g_autoptr (GTask) task = G_TASK (user_data);
1850   g_autoptr (PhotosPipeline) pipeline = NULL;
1851   gboolean is_edited;
1852 
1853   {
1854     g_autoptr (GError) error = NULL;
1855 
1856     pipeline = photos_base_item_load_pipeline_finish (self, res, &error);
1857     if (error != NULL)
1858       {
1859         g_task_return_error (task, g_steal_pointer (&error));
1860         goto out;
1861       }
1862   }
1863 
1864   is_edited = photos_pipeline_is_edited (pipeline);
1865   g_task_return_boolean (task, is_edited);
1866 
1867   if (!photos_base_item_can_edit (self))
1868     g_return_if_fail (!is_edited);
1869 
1870  out:
1871   return;
1872 }
1873 
1874 
1875 static void
photos_base_item_pipeline_save_delete(GObject * source_object,GAsyncResult * res,gpointer user_data)1876 photos_base_item_pipeline_save_delete (GObject *source_object, GAsyncResult *res, gpointer user_data)
1877 {
1878   PhotosBaseItem *self;
1879   PhotosBaseItemPrivate *priv;
1880   GFile *thumbnail_file = G_FILE (source_object);
1881   g_autoptr (GTask) task = G_TASK (user_data);
1882 
1883   self = PHOTOS_BASE_ITEM (g_task_get_source_object (task));
1884   priv = photos_base_item_get_instance_private (self);
1885 
1886   {
1887     g_autoptr (GError) error = NULL;
1888 
1889     if (!g_file_delete_finish (thumbnail_file, res, &error))
1890       {
1891         if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
1892           {
1893             g_autofree gchar *uri = NULL;
1894 
1895             uri = g_file_get_uri (thumbnail_file);
1896             g_warning ("Unable to delete thumbnail %s: %s", uri, error->message);
1897           }
1898       }
1899   }
1900 
1901   /* Mark the task as a success, no matter what. The pipeline has
1902    * already been saved, so it doesn't make sense to fail the task
1903    * just because we failed to delete the old thumbnail.
1904    */
1905 
1906   g_clear_pointer (&priv->thumb_path, g_free);
1907   photos_base_item_refresh (self);
1908   g_task_return_boolean (task, TRUE);
1909 }
1910 
1911 
1912 static void
photos_base_item_pipeline_save_save(GObject * source_object,GAsyncResult * res,gpointer user_data)1913 photos_base_item_pipeline_save_save (GObject *source_object, GAsyncResult *res, gpointer user_data)
1914 {
1915   g_autoptr (GTask) task = G_TASK (user_data);
1916   PhotosBaseItem *self;
1917   PhotosBaseItemPrivate *priv;
1918   GCancellable *cancellable;
1919   g_autoptr (GFile) thumbnail_file = NULL;
1920   PhotosPipeline *pipeline = PHOTOS_PIPELINE (source_object);
1921   g_autofree gchar *thumbnail_path = NULL;
1922 
1923   self = PHOTOS_BASE_ITEM (g_task_get_source_object (task));
1924   priv = photos_base_item_get_instance_private (self);
1925 
1926   cancellable = g_task_get_cancellable (task);
1927 
1928   {
1929     g_autoptr (GError) error = NULL;
1930 
1931     if (!photos_pipeline_save_finish (pipeline, res, &error))
1932       {
1933         g_task_return_error (task, g_steal_pointer (&error));
1934         goto out;
1935       }
1936   }
1937 
1938   g_cancellable_cancel (priv->cancellable);
1939   g_clear_object (&priv->cancellable);
1940   priv->cancellable = g_cancellable_new ();
1941 
1942   thumbnail_path = photos_base_item_create_thumbnail_path (self);
1943   thumbnail_file = g_file_new_for_path (thumbnail_path);
1944   g_file_delete_async (thumbnail_file,
1945                        G_PRIORITY_DEFAULT,
1946                        cancellable,
1947                        photos_base_item_pipeline_save_delete,
1948                        g_object_ref (task));
1949 
1950  out:
1951   return;
1952 }
1953 
1954 
1955 static void
photos_base_item_save_metadata_in_thread_func(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)1956 photos_base_item_save_metadata_in_thread_func (GTask *task,
1957                                                gpointer source_object,
1958                                                gpointer task_data,
1959                                                GCancellable *cancellable)
1960 {
1961   PhotosBaseItem *self = PHOTOS_BASE_ITEM (source_object);
1962   PhotosBaseItemPrivate *priv;
1963   GFile *export_file = G_FILE (task_data);
1964   g_autoptr (GFile) source_file = NULL;
1965   g_autoptr (GExiv2Metadata) metadata = NULL;
1966   g_autofree gchar *export_path = NULL;
1967   g_autofree gchar *source_path = NULL;
1968 
1969   priv = photos_base_item_get_instance_private (self);
1970 
1971   g_mutex_lock (&priv->mutex_save_metadata);
1972 
1973   {
1974     g_autoptr (GError) error = NULL;
1975 
1976     source_file = photos_base_item_download (self, cancellable, &error);
1977     if (error != NULL)
1978       {
1979         g_task_return_error (task, g_steal_pointer (&error));
1980         goto out;
1981       }
1982   }
1983 
1984   metadata = gexiv2_metadata_new ();
1985   source_path = g_file_get_path (source_file);
1986 
1987   {
1988     g_autoptr (GError) error = NULL;
1989 
1990     if (!gexiv2_metadata_open_path (metadata, source_path, &error))
1991       {
1992         g_task_return_error (task, g_steal_pointer (&error));
1993         goto out;
1994       }
1995   }
1996 
1997   gexiv2_metadata_set_orientation (metadata, GEXIV2_ORIENTATION_NORMAL);
1998   export_path = g_file_get_path (export_file);
1999 
2000   {
2001     g_autoptr (GError) error = NULL;
2002 
2003     if (!gexiv2_metadata_save_file (metadata, export_path, &error))
2004       {
2005         g_task_return_error (task, g_steal_pointer (&error));
2006         goto out;
2007       }
2008   }
2009 
2010   g_task_return_boolean (task, TRUE);
2011 
2012  out:
2013   g_mutex_unlock (&priv->mutex_save_metadata);
2014 }
2015 
2016 
2017 static void
photos_base_item_save_metadata_async(PhotosBaseItem * self,GFile * file,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)2018 photos_base_item_save_metadata_async (PhotosBaseItem *self,
2019                                       GFile *file,
2020                                       GCancellable *cancellable,
2021                                       GAsyncReadyCallback callback,
2022                                       gpointer user_data)
2023 {
2024   g_autoptr (GTask) task = NULL;
2025 
2026   task = g_task_new (self, cancellable, callback, user_data);
2027   g_task_set_source_tag (task, photos_base_item_save_metadata_async);
2028   g_task_set_task_data (task, g_object_ref (file), g_object_unref);
2029 
2030   g_task_run_in_thread (task, photos_base_item_save_metadata_in_thread_func);
2031 }
2032 
2033 
2034 static gboolean
photos_base_item_save_metadata_finish(PhotosBaseItem * self,GAsyncResult * res,GError ** error)2035 photos_base_item_save_metadata_finish (PhotosBaseItem *self, GAsyncResult *res, GError **error)
2036 {
2037   GTask *task;
2038 
2039   g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
2040   task = G_TASK (res);
2041 
2042   g_return_val_if_fail (g_task_get_source_tag (task) == photos_base_item_save_metadata_async, FALSE);
2043   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2044 
2045   return g_task_propagate_boolean (task, error);
2046 }
2047 
2048 
2049 static void
photos_base_item_save_buffer_save_metadata(GObject * source_object,GAsyncResult * res,gpointer user_data)2050 photos_base_item_save_buffer_save_metadata (GObject *source_object, GAsyncResult *res, gpointer user_data)
2051 {
2052   PhotosBaseItem *self = PHOTOS_BASE_ITEM (source_object);
2053   g_autoptr (GTask) task = G_TASK (user_data);
2054 
2055   {
2056     g_autoptr (GError) error = NULL;
2057 
2058     if (!photos_base_item_save_metadata_finish (self, res, &error))
2059       {
2060         g_task_return_error (task, g_steal_pointer (&error));
2061         goto out;
2062       }
2063   }
2064 
2065   g_task_return_boolean (task, TRUE);
2066 
2067  out:
2068   return;
2069 }
2070 
2071 
2072 static void
photos_base_item_save_buffer_stream_close(GObject * source_object,GAsyncResult * res,gpointer user_data)2073 photos_base_item_save_buffer_stream_close (GObject *source_object, GAsyncResult *res, gpointer user_data)
2074 {
2075   g_autoptr (GTask) task = G_TASK (user_data);
2076   PhotosBaseItem *self;
2077   PhotosBaseItemSaveBufferData *data;
2078   GCancellable *cancellable;
2079   GOutputStream *stream = G_OUTPUT_STREAM (source_object);
2080 
2081   self = PHOTOS_BASE_ITEM (g_task_get_source_object (task));
2082   cancellable = g_task_get_cancellable (task);
2083   data = (PhotosBaseItemSaveBufferData *) g_task_get_task_data (task);
2084 
2085   {
2086     g_autoptr (GError) error = NULL;
2087 
2088     if (!g_output_stream_close_finish (stream, res, &error))
2089       {
2090         g_task_return_error (task, g_steal_pointer (&error));
2091         goto out;
2092       }
2093   }
2094 
2095   photos_base_item_save_metadata_async (self,
2096                                         data->file,
2097                                         cancellable,
2098                                         photos_base_item_save_buffer_save_metadata,
2099                                         g_object_ref (task));
2100 
2101  out:
2102   return;
2103 }
2104 
2105 
2106 static void
photos_base_item_save_buffer_save_to_stream(GObject * source_object,GAsyncResult * res,gpointer user_data)2107 photos_base_item_save_buffer_save_to_stream (GObject *source_object, GAsyncResult *res, gpointer user_data)
2108 {
2109   g_autoptr (GTask) task = G_TASK (user_data);
2110   PhotosBaseItemSaveBufferData *data;
2111   GCancellable *cancellable;
2112 
2113   cancellable = g_task_get_cancellable (task);
2114   data = (PhotosBaseItemSaveBufferData *) g_task_get_task_data (task);
2115 
2116   {
2117     g_autoptr (GError) error = NULL;
2118 
2119     if (!gdk_pixbuf_save_to_stream_finish (res, &error))
2120       {
2121         g_task_return_error (task, g_steal_pointer (&error));
2122         goto out;
2123       }
2124   }
2125 
2126   g_output_stream_close_async (G_OUTPUT_STREAM (data->stream),
2127                                G_PRIORITY_DEFAULT,
2128                                cancellable,
2129                                photos_base_item_save_buffer_stream_close,
2130                                g_object_ref (task));
2131 
2132  out:
2133   return;
2134 }
2135 
2136 
2137 static void
photos_base_item_save_buffer_async(PhotosBaseItem * self,GeglBuffer * buffer,GFile * file,GFileOutputStream * stream,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)2138 photos_base_item_save_buffer_async (PhotosBaseItem *self,
2139                                     GeglBuffer *buffer,
2140                                     GFile *file,
2141                                     GFileOutputStream *stream,
2142                                     GCancellable *cancellable,
2143                                     GAsyncReadyCallback callback,
2144                                     gpointer user_data)
2145 {
2146   PhotosBaseItemPrivate *priv;
2147   g_autoptr (GTask) task = NULL;
2148   g_autoptr (GdkPixbuf) pixbuf = NULL;
2149   GeglNode *buffer_source;
2150   g_autoptr (GeglNode) graph = NULL;
2151   PhotosBaseItemSaveBufferData *data;
2152 
2153   priv = photos_base_item_get_instance_private (self);
2154 
2155   data = photos_base_item_save_buffer_data_new (file, stream);
2156 
2157   task = g_task_new (self, cancellable, callback, user_data);
2158   g_task_set_source_tag (task, photos_base_item_save_buffer_async);
2159   g_task_set_task_data (task, data, (GDestroyNotify) photos_base_item_save_buffer_data_free);
2160 
2161   graph = gegl_node_new ();
2162   buffer_source = gegl_node_new_child (graph, "operation", "gegl:buffer-source", "buffer", buffer, NULL);
2163   pixbuf = photos_gegl_create_pixbuf_from_node (buffer_source);
2164   if (pixbuf == NULL)
2165     {
2166       g_task_return_new_error (task, PHOTOS_ERROR, 0, "Failed to create a GdkPixbuf from the GeglBuffer");
2167       goto out;
2168     }
2169 
2170   if (g_strcmp0 (priv->mime_type, "image/png") == 0)
2171     {
2172       gdk_pixbuf_save_to_stream_async (pixbuf,
2173                                        G_OUTPUT_STREAM (stream),
2174                                        "png",
2175                                        cancellable,
2176                                        photos_base_item_save_buffer_save_to_stream,
2177                                        g_object_ref (task),
2178                                        NULL);
2179     }
2180   else
2181     {
2182       gdk_pixbuf_save_to_stream_async (pixbuf,
2183                                        G_OUTPUT_STREAM (stream),
2184                                        "jpeg",
2185                                        cancellable,
2186                                        photos_base_item_save_buffer_save_to_stream,
2187                                        g_object_ref (task),
2188                                        "quality", "90",
2189                                        NULL);
2190     }
2191 
2192  out:
2193   return;
2194 }
2195 
2196 
2197 static gboolean
photos_base_item_save_buffer_finish(PhotosBaseItem * self,GAsyncResult * res,GError ** error)2198 photos_base_item_save_buffer_finish (PhotosBaseItem *self, GAsyncResult *res, GError **error)
2199 {
2200   GTask *task;
2201 
2202   g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
2203   task = G_TASK (res);
2204 
2205   g_return_val_if_fail (g_task_get_source_tag (task) == photos_base_item_save_buffer_async, FALSE);
2206   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2207 
2208   return g_task_propagate_boolean (task, error);
2209 }
2210 
2211 
2212 static void
photos_base_item_save_to_dir_save_buffer(GObject * source_object,GAsyncResult * res,gpointer user_data)2213 photos_base_item_save_to_dir_save_buffer (GObject *source_object, GAsyncResult *res, gpointer user_data)
2214 {
2215   PhotosBaseItem *self = PHOTOS_BASE_ITEM (source_object);
2216   g_autoptr (GTask) task = G_TASK (user_data);
2217   PhotosBaseItemSaveData *data;
2218 
2219   data = (PhotosBaseItemSaveData *) g_task_get_task_data (task);
2220 
2221   {
2222     g_autoptr (GError) error = NULL;
2223 
2224     if (!photos_base_item_save_buffer_finish (self, res, &error))
2225       {
2226         g_task_return_error (task, g_steal_pointer (&error));
2227         goto out;
2228       }
2229   }
2230 
2231   g_task_return_pointer (task, g_object_ref (data->unique_file), g_object_unref);
2232 
2233  out:
2234   return;
2235 }
2236 
2237 
2238 static void
photos_base_item_save_to_dir_file_create(GObject * source_object,GAsyncResult * res,gpointer user_data)2239 photos_base_item_save_to_dir_file_create (GObject *source_object, GAsyncResult *res, gpointer user_data)
2240 {
2241   PhotosBaseItem *self;
2242   GCancellable *cancellable;
2243   GFile *file = G_FILE (source_object);
2244   g_autoptr (GFile) unique_file = NULL;
2245   g_autoptr (GFileOutputStream) stream = NULL;
2246   g_autoptr (GTask) task = G_TASK (user_data);
2247   PhotosBaseItemSaveData *data;
2248 
2249   self = PHOTOS_BASE_ITEM (g_task_get_source_object (task));
2250   cancellable = g_task_get_cancellable (task);
2251   data = (PhotosBaseItemSaveData *) g_task_get_task_data (task);
2252 
2253   {
2254     g_autoptr (GError) error = NULL;
2255 
2256     stream = photos_glib_file_create_finish (file, res, &unique_file, &error);
2257     if (error != NULL)
2258       {
2259         g_task_return_error (task, g_steal_pointer (&error));
2260         goto out;
2261       }
2262   }
2263 
2264   g_assert_true (G_IS_FILE_OUTPUT_STREAM (stream));
2265 
2266   g_assert_null (data->unique_file);
2267   g_assert_true (G_IS_FILE (unique_file));
2268   data->unique_file = g_object_ref (unique_file);
2269 
2270   photos_base_item_save_buffer_async (self,
2271                                       data->buffer,
2272                                       unique_file,
2273                                       stream,
2274                                       cancellable,
2275                                       photos_base_item_save_to_dir_save_buffer,
2276                                       g_object_ref (task));
2277 
2278  out:
2279   return;
2280 }
2281 
2282 
2283 static void
photos_base_item_save_to_dir_buffer_zoom(GObject * source_object,GAsyncResult * res,gpointer user_data)2284 photos_base_item_save_to_dir_buffer_zoom (GObject *source_object, GAsyncResult *res, gpointer user_data)
2285 {
2286   PhotosBaseItem *self;
2287   PhotosBaseItemPrivate *priv;
2288   GCancellable *cancellable;
2289   g_autoptr (GFile) file = NULL;
2290   g_autoptr (GTask) task = G_TASK (user_data);
2291   GeglBuffer *buffer = GEGL_BUFFER (source_object);
2292   g_autoptr (GeglBuffer) buffer_zoomed = NULL;
2293   PhotosBaseItemSaveData *data;
2294   const gchar *extension;
2295   g_autofree gchar *basename = NULL;
2296   g_autofree gchar *filename = NULL;
2297 
2298   self = PHOTOS_BASE_ITEM (g_task_get_source_object (task));
2299   priv = photos_base_item_get_instance_private (self);
2300 
2301   cancellable = g_task_get_cancellable (task);
2302   data = (PhotosBaseItemSaveData *) g_task_get_task_data (task);
2303 
2304   {
2305     g_autoptr (GError) error = NULL;
2306 
2307     buffer_zoomed = photos_gegl_buffer_zoom_finish (buffer, res, &error);
2308     if (error != NULL)
2309       {
2310         g_task_return_error (task, g_steal_pointer (&error));
2311         goto out;
2312       }
2313   }
2314 
2315   g_assert_null (data->buffer);
2316   data->buffer = g_object_ref (buffer_zoomed);
2317 
2318   basename = photos_glib_filename_strip_extension (priv->filename);
2319   extension = g_strcmp0 (priv->mime_type, "image/png") == 0 ? ".png" : ".jpg";
2320   filename = g_strconcat (basename, extension, NULL);
2321 
2322   file = g_file_get_child (data->dir, filename);
2323   photos_glib_file_create_async (file,
2324                                  G_FILE_CREATE_NONE,
2325                                  G_PRIORITY_DEFAULT,
2326                                  cancellable,
2327                                  photos_base_item_save_to_dir_file_create,
2328                                  g_object_ref (task));
2329 
2330  out:
2331   return;
2332 }
2333 
2334 
2335 static void
photos_base_item_save_to_dir_load(GObject * source_object,GAsyncResult * res,gpointer user_data)2336 photos_base_item_save_to_dir_load (GObject *source_object, GAsyncResult *res, gpointer user_data)
2337 {
2338   PhotosBaseItem *self = PHOTOS_BASE_ITEM (source_object);
2339   GCancellable *cancellable;
2340   g_autoptr (GTask) task = G_TASK (user_data);
2341   g_autoptr (GeglBuffer) buffer = NULL;
2342   g_autoptr (GeglNode) graph = NULL;
2343   PhotosBaseItemSaveData *data;
2344 
2345   cancellable = g_task_get_cancellable (task);
2346   data = (PhotosBaseItemSaveData *) g_task_get_task_data (task);
2347 
2348   {
2349     g_autoptr (GError) error = NULL;
2350 
2351     graph = photos_base_item_load_finish (self, res, &error);
2352     if (error != NULL)
2353       {
2354         g_task_return_error (task, g_steal_pointer (&error));
2355         goto out;
2356       }
2357   }
2358 
2359   buffer = photos_gegl_get_buffer_from_node (graph, NULL);
2360   photos_gegl_buffer_zoom_async (buffer,
2361                                  data->zoom,
2362                                  cancellable,
2363                                  photos_base_item_save_to_dir_buffer_zoom,
2364                                  g_object_ref (task));
2365 
2366  out:
2367   return;
2368 }
2369 
2370 
2371 static void
photos_base_item_save_to_file_save_buffer(GObject * source_object,GAsyncResult * res,gpointer user_data)2372 photos_base_item_save_to_file_save_buffer (GObject *source_object, GAsyncResult *res, gpointer user_data)
2373 {
2374   PhotosBaseItem *self = PHOTOS_BASE_ITEM (source_object);
2375   g_autoptr (GTask) task = G_TASK (user_data);
2376 
2377   {
2378     g_autoptr (GError) error = NULL;
2379 
2380     if (!photos_base_item_save_buffer_finish (self, res, &error))
2381       {
2382         g_task_return_error (task, g_steal_pointer (&error));
2383         goto out;
2384       }
2385   }
2386 
2387   g_task_return_boolean (task, TRUE);
2388 
2389  out:
2390   return;
2391 }
2392 
2393 
2394 static void
photos_base_item_save_to_file_file_replace(GObject * source_object,GAsyncResult * res,gpointer user_data)2395 photos_base_item_save_to_file_file_replace (GObject *source_object, GAsyncResult *res, gpointer user_data)
2396 {
2397   PhotosBaseItem *self;
2398   GCancellable *cancellable;
2399   GFile *file = G_FILE (source_object);
2400   g_autoptr (GFileOutputStream) stream = NULL;
2401   g_autoptr (GTask) task = G_TASK (user_data);
2402   PhotosBaseItemSaveToFileData *data;
2403 
2404   self = PHOTOS_BASE_ITEM (g_task_get_source_object (task));
2405   cancellable = g_task_get_cancellable (task);
2406   data = (PhotosBaseItemSaveToFileData *) g_task_get_task_data (task);
2407 
2408   {
2409     g_autoptr (GError) error = NULL;
2410 
2411     stream = g_file_replace_finish (file, res, &error);
2412     if (error != NULL)
2413       {
2414         g_task_return_error (task, g_steal_pointer (&error));
2415         goto out;
2416       }
2417   }
2418 
2419   photos_base_item_save_buffer_async (self,
2420                                       data->buffer,
2421                                       file,
2422                                       stream,
2423                                       cancellable,
2424                                       photos_base_item_save_to_file_save_buffer,
2425                                       g_object_ref (task));
2426 
2427  out:
2428   return;
2429 }
2430 
2431 
2432 static void
photos_base_item_save_to_file_buffer_zoom(GObject * source_object,GAsyncResult * res,gpointer user_data)2433 photos_base_item_save_to_file_buffer_zoom (GObject *source_object, GAsyncResult *res, gpointer user_data)
2434 {
2435   GCancellable *cancellable;
2436   g_autoptr (GTask) task = G_TASK (user_data);
2437   GeglBuffer *buffer = GEGL_BUFFER (source_object);
2438   g_autoptr (GeglBuffer) buffer_zoomed = NULL;
2439   PhotosBaseItemSaveToFileData *data;
2440 
2441   cancellable = g_task_get_cancellable (task);
2442   data = (PhotosBaseItemSaveToFileData *) g_task_get_task_data (task);
2443 
2444   {
2445     g_autoptr (GError) error = NULL;
2446 
2447     buffer_zoomed = photos_gegl_buffer_zoom_finish (buffer, res, &error);
2448     if (error != NULL)
2449       {
2450         g_task_return_error (task, g_steal_pointer (&error));
2451         goto out;
2452       }
2453   }
2454 
2455   g_assert_null (data->buffer);
2456   data->buffer = g_object_ref (buffer_zoomed);
2457 
2458   g_file_replace_async (data->file,
2459                         NULL,
2460                         FALSE,
2461                         data->flags,
2462                         G_PRIORITY_DEFAULT,
2463                         cancellable,
2464                         photos_base_item_save_to_file_file_replace,
2465                         g_object_ref (task));
2466 
2467  out:
2468   return;
2469 }
2470 
2471 
2472 static void
photos_base_item_save_to_file_load(GObject * source_object,GAsyncResult * res,gpointer user_data)2473 photos_base_item_save_to_file_load (GObject *source_object, GAsyncResult *res, gpointer user_data)
2474 {
2475   PhotosBaseItem *self = PHOTOS_BASE_ITEM (source_object);
2476   GCancellable *cancellable;
2477   g_autoptr (GTask) task = G_TASK (user_data);
2478   g_autoptr (GeglBuffer) buffer = NULL;
2479   g_autoptr (GeglNode) graph = NULL;
2480   PhotosBaseItemSaveToFileData *data;
2481 
2482   cancellable = g_task_get_cancellable (task);
2483   data = (PhotosBaseItemSaveToFileData *) g_task_get_task_data (task);
2484 
2485   {
2486     g_autoptr (GError) error = NULL;
2487 
2488     graph = photos_base_item_load_finish (self, res, &error);
2489     if (error != NULL)
2490       {
2491         g_task_return_error (task, g_steal_pointer (&error));
2492         goto out;
2493       }
2494   }
2495 
2496   buffer = photos_gegl_get_buffer_from_node (graph, NULL);
2497   photos_gegl_buffer_zoom_async (buffer,
2498                                  data->zoom,
2499                                  cancellable,
2500                                  photos_base_item_save_to_file_buffer_zoom,
2501                                  g_object_ref (task));
2502 
2503  out:
2504   return;
2505 }
2506 
2507 
2508 static void
photos_base_item_save_to_stream_file_delete(GObject * source_object,GAsyncResult * res,gpointer user_data)2509 photos_base_item_save_to_stream_file_delete (GObject *source_object, GAsyncResult *res, gpointer user_data)
2510 {
2511   GFile *file = G_FILE (source_object);
2512   g_autoptr (GTask) task = G_TASK (user_data);
2513 
2514   {
2515     g_autoptr (GError) error = NULL;
2516 
2517     if (!g_file_delete_finish (file, res, &error))
2518       {
2519         g_autofree gchar *uri = NULL;
2520 
2521         uri = g_file_get_uri (file);
2522         g_warning ("Unable to delete temporary file %s: %s", uri, error->message);
2523       }
2524   }
2525 
2526   /* Mark the task as a success, no matter what. The item has already
2527    * been saved to the GOutputStream, so it doesn't make sense to
2528    * fail the task just because we failed to delete the temporary
2529    * file created during the process.
2530    */
2531   g_task_return_boolean (task, TRUE);
2532 }
2533 
2534 
2535 static void
photos_base_item_save_to_stream_stream_close(GObject * source_object,GAsyncResult * res,gpointer user_data)2536 photos_base_item_save_to_stream_stream_close (GObject *source_object, GAsyncResult *res, gpointer user_data)
2537 {
2538   GCancellable *cancellable;
2539   GIOStream *iostream = G_IO_STREAM (source_object);
2540   g_autoptr (GTask) task = G_TASK (user_data);
2541   PhotosBaseItemSaveToStreamData *data;
2542 
2543   cancellable = g_task_get_cancellable (task);
2544   data = (PhotosBaseItemSaveToStreamData *) g_task_get_task_data (task);
2545 
2546   {
2547     g_autoptr (GError) error = NULL;
2548 
2549     g_io_stream_close_finish (iostream, res, &error);
2550     if (error != NULL)
2551       {
2552         g_task_return_error (task, g_steal_pointer (&error));
2553         goto out;
2554       }
2555   }
2556 
2557   g_file_delete_async (data->file,
2558                        G_PRIORITY_DEFAULT,
2559                        cancellable,
2560                        photos_base_item_save_to_stream_file_delete,
2561                        g_object_ref (task));
2562 
2563  out:
2564   return;
2565 }
2566 
2567 
2568 static void
photos_base_item_save_to_stream_stream_splice(GObject * source_object,GAsyncResult * res,gpointer user_data)2569 photos_base_item_save_to_stream_stream_splice (GObject *source_object, GAsyncResult *res, gpointer user_data)
2570 {
2571   GCancellable *cancellable;
2572   GOutputStream *ostream = G_OUTPUT_STREAM (source_object);
2573   g_autoptr (GTask) task = G_TASK (user_data);
2574   PhotosBaseItemSaveToStreamData *data;
2575 
2576   cancellable = g_task_get_cancellable (task);
2577   data = (PhotosBaseItemSaveToStreamData *) g_task_get_task_data (task);
2578 
2579   {
2580     g_autoptr (GError) error = NULL;
2581 
2582     g_output_stream_splice_finish (ostream, res, &error);
2583     if (error != NULL)
2584       {
2585         g_task_return_error (task, g_steal_pointer (&error));
2586         goto out;
2587       }
2588   }
2589 
2590   g_io_stream_close_async (G_IO_STREAM (data->iostream),
2591                            G_PRIORITY_DEFAULT,
2592                            cancellable,
2593                            photos_base_item_save_to_stream_stream_close,
2594                            g_object_ref (task));
2595 
2596  out:
2597   return;
2598 }
2599 
2600 
2601 static void
photos_base_item_save_to_stream_save_buffer(GObject * source_object,GAsyncResult * res,gpointer user_data)2602 photos_base_item_save_to_stream_save_buffer (GObject *source_object, GAsyncResult *res, gpointer user_data)
2603 {
2604   PhotosBaseItem *self = PHOTOS_BASE_ITEM (source_object);
2605   GCancellable *cancellable;
2606   GInputStream *istream;
2607   g_autoptr (GTask) task = G_TASK (user_data);
2608   PhotosBaseItemSaveToStreamData *data;
2609 
2610   cancellable = g_task_get_cancellable (task);
2611   data = (PhotosBaseItemSaveToStreamData *) g_task_get_task_data (task);
2612 
2613   {
2614     g_autoptr (GError) error = NULL;
2615 
2616     if (!photos_base_item_save_buffer_finish (self, res, &error))
2617       {
2618         g_task_return_error (task, g_steal_pointer (&error));
2619         goto out;
2620       }
2621   }
2622 
2623   istream = g_io_stream_get_input_stream (G_IO_STREAM (data->iostream));
2624   g_assert_true (g_seekable_can_seek (G_SEEKABLE (istream)));
2625 
2626   {
2627     g_autoptr (GError) error = NULL;
2628 
2629     if (!g_seekable_seek (G_SEEKABLE (istream), 0, G_SEEK_SET, cancellable, &error))
2630       {
2631         g_task_return_error (task, g_steal_pointer (&error));
2632         goto out;
2633       }
2634   }
2635 
2636   g_output_stream_splice_async (data->ostream,
2637                                 istream,
2638                                 G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
2639                                 G_PRIORITY_DEFAULT,
2640                                 cancellable,
2641                                 photos_base_item_save_to_stream_stream_splice,
2642                                 g_object_ref (task));
2643 
2644  out:
2645   return;
2646 }
2647 
2648 
2649 static void
photos_base_item_save_to_stream_buffer_zoom(GObject * source_object,GAsyncResult * res,gpointer user_data)2650 photos_base_item_save_to_stream_buffer_zoom (GObject *source_object, GAsyncResult *res, gpointer user_data)
2651 {
2652   g_autoptr (GTask) task = G_TASK (user_data);
2653   PhotosBaseItem *self;
2654   GCancellable *cancellable;
2655   g_autoptr (GFile) file = NULL;
2656   g_autoptr (GFileIOStream) iostream = NULL;
2657   GOutputStream *ostream;
2658   GeglBuffer *buffer = GEGL_BUFFER (source_object);
2659   g_autoptr (GeglBuffer) buffer_zoomed = NULL;
2660   PhotosBaseItemSaveToStreamData *data;
2661 
2662   self = PHOTOS_BASE_ITEM (g_task_get_source_object (task));
2663   cancellable = g_task_get_cancellable (task);
2664   data = (PhotosBaseItemSaveToStreamData *) g_task_get_task_data (task);
2665 
2666   {
2667     g_autoptr (GError) error = NULL;
2668 
2669     buffer_zoomed = photos_gegl_buffer_zoom_finish (buffer, res, &error);
2670     if (error != NULL)
2671       {
2672         g_task_return_error (task, g_steal_pointer (&error));
2673         goto out;
2674       }
2675   }
2676 
2677   {
2678     g_autoptr (GError) error = NULL;
2679 
2680     file = g_file_new_tmp (PACKAGE_TARNAME "-XXXXXX", &iostream, &error);
2681     if (error != NULL)
2682       {
2683         g_task_return_error (task, g_steal_pointer (&error));
2684         goto out;
2685       }
2686   }
2687 
2688   g_assert_null (data->file);
2689   data->file = g_object_ref (file);
2690 
2691   g_assert_null (data->iostream);
2692   data->iostream = g_object_ref (iostream);
2693 
2694   ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
2695   photos_base_item_save_buffer_async (self,
2696                                       buffer_zoomed,
2697                                       file,
2698                                       G_FILE_OUTPUT_STREAM (ostream),
2699                                       cancellable,
2700                                       photos_base_item_save_to_stream_save_buffer,
2701                                       g_object_ref (task));
2702 
2703  out:
2704   return;
2705 }
2706 
2707 
2708 static void
photos_base_item_save_to_stream_load(GObject * source_object,GAsyncResult * res,gpointer user_data)2709 photos_base_item_save_to_stream_load (GObject *source_object, GAsyncResult *res, gpointer user_data)
2710 {
2711   PhotosBaseItem *self = PHOTOS_BASE_ITEM (source_object);
2712   GCancellable *cancellable;
2713   g_autoptr (GTask) task = G_TASK (user_data);
2714   g_autoptr (GeglBuffer) buffer = NULL;
2715   g_autoptr (GeglNode) graph = NULL;
2716   PhotosBaseItemSaveToStreamData *data;
2717 
2718   cancellable = g_task_get_cancellable (task);
2719   data = (PhotosBaseItemSaveToStreamData *) g_task_get_task_data (task);
2720 
2721   {
2722     g_autoptr (GError) error = NULL;
2723 
2724     graph = photos_base_item_load_finish (self, res, &error);
2725     if (error != NULL)
2726       {
2727         g_task_return_error (task, g_steal_pointer (&error));
2728         goto out;
2729       }
2730   }
2731 
2732   buffer = photos_gegl_get_buffer_from_node (graph, NULL);
2733   photos_gegl_buffer_zoom_async (buffer,
2734                                  data->zoom,
2735                                  cancellable,
2736                                  photos_base_item_save_to_stream_buffer_zoom,
2737                                  g_object_ref (task));
2738 
2739  out:
2740   return;
2741 }
2742 
2743 
2744 static void
photos_base_item_trash_trash(GObject * source_object,GAsyncResult * res,gpointer user_data)2745 photos_base_item_trash_trash (GObject *source_object, GAsyncResult *res, gpointer user_data)
2746 {
2747   PhotosBaseItem *self = PHOTOS_BASE_ITEM (source_object);
2748   GApplication *app;
2749   g_autoptr (GTask) task = G_TASK (user_data);
2750 
2751   {
2752     g_autoptr (GError) error = NULL;
2753 
2754     if (!PHOTOS_BASE_ITEM_GET_CLASS (self)->trash_finish (self, res, &error))
2755       {
2756         g_task_return_error (task, g_steal_pointer (&error));
2757         goto out;
2758       }
2759   }
2760 
2761   g_task_return_boolean (task, TRUE);
2762 
2763  out:
2764   app = g_application_get_default ();
2765   g_application_release (app);
2766 }
2767 
2768 
2769 static void
photos_base_item_update_info_from_type(PhotosBaseItem * self)2770 photos_base_item_update_info_from_type (PhotosBaseItem *self)
2771 {
2772   PhotosBaseItemPrivate *priv;
2773 
2774   priv = photos_base_item_get_instance_private (self);
2775 
2776   if (strstr (priv->rdf_type, "nfo#DataContainer") != NULL)
2777     priv->collection = TRUE;
2778 
2779   PHOTOS_BASE_ITEM_GET_CLASS (self)->update_type_description (self);
2780 }
2781 
2782 
2783 static void
photos_base_item_populate_from_cursor(PhotosBaseItem * self,TrackerSparqlCursor * cursor)2784 photos_base_item_populate_from_cursor (PhotosBaseItem *self, TrackerSparqlCursor *cursor)
2785 {
2786   PhotosBaseItemPrivate *priv;
2787   gboolean favorite;
2788   const gchar *author;
2789   const gchar *ctime;
2790   const gchar *equipment;
2791   const gchar *flash;
2792   const gchar *id;
2793   const gchar *identifier;
2794   const gchar *location;
2795   const gchar *mime_type;
2796   const gchar *orientation;
2797   const gchar *rdf_type;
2798   const gchar *resource_urn;
2799   const gchar *title;
2800   const gchar *uri;
2801   gchar *filename;
2802   gchar *name_fallback;
2803   gint64 height;
2804   gint64 width;
2805 
2806   priv = photos_base_item_get_instance_private (self);
2807 
2808   uri = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_URI, NULL);
2809   if (uri == NULL)
2810     uri = "";
2811   photos_utils_set_string (&priv->uri, uri);
2812   g_object_notify (G_OBJECT (self), "uri");
2813 
2814   id = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_URN, NULL);
2815   photos_utils_set_string (&priv->id, id);
2816   g_object_notify (G_OBJECT (self), "id");
2817 
2818   identifier = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_IDENTIFIER, NULL);
2819   photos_utils_set_string (&priv->identifier, identifier);
2820 
2821   author = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_AUTHOR, NULL);
2822   photos_utils_set_string (&priv->author, author);
2823   g_object_notify (G_OBJECT (self), "secondary-text");
2824 
2825   location = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_LOCATION, NULL);
2826   photos_utils_set_string (&priv->location, location);
2827 
2828   resource_urn = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_RESOURCE_URN, NULL);
2829   photos_utils_set_string (&priv->resource_urn, resource_urn);
2830 
2831   favorite = tracker_sparql_cursor_get_boolean (cursor, PHOTOS_QUERY_COLUMNS_RESOURCE_FAVORITE);
2832 
2833   priv->mtime = photos_utils_get_mtime_from_sparql_cursor (cursor);
2834   g_object_notify (G_OBJECT (self), "mtime");
2835 
2836   mime_type = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_MIME_TYPE, NULL);
2837   photos_utils_set_string (&priv->mime_type, mime_type);
2838 
2839   rdf_type = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_RDF_TYPE, NULL);
2840   photos_utils_set_string (&priv->rdf_type, rdf_type);
2841 
2842   photos_base_item_update_info_from_type (self);
2843   priv->favorite = favorite && !priv->collection;
2844 
2845   priv->ctime = -1;
2846   ctime = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_DATE_CREATED, NULL);
2847   if (ctime != NULL)
2848     {
2849       g_autoptr (GDateTime) date_created = NULL;
2850 
2851       date_created = g_date_time_new_from_iso8601 (ctime, NULL);
2852       if (date_created != NULL)
2853         priv->ctime = g_date_time_to_unix (date_created);
2854     }
2855 
2856   if (g_strcmp0 (priv->id, PHOTOS_COLLECTION_SCREENSHOT) == 0)
2857     title = _("Screenshots");
2858   else
2859     title = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_TITLE, NULL);
2860 
2861   if (title == NULL)
2862     title = "";
2863   photos_utils_set_string (&priv->name, title);
2864   g_object_notify (G_OBJECT (self), "primary-text");
2865 
2866   filename = g_strdup (tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_FILENAME, NULL));
2867   if ((filename == NULL || filename[0] == '\0') && !priv->collection)
2868     filename = PHOTOS_BASE_ITEM_GET_CLASS (self)->create_filename_fallback (self);
2869   photos_utils_take_string (&priv->filename, filename);
2870 
2871   equipment = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_EQUIPMENT, NULL);
2872   priv->equipment = g_quark_from_string (equipment);
2873 
2874   orientation = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_ORIENTATION, NULL);
2875   priv->orientation = g_quark_from_string (orientation);
2876   if (priv->orientation != PHOTOS_ORIENTATION_BOTTOM
2877       && priv->orientation != PHOTOS_ORIENTATION_BOTTOM_MIRROR
2878       && priv->orientation != PHOTOS_ORIENTATION_LEFT
2879       && priv->orientation != PHOTOS_ORIENTATION_LEFT_MIRROR
2880       && priv->orientation != PHOTOS_ORIENTATION_RIGHT
2881       && priv->orientation != PHOTOS_ORIENTATION_RIGHT_MIRROR
2882       && priv->orientation != PHOTOS_ORIENTATION_TOP
2883       && priv->orientation != PHOTOS_ORIENTATION_TOP_MIRROR)
2884     {
2885       if (orientation != NULL)
2886         g_warning ("Unknown value for nfo:orientation: %s", orientation);
2887       priv->orientation = PHOTOS_ORIENTATION_TOP;
2888     }
2889 
2890   height = photos_utils_get_integer_from_sparql_cursor_with_default (cursor,
2891                                                                      PHOTOS_QUERY_COLUMNS_HEIGHT,
2892                                                                      0);
2893   width = photos_utils_get_integer_from_sparql_cursor_with_default (cursor,
2894                                                                     PHOTOS_QUERY_COLUMNS_WIDTH,
2895                                                                     0);
2896   if (priv->orientation == PHOTOS_ORIENTATION_BOTTOM
2897       || priv->orientation == PHOTOS_ORIENTATION_BOTTOM_MIRROR
2898       || priv->orientation == PHOTOS_ORIENTATION_TOP
2899       || priv->orientation == PHOTOS_ORIENTATION_TOP_MIRROR)
2900     {
2901       priv->height = height;
2902       priv->width = width;
2903     }
2904   else
2905     {
2906       priv->height = width;
2907       priv->width = height;
2908     }
2909 
2910   priv->exposure_time
2911     = photos_utils_get_double_from_sparql_cursor_with_default (cursor,
2912                                                                PHOTOS_QUERY_COLUMNS_EXPOSURE_TIME,
2913                                                                0.0);
2914 
2915   priv->fnumber = photos_utils_get_double_from_sparql_cursor_with_default (cursor,
2916                                                                            PHOTOS_QUERY_COLUMNS_FNUMBER,
2917                                                                            0.0);
2918 
2919   priv->focal_length
2920     = photos_utils_get_double_from_sparql_cursor_with_default (cursor,
2921                                                                PHOTOS_QUERY_COLUMNS_FOCAL_LENGTH,
2922                                                                0.0);
2923 
2924   priv->iso_speed = photos_utils_get_double_from_sparql_cursor_with_default (cursor,
2925                                                                              PHOTOS_QUERY_COLUMNS_ISO_SPEED,
2926                                                                              0.0);
2927 
2928   flash = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_FLASH, NULL);
2929   priv->flash = g_quark_from_string (flash);
2930 
2931   name_fallback = PHOTOS_BASE_ITEM_GET_CLASS (self)->create_name_fallback (self);
2932   photos_utils_take_string (&priv->name_fallback, name_fallback);
2933 
2934   PHOTOS_BASE_ITEM_GET_CLASS (self)->refresh_icon (self);
2935 }
2936 
2937 
2938 static void
photos_base_item_print_load(GObject * source_object,GAsyncResult * res,gpointer user_data)2939 photos_base_item_print_load (GObject *source_object, GAsyncResult *res, gpointer user_data)
2940 {
2941   PhotosBaseItem *self = PHOTOS_BASE_ITEM (source_object);
2942   g_autoptr (GtkWindow) toplevel = GTK_WINDOW (user_data);
2943   g_autoptr (GeglNode) node = NULL;
2944   g_autoptr (GtkPrintOperation) print_op = NULL;
2945   GtkPrintOperationResult print_res;
2946 
2947   {
2948     g_autoptr (GError) error = NULL;
2949 
2950     node = photos_base_item_load_finish (self, res, &error);
2951     if (error != NULL)
2952       {
2953         g_warning ("Unable to load the item: %s", error->message);
2954         goto out;
2955       }
2956   }
2957 
2958   print_op = photos_print_operation_new (self, node);
2959 
2960   /* It is self managing. */
2961   photos_print_notification_new (print_op);
2962 
2963   {
2964     g_autoptr (GError) error = NULL;
2965 
2966     print_res = gtk_print_operation_run (print_op, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, toplevel, &error);
2967     if (print_res == GTK_PRINT_OPERATION_RESULT_APPLY)
2968       {
2969         GAction *action;
2970         GApplication *app;
2971         GVariant *new_state;
2972 
2973         app = g_application_get_default ();
2974         action = g_action_map_lookup_action (G_ACTION_MAP (app), "selection-mode");
2975         new_state = g_variant_new ("b", FALSE);
2976         g_action_change_state (action, new_state);
2977       }
2978     else if (print_res == GTK_PRINT_OPERATION_RESULT_ERROR)
2979       {
2980         g_warning ("Unable to print file: %s", error->message);
2981       }
2982   }
2983 
2984  out:
2985   return;
2986 }
2987 
2988 
2989 static void
photos_base_item_constructed(GObject * object)2990 photos_base_item_constructed (GObject *object)
2991 {
2992   PhotosBaseItem *self = PHOTOS_BASE_ITEM (object);
2993   PhotosBaseItemPrivate *priv;
2994 
2995   priv = photos_base_item_get_instance_private (self);
2996 
2997   G_OBJECT_CLASS (photos_base_item_parent_class)->constructed (object);
2998 
2999   photos_base_item_populate_from_cursor (self, priv->cursor);
3000   g_clear_object (&priv->cursor); /* We will not need it any more */
3001 }
3002 
3003 
3004 static void
photos_base_item_dispose(GObject * object)3005 photos_base_item_dispose (GObject *object)
3006 {
3007   PhotosBaseItem *self = PHOTOS_BASE_ITEM (object);
3008   PhotosBaseItemPrivate *priv;
3009 
3010   priv = photos_base_item_get_instance_private (self);
3011 
3012   if (priv->cancellable != NULL)
3013     {
3014        g_cancellable_cancel (priv->cancellable);
3015        g_clear_object (&priv->cancellable);
3016     }
3017 
3018   photos_base_item_clear_pixels (self);
3019 
3020   g_clear_pointer (&priv->surface, cairo_surface_destroy);
3021   g_clear_object (&priv->default_app);
3022   g_clear_object (&priv->original_icon);
3023   g_clear_object (&priv->watcher);
3024   g_clear_object (&priv->cursor);
3025 
3026   G_OBJECT_CLASS (photos_base_item_parent_class)->dispose (object);
3027 }
3028 
3029 
3030 static void
photos_base_item_finalize(GObject * object)3031 photos_base_item_finalize (GObject *object)
3032 {
3033   PhotosBaseItem *self = PHOTOS_BASE_ITEM (object);
3034   PhotosBaseItemPrivate *priv;
3035 
3036   priv = photos_base_item_get_instance_private (self);
3037 
3038   g_free (priv->author);
3039   g_free (priv->default_app_name);
3040   g_free (priv->filename);
3041   g_free (priv->id);
3042   g_free (priv->identifier);
3043   g_free (priv->location);
3044   g_free (priv->mime_type);
3045   g_free (priv->name);
3046   g_free (priv->name_fallback);
3047   g_free (priv->rdf_type);
3048   g_free (priv->resource_urn);
3049   g_free (priv->thumb_path);
3050   g_free (priv->type_description);
3051   g_free (priv->uri);
3052 
3053   g_mutex_clear (&priv->mutex_download);
3054   g_mutex_clear (&priv->mutex_save_metadata);
3055 
3056   G_OBJECT_CLASS (photos_base_item_parent_class)->finalize (object);
3057 
3058   DZL_COUNTER_DEC (instances);
3059 }
3060 
3061 
3062 static void
photos_base_item_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)3063 photos_base_item_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
3064 {
3065   PhotosBaseItem *self = PHOTOS_BASE_ITEM (object);
3066   PhotosBaseItemPrivate *priv;
3067 
3068   priv = photos_base_item_get_instance_private (self);
3069 
3070   switch (prop_id)
3071     {
3072     case PROP_ICON:
3073       g_value_set_boxed (value, priv->surface);
3074       break;
3075 
3076     case PROP_ID:
3077       g_value_set_string (value, priv->id);
3078       break;
3079 
3080     case PROP_MTIME:
3081       g_value_set_int64 (value, priv->mtime);
3082       break;
3083 
3084     case PROP_PRIMARY_TEXT:
3085       {
3086         const gchar *primary_text;
3087 
3088         primary_text = photos_base_item_main_box_item_get_primary_text (GD_MAIN_BOX_ITEM (self));
3089         g_value_set_string (value, primary_text);
3090         break;
3091       }
3092 
3093     case PROP_PULSE:
3094       {
3095         gboolean pulse = priv->busy_count > 0;
3096 
3097         g_value_set_boolean (value, pulse);
3098         break;
3099       }
3100 
3101     case PROP_SECONDARY_TEXT:
3102       g_value_set_string (value, priv->author);
3103       break;
3104 
3105     case PROP_URI:
3106       g_value_set_string (value, priv->uri);
3107       break;
3108 
3109     default:
3110       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3111       break;
3112     }
3113 }
3114 
3115 
3116 static void
photos_base_item_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)3117 photos_base_item_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
3118 {
3119   PhotosBaseItem *self = PHOTOS_BASE_ITEM (object);
3120   PhotosBaseItemPrivate *priv;
3121 
3122   priv = photos_base_item_get_instance_private (self);
3123 
3124   switch (prop_id)
3125     {
3126     case PROP_CURSOR:
3127       priv->cursor = TRACKER_SPARQL_CURSOR (g_value_dup_object (value));
3128       break;
3129 
3130     case PROP_FAILED_THUMBNAILING:
3131       priv->failed_thumbnailing = g_value_get_boolean (value);
3132       break;
3133 
3134     default:
3135       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3136       break;
3137     }
3138 }
3139 
3140 
3141 static void
photos_base_item_init(PhotosBaseItem * self)3142 photos_base_item_init (PhotosBaseItem *self)
3143 {
3144   PhotosBaseItemPrivate *priv;
3145 
3146   DZL_COUNTER_INC (instances);
3147 
3148   priv = photos_base_item_get_instance_private (self);
3149 
3150   priv->cancellable = g_cancellable_new ();
3151 
3152   g_mutex_init (&priv->mutex_download);
3153   g_mutex_init (&priv->mutex_save_metadata);
3154 }
3155 
3156 
3157 static void
photos_base_item_class_init(PhotosBaseItemClass * class)3158 photos_base_item_class_init (PhotosBaseItemClass *class)
3159 {
3160   GObjectClass *object_class = G_OBJECT_CLASS (class);
3161 
3162   object_class->constructed = photos_base_item_constructed;
3163   object_class->dispose = photos_base_item_dispose;
3164   object_class->finalize = photos_base_item_finalize;
3165   object_class->get_property = photos_base_item_get_property;
3166   object_class->set_property = photos_base_item_set_property;
3167   class->create_thumbnail_path = photos_base_item_default_create_thumbnail_path;
3168   class->metadata_add_shared = photos_base_item_default_metadata_add_shared;
3169   class->open = photos_base_item_default_open;
3170   class->refresh_icon = photos_base_item_default_refresh_icon;
3171   class->set_favorite = photos_base_item_default_set_favorite;
3172   class->update_type_description = photos_base_item_default_update_type_description;
3173 
3174   g_object_class_install_property (object_class,
3175                                    PROP_CURSOR,
3176                                    g_param_spec_object ("cursor",
3177                                                         "TrackerSparqlCursor object",
3178                                                         "A cursor to iterate over the results of a query",
3179                                                         TRACKER_SPARQL_TYPE_CURSOR,
3180                                                         G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
3181 
3182   g_object_class_install_property (object_class,
3183                                    PROP_FAILED_THUMBNAILING,
3184                                    g_param_spec_boolean ("failed-thumbnailing",
3185                                                          "Thumbnailing failed",
3186                                                          "Failed to create a thumbnail",
3187                                                          FALSE,
3188                                                          G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
3189 
3190   signals[INFO_UPDATED] = g_signal_new ("info-updated",
3191                                         G_TYPE_FROM_CLASS (class),
3192                                         G_SIGNAL_RUN_LAST,
3193                                         G_STRUCT_OFFSET (PhotosBaseItemClass, info_updated),
3194                                         NULL, /* accumulator */
3195                                         NULL, /* accu_data */
3196                                         g_cclosure_marshal_VOID__VOID,
3197                                         G_TYPE_NONE,
3198                                         0);
3199 
3200   g_object_class_override_property (object_class, PROP_ICON, "icon");
3201   g_object_class_override_property (object_class, PROP_ID, "id");
3202   g_object_class_override_property (object_class, PROP_MTIME, "mtime");
3203   g_object_class_override_property (object_class, PROP_PRIMARY_TEXT, "primary-text");
3204   g_object_class_override_property (object_class, PROP_PULSE, "pulse");
3205   g_object_class_override_property (object_class, PROP_SECONDARY_TEXT, "secondary-text");
3206   g_object_class_override_property (object_class, PROP_URI, "uri");
3207 
3208   pipeline_cache = dzl_task_cache_new (g_direct_hash,
3209                                        g_direct_equal,
3210                                        NULL,
3211                                        NULL,
3212                                        g_object_ref,
3213                                        g_object_unref,
3214                                        0,
3215                                        photos_base_item_load_pipeline_task_cache_populate,
3216                                        NULL,
3217                                        NULL);
3218   dzl_task_cache_set_name (pipeline_cache, "PhotosPipeline cache");
3219 
3220   create_thumbnail_pool = g_thread_pool_new (photos_base_item_create_thumbnail_in_thread_func,
3221                                              NULL,
3222                                              1,
3223                                              FALSE,
3224                                              NULL);
3225   g_thread_pool_set_sort_function (create_thumbnail_pool, photos_base_item_create_thumbnail_sort_func, NULL);
3226 }
3227 
3228 
3229 static void
photos_base_item_filterable_iface_init(PhotosFilterableInterface * iface)3230 photos_base_item_filterable_iface_init (PhotosFilterableInterface *iface)
3231 {
3232   iface->get_id = photos_base_item_filterable_get_id;
3233   iface->is_search_criterion = photos_base_item_filterable_is_search_criterion;
3234 }
3235 
3236 
3237 static void
photos_base_item_main_box_item_iface_init(GdMainBoxItemInterface * iface)3238 photos_base_item_main_box_item_iface_init (GdMainBoxItemInterface *iface)
3239 {
3240   iface->get_icon = photos_base_item_main_box_item_get_icon;
3241   iface->get_id = photos_base_item_main_box_item_get_id;
3242   iface->get_primary_text = photos_base_item_main_box_item_get_primary_text;
3243   iface->get_secondary_text = photos_base_item_main_box_item_get_secondary_text;
3244   iface->get_uri = photos_base_item_main_box_item_get_uri;
3245 }
3246 
3247 
3248 gboolean
photos_base_item_can_edit(PhotosBaseItem * self)3249 photos_base_item_can_edit (PhotosBaseItem *self)
3250 {
3251   PhotosBaseItemPrivate *priv;
3252 
3253   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), FALSE);
3254   priv = photos_base_item_get_instance_private (self);
3255 
3256   g_return_val_if_fail (!priv->collection, FALSE);
3257 
3258   return PHOTOS_BASE_ITEM_GET_CLASS (self)->create_pipeline_paths != NULL;
3259 }
3260 
3261 
3262 gboolean
photos_base_item_can_trash(PhotosBaseItem * self)3263 photos_base_item_can_trash (PhotosBaseItem *self)
3264 {
3265   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), FALSE);
3266   return PHOTOS_BASE_ITEM_GET_CLASS (self)->trash_async != NULL;
3267 }
3268 
3269 
3270 GStrv
photos_base_item_create_pipeline_paths(PhotosBaseItem * self)3271 photos_base_item_create_pipeline_paths (PhotosBaseItem *self)
3272 {
3273   GStrv paths;
3274 
3275   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), NULL);
3276 
3277   paths = PHOTOS_BASE_ITEM_GET_CLASS (self)->create_pipeline_paths (self);
3278   return paths;
3279 }
3280 
3281 
3282 cairo_surface_t *
photos_base_item_create_preview(PhotosBaseItem * self,gint size,gint scale,const gchar * operation,const gchar * first_property_name,...)3283 photos_base_item_create_preview (PhotosBaseItem *self,
3284                                  gint size,
3285                                  gint scale,
3286                                  const gchar *operation,
3287                                  const gchar *first_property_name,
3288                                  ...)
3289 {
3290   PhotosBaseItemPrivate *priv;
3291   const Babl *format;
3292   GeglBuffer *preview_source_buffer;
3293   GeglNode *buffer_source;
3294   g_autoptr (GeglNode) graph = NULL;
3295   GeglNode *operation_node;
3296   GeglOperation *op;
3297   GeglRectangle bbox;
3298   cairo_surface_t *surface = NULL;
3299   static const cairo_user_data_key_t key;
3300   const gchar *name;
3301   gint stride;
3302   gint64 end;
3303   gint64 start;
3304   guchar *buf = NULL;
3305   va_list ap;
3306 
3307   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), NULL);
3308   priv = photos_base_item_get_instance_private (self);
3309 
3310   g_return_val_if_fail (!priv->collection, NULL);
3311   g_return_val_if_fail (operation != NULL && operation[0] != '\0', NULL);
3312   g_return_val_if_fail (priv->buffer_source != NULL, NULL);
3313   g_return_val_if_fail (priv->edit_graph != NULL, NULL);
3314 
3315   op = gegl_node_get_gegl_operation (priv->buffer_source);
3316   g_return_val_if_fail (op != NULL, NULL);
3317 
3318   name = gegl_operation_get_name (op);
3319   g_return_val_if_fail (g_strcmp0 (name, "gegl:buffer-source") == 0, NULL);
3320 
3321   preview_source_buffer = photos_base_item_get_preview_source_buffer (self, size, scale);
3322   g_return_val_if_fail (GEGL_IS_BUFFER (preview_source_buffer), NULL);
3323 
3324   graph = gegl_node_new ();
3325   buffer_source = gegl_node_new_child (graph,
3326                                        "operation", "gegl:buffer-source",
3327                                        "buffer", preview_source_buffer,
3328                                        NULL);
3329 
3330   operation_node = gegl_node_new_child (graph, "operation", operation, NULL);
3331 
3332   va_start (ap, first_property_name);
3333   gegl_node_set_valist (operation_node, first_property_name, ap);
3334   va_end (ap);
3335 
3336   gegl_node_link_many (buffer_source, operation_node, NULL);
3337 
3338   start = g_get_monotonic_time ();
3339 
3340   gegl_node_process (operation_node);
3341 
3342   end = g_get_monotonic_time ();
3343   photos_debug (PHOTOS_DEBUG_GEGL, "Create Preview: Process: %" G_GINT64_FORMAT, end - start);
3344 
3345   bbox = gegl_node_get_bounding_box (operation_node);
3346   stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, bbox.width);
3347   buf = g_malloc0 (stride * bbox.height);
3348   format = babl_format ("cairo-ARGB32");
3349 
3350   start = g_get_monotonic_time ();
3351 
3352   gegl_node_blit (operation_node, 1.0, &bbox, format, buf, stride, GEGL_BLIT_DEFAULT);
3353 
3354   end = g_get_monotonic_time ();
3355   photos_debug (PHOTOS_DEBUG_GEGL, "Create Preview: Node Blit: %" G_GINT64_FORMAT, end - start);
3356 
3357   surface = cairo_image_surface_create_for_data (buf, CAIRO_FORMAT_ARGB32, bbox.width, bbox.height, stride);
3358   cairo_surface_set_device_scale (surface, (gdouble) scale, (gdouble) scale);
3359   cairo_surface_set_user_data (surface, &key, buf, (cairo_destroy_func_t) g_free);
3360 
3361   return surface;
3362 }
3363 
3364 
3365 gchar *
photos_base_item_create_thumbnail_path(PhotosBaseItem * self)3366 photos_base_item_create_thumbnail_path (PhotosBaseItem *self)
3367 {
3368   gchar *path;
3369 
3370   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), NULL);
3371 
3372   path = PHOTOS_BASE_ITEM_GET_CLASS (self)->create_thumbnail_path (self);
3373   return path;
3374 }
3375 
3376 
3377 void
photos_base_item_destroy(PhotosBaseItem * self)3378 photos_base_item_destroy (PhotosBaseItem *self)
3379 {
3380   PhotosBaseItemPrivate *priv;
3381 
3382   g_return_if_fail (PHOTOS_IS_BASE_ITEM (self));
3383   priv = photos_base_item_get_instance_private (self);
3384 
3385   /* TODO: SearchCategoryManager */
3386   g_clear_object (&priv->watcher);
3387 }
3388 
3389 
3390 GFile *
photos_base_item_download(PhotosBaseItem * self,GCancellable * cancellable,GError ** error)3391 photos_base_item_download (PhotosBaseItem *self, GCancellable *cancellable, GError **error)
3392 {
3393   PhotosBaseItemPrivate *priv;
3394   g_autoptr (GFile) file = NULL;
3395   GFile *ret_val = NULL;
3396 
3397   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), NULL);
3398   priv = photos_base_item_get_instance_private (self);
3399 
3400   g_mutex_lock (&priv->mutex_download);
3401   file = PHOTOS_BASE_ITEM_GET_CLASS (self)->download (self, cancellable, error);
3402   g_mutex_unlock (&priv->mutex_download);
3403 
3404   if (file == NULL)
3405     goto out;
3406 
3407   g_return_val_if_fail (g_file_is_native (file), NULL);
3408   ret_val = g_object_ref (file);
3409 
3410  out:
3411   return ret_val;
3412 }
3413 
3414 
3415 void
photos_base_item_download_async(PhotosBaseItem * self,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)3416 photos_base_item_download_async (PhotosBaseItem *self,
3417                                  GCancellable *cancellable,
3418                                  GAsyncReadyCallback callback,
3419                                  gpointer user_data)
3420 {
3421   g_autoptr (GTask) task = NULL;
3422 
3423   g_return_if_fail (PHOTOS_IS_BASE_ITEM (self));
3424 
3425   task = g_task_new (self, cancellable, callback, user_data);
3426   g_task_set_source_tag (task, photos_base_item_download_async);
3427 
3428   g_task_run_in_thread (task, photos_base_item_download_in_thread_func);
3429 }
3430 
3431 
3432 GFile *
photos_base_item_download_finish(PhotosBaseItem * self,GAsyncResult * res,GError ** error)3433 photos_base_item_download_finish (PhotosBaseItem *self, GAsyncResult *res, GError **error)
3434 {
3435   GTask *task;
3436 
3437   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), NULL);
3438 
3439   g_return_val_if_fail (g_task_is_valid (res, self), NULL);
3440   task = G_TASK (res);
3441 
3442   g_return_val_if_fail (g_task_get_source_tag (task) == photos_base_item_download_async, NULL);
3443   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
3444 
3445   return g_task_propagate_pointer (task, error);
3446 }
3447 
3448 
3449 const gchar *
photos_base_item_get_author(PhotosBaseItem * self)3450 photos_base_item_get_author (PhotosBaseItem *self)
3451 {
3452   PhotosBaseItemPrivate *priv;
3453 
3454   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), NULL);
3455   priv = photos_base_item_get_instance_private (self);
3456 
3457   return priv->author;
3458 }
3459 
3460 
3461 gboolean
photos_base_item_get_bbox_edited(PhotosBaseItem * self,GeglRectangle * out_bbox)3462 photos_base_item_get_bbox_edited (PhotosBaseItem *self, GeglRectangle *out_bbox)
3463 {
3464   PhotosBaseItemPrivate *priv;
3465   GeglNode *graph;
3466   GeglRectangle bbox;
3467   PhotosPipeline *pipeline;
3468 
3469   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), FALSE);
3470   priv = photos_base_item_get_instance_private (self);
3471 
3472   g_return_val_if_fail (!priv->collection, FALSE);
3473   g_return_val_if_fail (priv->edit_graph != NULL, FALSE);
3474 
3475   pipeline = PHOTOS_PIPELINE (dzl_task_cache_peek (pipeline_cache, self));
3476   g_return_val_if_fail (pipeline != NULL, FALSE);
3477 
3478   g_return_val_if_fail (priv->processor != NULL, FALSE);
3479   g_return_val_if_fail (!gegl_processor_work (priv->processor, NULL), FALSE);
3480 
3481   graph = photos_pipeline_get_graph (pipeline);
3482   bbox = gegl_node_get_bounding_box (graph);
3483 
3484   if (out_bbox != NULL)
3485     *out_bbox = bbox;
3486 
3487   return TRUE;
3488 }
3489 
3490 
3491 gboolean
photos_base_item_get_bbox_source(PhotosBaseItem * self,GeglRectangle * bbox)3492 photos_base_item_get_bbox_source (PhotosBaseItem *self, GeglRectangle *bbox)
3493 {
3494   PhotosBaseItemPrivate *priv;
3495   gboolean ret_val = FALSE;
3496 
3497   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), FALSE);
3498   priv = photos_base_item_get_instance_private (self);
3499 
3500   g_return_val_if_fail (!priv->collection, FALSE);
3501 
3502   if (priv->buffer_source == NULL)
3503     goto out;
3504 
3505   *bbox = gegl_node_get_bounding_box (priv->buffer_source);
3506   ret_val = TRUE;
3507 
3508  out:
3509   return ret_val;
3510 }
3511 
3512 
3513 gint64
photos_base_item_get_date_created(PhotosBaseItem * self)3514 photos_base_item_get_date_created (PhotosBaseItem *self)
3515 {
3516   PhotosBaseItemPrivate *priv;
3517 
3518   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), 0);
3519   priv = photos_base_item_get_instance_private (self);
3520 
3521   return priv->ctime;
3522 }
3523 
3524 
3525 const gchar *
photos_base_item_get_default_app_name(PhotosBaseItem * self)3526 photos_base_item_get_default_app_name (PhotosBaseItem *self)
3527 {
3528   PhotosBaseItemPrivate *priv;
3529 
3530   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), NULL);
3531   priv = photos_base_item_get_instance_private (self);
3532 
3533   return priv->default_app_name;
3534 }
3535 
3536 
3537 GQuark
photos_base_item_get_equipment(PhotosBaseItem * self)3538 photos_base_item_get_equipment (PhotosBaseItem *self)
3539 {
3540   PhotosBaseItemPrivate *priv;
3541 
3542   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), 0);
3543   priv = photos_base_item_get_instance_private (self);
3544 
3545   return priv->equipment;
3546 }
3547 
3548 
3549 gdouble
photos_base_item_get_exposure_time(PhotosBaseItem * self)3550 photos_base_item_get_exposure_time (PhotosBaseItem *self)
3551 {
3552   PhotosBaseItemPrivate *priv;
3553 
3554   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), 0.0);
3555   priv = photos_base_item_get_instance_private (self);
3556 
3557   return priv->exposure_time;
3558 }
3559 
3560 
3561 GQuark
photos_base_item_get_flash(PhotosBaseItem * self)3562 photos_base_item_get_flash (PhotosBaseItem *self)
3563 {
3564   PhotosBaseItemPrivate *priv;
3565 
3566   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), 0);
3567   priv = photos_base_item_get_instance_private (self);
3568 
3569   return priv->flash;
3570 }
3571 
3572 
3573 const gchar *
photos_base_item_get_filename(PhotosBaseItem * self)3574 photos_base_item_get_filename (PhotosBaseItem *self)
3575 {
3576   PhotosBaseItemPrivate *priv;
3577 
3578   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), NULL);
3579   priv = photos_base_item_get_instance_private (self);
3580 
3581   return priv->filename;
3582 }
3583 
3584 
3585 gdouble
photos_base_item_get_fnumber(PhotosBaseItem * self)3586 photos_base_item_get_fnumber (PhotosBaseItem *self)
3587 {
3588   PhotosBaseItemPrivate *priv;
3589 
3590   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), 0.0);
3591   priv = photos_base_item_get_instance_private (self);
3592 
3593   return priv->fnumber;
3594 }
3595 
3596 
3597 gdouble
photos_base_item_get_focal_length(PhotosBaseItem * self)3598 photos_base_item_get_focal_length (PhotosBaseItem *self)
3599 {
3600   PhotosBaseItemPrivate *priv;
3601 
3602   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), 0.0);
3603   priv = photos_base_item_get_instance_private (self);
3604 
3605   return priv->focal_length;
3606 }
3607 
3608 
3609 gint64
photos_base_item_get_height(PhotosBaseItem * self)3610 photos_base_item_get_height (PhotosBaseItem *self)
3611 {
3612   PhotosBaseItemPrivate *priv;
3613 
3614   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), 0);
3615   priv = photos_base_item_get_instance_private (self);
3616 
3617   return priv->height;
3618 }
3619 
3620 
3621 const gchar *
photos_base_item_get_identifier(PhotosBaseItem * self)3622 photos_base_item_get_identifier (PhotosBaseItem *self)
3623 {
3624   PhotosBaseItemPrivate *priv;
3625 
3626   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), NULL);
3627   priv = photos_base_item_get_instance_private (self);
3628 
3629   return priv->identifier;
3630 }
3631 
3632 
3633 gdouble
photos_base_item_get_iso_speed(PhotosBaseItem * self)3634 photos_base_item_get_iso_speed (PhotosBaseItem *self)
3635 {
3636   PhotosBaseItemPrivate *priv;
3637 
3638   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), 0.0);
3639   priv = photos_base_item_get_instance_private (self);
3640 
3641   return priv->iso_speed;
3642 }
3643 
3644 
3645 const gchar *
photos_base_item_get_location(PhotosBaseItem * self)3646 photos_base_item_get_location (PhotosBaseItem *self)
3647 {
3648   PhotosBaseItemPrivate *priv;
3649 
3650   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), NULL);
3651   priv = photos_base_item_get_instance_private (self);
3652 
3653   return priv->location;
3654 }
3655 
3656 
3657 const gchar *
photos_base_item_get_mime_type(PhotosBaseItem * self)3658 photos_base_item_get_mime_type (PhotosBaseItem *self)
3659 {
3660   PhotosBaseItemPrivate *priv;
3661 
3662   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), NULL);
3663   priv = photos_base_item_get_instance_private (self);
3664 
3665   return priv->mime_type;
3666 }
3667 
3668 
3669 gint64
photos_base_item_get_mtime(PhotosBaseItem * self)3670 photos_base_item_get_mtime (PhotosBaseItem *self)
3671 {
3672   PhotosBaseItemPrivate *priv;
3673 
3674   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), 0);
3675   priv = photos_base_item_get_instance_private (self);
3676 
3677   return priv->mtime;
3678 }
3679 
3680 
3681 const gchar *
photos_base_item_get_name(PhotosBaseItem * self)3682 photos_base_item_get_name (PhotosBaseItem *self)
3683 {
3684   PhotosBaseItemPrivate *priv;
3685 
3686   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), NULL);
3687   priv = photos_base_item_get_instance_private (self);
3688 
3689   return priv->name;
3690 }
3691 
3692 
3693 const gchar *
photos_base_item_get_name_with_fallback(PhotosBaseItem * self)3694 photos_base_item_get_name_with_fallback (PhotosBaseItem *self)
3695 {
3696   PhotosBaseItemPrivate *priv;
3697   const gchar *name;
3698 
3699   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), NULL);
3700   priv = photos_base_item_get_instance_private (self);
3701 
3702   name = priv->name;
3703   if (name == NULL || name[0] == '\0')
3704     name = priv->name_fallback;
3705 
3706   return name;
3707 }
3708 
3709 
3710 GQuark
photos_base_item_get_orientation(PhotosBaseItem * self)3711 photos_base_item_get_orientation (PhotosBaseItem *self)
3712 {
3713   PhotosBaseItemPrivate *priv;
3714 
3715   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), 0);
3716   priv = photos_base_item_get_instance_private (self);
3717 
3718   return priv->orientation;
3719 }
3720 
3721 
3722 GdkPixbuf *
photos_base_item_get_original_icon(PhotosBaseItem * self)3723 photos_base_item_get_original_icon (PhotosBaseItem *self)
3724 {
3725   PhotosBaseItemPrivate *priv;
3726 
3727   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), NULL);
3728   priv = photos_base_item_get_instance_private (self);
3729 
3730   return priv->original_icon;
3731 }
3732 
3733 
3734 const gchar *
photos_base_item_get_resource_urn(PhotosBaseItem * self)3735 photos_base_item_get_resource_urn (PhotosBaseItem *self)
3736 {
3737   PhotosBaseItemPrivate *priv;
3738 
3739   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), NULL);
3740   priv = photos_base_item_get_instance_private (self);
3741 
3742   return priv->resource_urn;
3743 }
3744 
3745 
3746 GtkWidget *
photos_base_item_get_source_widget(PhotosBaseItem * self)3747 photos_base_item_get_source_widget (PhotosBaseItem *self)
3748 {
3749   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), NULL);
3750   return PHOTOS_BASE_ITEM_GET_CLASS (self)->get_source_widget(self);
3751 }
3752 
3753 
3754 cairo_surface_t *
photos_base_item_get_surface(PhotosBaseItem * self)3755 photos_base_item_get_surface (PhotosBaseItem *self)
3756 {
3757   PhotosBaseItemPrivate *priv;
3758 
3759   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), NULL);
3760   priv = photos_base_item_get_instance_private (self);
3761 
3762   return priv->surface;
3763 }
3764 
3765 
3766 const gchar *
photos_base_item_get_type_description(PhotosBaseItem * self)3767 photos_base_item_get_type_description (PhotosBaseItem *self)
3768 {
3769   PhotosBaseItemPrivate *priv;
3770 
3771   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), NULL);
3772   priv = photos_base_item_get_instance_private (self);
3773 
3774   return priv->type_description;
3775 }
3776 
3777 
3778 const gchar *
photos_base_item_get_uri(PhotosBaseItem * self)3779 photos_base_item_get_uri (PhotosBaseItem *self)
3780 {
3781   PhotosBaseItemPrivate *priv;
3782 
3783   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), NULL);
3784   priv = photos_base_item_get_instance_private (self);
3785 
3786   return priv->uri;
3787 }
3788 
3789 
3790 gchar *
photos_base_item_get_where(PhotosBaseItem * self)3791 photos_base_item_get_where (PhotosBaseItem *self)
3792 {
3793   PhotosBaseItemPrivate *priv;
3794   gchar *ret_val;
3795 
3796   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), NULL);
3797   priv = photos_base_item_get_instance_private (self);
3798 
3799   if (priv->collection)
3800     ret_val = g_strconcat ("{ ?urn nie:isLogicalPartOf <", priv->id, "> }", NULL);
3801   else
3802     ret_val = g_strdup ("");
3803 
3804   return ret_val;
3805 }
3806 
3807 
3808 gint64
photos_base_item_get_width(PhotosBaseItem * self)3809 photos_base_item_get_width (PhotosBaseItem *self)
3810 {
3811   PhotosBaseItemPrivate *priv;
3812 
3813   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), 0);
3814   priv = photos_base_item_get_instance_private (self);
3815 
3816   return priv->width;
3817 }
3818 
3819 
3820 void
photos_base_item_guess_save_sizes_async(PhotosBaseItem * self,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)3821 photos_base_item_guess_save_sizes_async (PhotosBaseItem *self,
3822                                          GCancellable *cancellable,
3823                                          GAsyncReadyCallback callback,
3824                                          gpointer user_data)
3825 {
3826   PhotosBaseItemPrivate *priv;
3827   g_autoptr (GTask) task = NULL;
3828 
3829   g_return_if_fail (PHOTOS_IS_BASE_ITEM (self));
3830   priv = photos_base_item_get_instance_private (self);
3831 
3832   g_return_if_fail (!priv->collection);
3833 
3834   task = g_task_new (self, cancellable, callback, user_data);
3835   g_task_set_source_tag (task, photos_base_item_guess_save_sizes_async);
3836 
3837   photos_base_item_load_async (self, cancellable, photos_base_item_guess_save_sizes_load, g_object_ref (task));
3838 }
3839 
3840 
3841 gboolean
photos_base_item_guess_save_sizes_finish(PhotosBaseItem * self,GAsyncResult * res,PhotosBaseItemSize * out_full_size,PhotosBaseItemSize * out_reduced_size,GError ** error)3842 photos_base_item_guess_save_sizes_finish (PhotosBaseItem *self,
3843                                           GAsyncResult *res,
3844                                           PhotosBaseItemSize *out_full_size,
3845                                           PhotosBaseItemSize *out_reduced_size,
3846                                           GError **error)
3847 {
3848   GTask *task;
3849   GeglRectangle bbox;
3850   gboolean ret_val = FALSE;
3851   gint max_dimension;
3852   gdouble reduced_zoom = -1.0;
3853   guint i;
3854   gsize *sizes;
3855 
3856   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), FALSE);
3857 
3858   g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
3859   task = G_TASK (res);
3860 
3861   g_return_val_if_fail (g_task_get_source_tag (task) == photos_base_item_guess_save_sizes_async, FALSE);
3862   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
3863 
3864   sizes = g_task_propagate_pointer (task, error);
3865   if (sizes == NULL)
3866     goto out;
3867 
3868   if (!photos_base_item_get_bbox_edited (self, &bbox))
3869     {
3870       g_set_error (error, PHOTOS_ERROR, 0, "Failed to get the bounding box");
3871       goto out;
3872     }
3873 
3874   ret_val = TRUE;
3875 
3876   max_dimension = MAX (bbox.height, bbox.width);
3877   for (i = 0; i < G_N_ELEMENTS (PIXEL_SIZES); i++)
3878     {
3879       if (max_dimension > PIXEL_SIZES[i])
3880         {
3881           reduced_zoom = (gdouble) PIXEL_SIZES[i] / (gdouble) max_dimension;
3882           break;
3883         }
3884     }
3885 
3886   if (out_full_size != NULL)
3887     {
3888       out_full_size->height = bbox.height;
3889       out_full_size->width = bbox.width;
3890       out_full_size->bytes = sizes[0];
3891       out_full_size->zoom = 1.0;
3892     }
3893 
3894   if (out_reduced_size != NULL)
3895     {
3896       out_reduced_size->zoom = reduced_zoom;
3897       if (reduced_zoom > 0.0)
3898         {
3899           out_reduced_size->height = (gint) ((gdouble) bbox.height * reduced_zoom + 0.5);
3900           out_reduced_size->width = (gint) ((gdouble) bbox.width * reduced_zoom + 0.5);
3901           out_reduced_size->bytes = (gsize) (sizes[1]
3902                                              + (sizes[0] - sizes[1]) * (reduced_zoom - 0.5) / (1.0 - 0.5)
3903                                              + 0.5);
3904         }
3905     }
3906 
3907  out:
3908   return ret_val;
3909 }
3910 
3911 
3912 gboolean
photos_base_item_is_collection(PhotosBaseItem * self)3913 photos_base_item_is_collection (PhotosBaseItem *self)
3914 {
3915   PhotosBaseItemPrivate *priv;
3916 
3917   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), FALSE);
3918   priv = photos_base_item_get_instance_private (self);
3919 
3920   return priv->collection;
3921 }
3922 
3923 
3924 gboolean
photos_base_item_is_favorite(PhotosBaseItem * self)3925 photos_base_item_is_favorite (PhotosBaseItem *self)
3926 {
3927   PhotosBaseItemPrivate *priv;
3928 
3929   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), FALSE);
3930   priv = photos_base_item_get_instance_private (self);
3931 
3932   return priv->favorite;
3933 }
3934 
3935 
3936 void
photos_base_item_load_async(PhotosBaseItem * self,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)3937 photos_base_item_load_async (PhotosBaseItem *self,
3938                              GCancellable *cancellable,
3939                              GAsyncReadyCallback callback,
3940                              gpointer user_data)
3941 {
3942   PhotosBaseItemPrivate *priv;
3943   g_autoptr (GTask) task = NULL;
3944   PhotosPipeline *pipeline;
3945 
3946   g_return_if_fail (PHOTOS_IS_BASE_ITEM (self));
3947   priv = photos_base_item_get_instance_private (self);
3948 
3949   g_return_if_fail (!priv->collection);
3950 
3951   pipeline = PHOTOS_PIPELINE (dzl_task_cache_peek (pipeline_cache, self));
3952   g_return_if_fail (priv->edit_graph == NULL || (GEGL_IS_NODE (priv->edit_graph) && PHOTOS_IS_PIPELINE (pipeline)));
3953 
3954   task = g_task_new (self, cancellable, callback, user_data);
3955   g_task_set_source_tag (task, photos_base_item_load_async);
3956 
3957   if (priv->edit_graph != NULL)
3958     {
3959       GeglNode *graph;
3960 
3961       graph = photos_pipeline_get_graph (pipeline);
3962       g_task_return_pointer (task, g_object_ref (graph), g_object_unref);
3963       goto out;
3964     }
3965 
3966   photos_base_item_load_pipeline_async (self,
3967                                         cancellable,
3968                                         photos_base_item_load_load_pipeline,
3969                                         g_object_ref (task));
3970 
3971  out:
3972   return;
3973 }
3974 
3975 
3976 GeglNode *
photos_base_item_load_finish(PhotosBaseItem * self,GAsyncResult * res,GError ** error)3977 photos_base_item_load_finish (PhotosBaseItem *self, GAsyncResult *res, GError **error)
3978 {
3979   GTask *task;
3980 
3981   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), NULL);
3982 
3983   g_return_val_if_fail (g_task_is_valid (res, self), NULL);
3984   task = G_TASK (res);
3985 
3986   g_return_val_if_fail (g_task_get_source_tag (task) == photos_base_item_load_async, NULL);
3987   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
3988 
3989   return g_task_propagate_pointer (task, error);
3990 }
3991 
3992 
3993 void
photos_base_item_mark_busy(PhotosBaseItem * self)3994 photos_base_item_mark_busy (PhotosBaseItem *self)
3995 {
3996   PhotosBaseItemPrivate *priv;
3997 
3998   g_return_if_fail (PHOTOS_IS_BASE_ITEM (self));
3999   priv = photos_base_item_get_instance_private (self);
4000 
4001   priv->busy_count++;
4002   if (priv->busy_count == 1)
4003     g_object_notify (G_OBJECT (self), "pulse");
4004 }
4005 
4006 
4007 void
photos_base_item_metadata_add_shared_async(PhotosBaseItem * self,const gchar * provider_type,const gchar * account_identity,const gchar * shared_id,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)4008 photos_base_item_metadata_add_shared_async (PhotosBaseItem *self,
4009                                             const gchar *provider_type,
4010                                             const gchar *account_identity,
4011                                             const gchar *shared_id,
4012                                             GCancellable *cancellable,
4013                                             GAsyncReadyCallback callback,
4014                                             gpointer user_data)
4015 {
4016   PhotosBaseItemPrivate *priv;
4017   g_autoptr (GTask) task = NULL;
4018   PhotosBaseItemMetadataAddSharedData *data;
4019 
4020   g_return_if_fail (PHOTOS_IS_BASE_ITEM (self));
4021   priv = photos_base_item_get_instance_private (self);
4022 
4023   g_return_if_fail (!priv->collection);
4024   g_return_if_fail (provider_type != NULL && provider_type[0] != '\0');
4025   g_return_if_fail (account_identity != NULL && account_identity[0] != '\0');
4026   g_return_if_fail (shared_id != NULL && shared_id[0] != '\0');
4027 
4028   data = photos_base_item_metadata_add_shared_data_new (provider_type, account_identity, shared_id);
4029 
4030   task = g_task_new (self, cancellable, callback, user_data);
4031   g_task_set_source_tag (task, photos_base_item_metadata_add_shared_async);
4032   g_task_set_task_data (task, data, (GDestroyNotify) photos_base_item_metadata_add_shared_data_free);
4033 
4034   g_task_run_in_thread (task, photos_base_item_metadata_add_shared_in_thread_func);
4035 }
4036 
4037 
4038 gboolean
photos_base_item_metadata_add_shared_finish(PhotosBaseItem * self,GAsyncResult * res,GError ** error)4039 photos_base_item_metadata_add_shared_finish (PhotosBaseItem *self, GAsyncResult *res, GError **error)
4040 {
4041   GTask *task;
4042 
4043   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), FALSE);
4044 
4045   g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
4046   task = G_TASK (res);
4047 
4048   g_return_val_if_fail (g_task_get_source_tag (task) == photos_base_item_metadata_add_shared_async, FALSE);
4049   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
4050 
4051   return g_task_propagate_boolean (task, error);
4052 }
4053 
4054 
4055 void
photos_base_item_open(PhotosBaseItem * self,GtkWindow * parent,guint32 timestamp)4056 photos_base_item_open (PhotosBaseItem *self, GtkWindow *parent, guint32 timestamp)
4057 {
4058   g_return_if_fail (PHOTOS_IS_BASE_ITEM (self));
4059   PHOTOS_BASE_ITEM_GET_CLASS (self)->open (self, parent, timestamp);
4060 }
4061 
4062 
4063 void
photos_base_item_operation_add_async(PhotosBaseItem * self,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data,const gchar * operation,const gchar * first_property_name,...)4064 photos_base_item_operation_add_async (PhotosBaseItem *self,
4065                                       GCancellable *cancellable,
4066                                       GAsyncReadyCallback callback,
4067                                       gpointer user_data,
4068                                       const gchar *operation,
4069                                       const gchar *first_property_name,
4070                                       ...)
4071 {
4072   PhotosBaseItemPrivate *priv;
4073   g_autoptr (GTask) task = NULL;
4074   PhotosPipeline *pipeline;
4075   va_list ap;
4076 
4077   g_return_if_fail (PHOTOS_IS_BASE_ITEM (self));
4078   priv = photos_base_item_get_instance_private (self);
4079 
4080   g_return_if_fail (!priv->collection);
4081 
4082   pipeline = PHOTOS_PIPELINE (dzl_task_cache_peek (pipeline_cache, self));
4083   g_return_if_fail (PHOTOS_IS_PIPELINE (pipeline));
4084 
4085   va_start (ap, first_property_name);
4086   photos_pipeline_add_valist (pipeline, operation, first_property_name, ap);
4087   va_end (ap);
4088 
4089   task = g_task_new (self, cancellable, callback, user_data);
4090   g_task_set_source_tag (task, photos_base_item_operation_add_async);
4091 
4092   photos_base_item_process_async (self, cancellable, photos_base_item_common_process, g_object_ref (task));
4093 }
4094 
4095 
4096 gboolean
photos_base_item_operation_add_finish(PhotosBaseItem * self,GAsyncResult * res,GError ** error)4097 photos_base_item_operation_add_finish (PhotosBaseItem *self, GAsyncResult *res, GError **error)
4098 {
4099   GTask *task;
4100 
4101   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), FALSE);
4102 
4103   g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
4104   task = G_TASK (res);
4105 
4106   g_return_val_if_fail (g_task_get_source_tag (task) == photos_base_item_operation_add_async, FALSE);
4107   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
4108 
4109   return g_task_propagate_boolean (task, error);
4110 }
4111 
4112 
4113 gboolean
photos_base_item_operation_get(PhotosBaseItem * self,const gchar * operation,const gchar * first_property_name,...)4114 photos_base_item_operation_get (PhotosBaseItem *self, const gchar *operation, const gchar *first_property_name, ...)
4115 {
4116   PhotosBaseItemPrivate *priv;
4117   PhotosPipeline *pipeline;
4118   gboolean ret_val;
4119   va_list ap;
4120 
4121   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), FALSE);
4122   priv = photos_base_item_get_instance_private (self);
4123 
4124   g_return_val_if_fail (!priv->collection, FALSE);
4125 
4126   pipeline = PHOTOS_PIPELINE (dzl_task_cache_peek (pipeline_cache, self));
4127   g_return_val_if_fail (PHOTOS_IS_PIPELINE (pipeline), FALSE);
4128 
4129   va_start (ap, first_property_name);
4130   ret_val = photos_pipeline_get_valist (pipeline, operation, first_property_name, ap);
4131   va_end (ap);
4132 
4133   return ret_val;
4134 }
4135 
4136 
4137 void
photos_base_item_operation_remove_async(PhotosBaseItem * self,const gchar * operation,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)4138 photos_base_item_operation_remove_async (PhotosBaseItem *self,
4139                                          const gchar *operation,
4140                                          GCancellable *cancellable,
4141                                          GAsyncReadyCallback callback,
4142                                          gpointer user_data)
4143 {
4144   PhotosBaseItemPrivate *priv;
4145   g_autoptr (GTask) task = NULL;
4146   PhotosPipeline *pipeline;
4147 
4148   g_return_if_fail (PHOTOS_IS_BASE_ITEM (self));
4149   priv = photos_base_item_get_instance_private (self);
4150 
4151   g_return_if_fail (!priv->collection);
4152 
4153   pipeline = PHOTOS_PIPELINE (dzl_task_cache_peek (pipeline_cache, self));
4154   g_return_if_fail (PHOTOS_IS_PIPELINE (pipeline));
4155 
4156   task = g_task_new (self, cancellable, callback, user_data);
4157   g_task_set_source_tag (task, photos_base_item_operation_remove_async);
4158 
4159   if (!photos_pipeline_remove (pipeline, operation))
4160     {
4161       g_task_return_new_error (task, PHOTOS_ERROR, 0, "Failed to find a GeglNode for %s", operation);
4162       goto out;
4163     }
4164 
4165   photos_base_item_process_async (self, cancellable, photos_base_item_common_process, g_object_ref (task));
4166 
4167  out:
4168   return;
4169 }
4170 
4171 
4172 gboolean
photos_base_item_operation_remove_finish(PhotosBaseItem * self,GAsyncResult * res,GError ** error)4173 photos_base_item_operation_remove_finish (PhotosBaseItem *self, GAsyncResult *res, GError **error)
4174 {
4175   GTask *task;
4176 
4177   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), FALSE);
4178 
4179   g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
4180   task = G_TASK (res);
4181 
4182   g_return_val_if_fail (g_task_get_source_tag (task) == photos_base_item_operation_remove_async, FALSE);
4183   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
4184 
4185   return g_task_propagate_boolean (task, error);
4186 }
4187 
4188 
4189 void
photos_base_item_pipeline_is_edited_async(PhotosBaseItem * self,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)4190 photos_base_item_pipeline_is_edited_async (PhotosBaseItem *self,
4191                                            GCancellable *cancellable,
4192                                            GAsyncReadyCallback callback,
4193                                            gpointer user_data)
4194 {
4195   PhotosBaseItemPrivate *priv;
4196   g_autoptr (GTask) task = NULL;
4197 
4198   g_return_if_fail (PHOTOS_IS_BASE_ITEM (self));
4199   priv = photos_base_item_get_instance_private (self);
4200 
4201   g_return_if_fail (!priv->collection);
4202 
4203   task = g_task_new (self, cancellable, callback, user_data);
4204   g_task_set_source_tag (task, photos_base_item_pipeline_is_edited_async);
4205 
4206   photos_base_item_load_pipeline_async (self,
4207                                         cancellable,
4208                                         photos_base_item_pipeline_is_edited_load_pipeline,
4209                                         g_object_ref (task));
4210 }
4211 
4212 
4213 gboolean
photos_base_item_pipeline_is_edited_finish(PhotosBaseItem * self,GAsyncResult * res,GError ** error)4214 photos_base_item_pipeline_is_edited_finish (PhotosBaseItem *self, GAsyncResult *res, GError **error)
4215 {
4216   GTask *task;
4217 
4218   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), FALSE);
4219 
4220   g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
4221   task = G_TASK (res);
4222 
4223   g_return_val_if_fail (g_task_get_source_tag (task) == photos_base_item_pipeline_is_edited_async, FALSE);
4224   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
4225 
4226   return g_task_propagate_boolean (task, error);
4227 }
4228 
4229 
4230 void
photos_base_item_pipeline_revert_async(PhotosBaseItem * self,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)4231 photos_base_item_pipeline_revert_async (PhotosBaseItem *self,
4232                                         GCancellable *cancellable,
4233                                         GAsyncReadyCallback callback,
4234                                         gpointer user_data)
4235 {
4236   PhotosBaseItemPrivate *priv;
4237   g_autoptr (GTask) task = NULL;
4238   PhotosPipeline *pipeline;
4239 
4240   g_return_if_fail (PHOTOS_IS_BASE_ITEM (self));
4241   priv = photos_base_item_get_instance_private (self);
4242 
4243   g_return_if_fail (!priv->collection);
4244 
4245   pipeline = PHOTOS_PIPELINE (dzl_task_cache_peek (pipeline_cache, self));
4246   g_return_if_fail (PHOTOS_IS_PIPELINE (pipeline));
4247 
4248   photos_pipeline_revert (pipeline);
4249 
4250   task = g_task_new (self, cancellable, callback, user_data);
4251   g_task_set_source_tag (task, photos_base_item_pipeline_revert_async);
4252 
4253   if (priv->edit_graph == NULL)
4254     {
4255       g_task_return_boolean (task, TRUE);
4256       goto out;
4257     }
4258 
4259   photos_base_item_process_async (self, cancellable, photos_base_item_common_process, g_object_ref (task));
4260 
4261  out:
4262   return;
4263 }
4264 
4265 
4266 gboolean
photos_base_item_pipeline_revert_finish(PhotosBaseItem * self,GAsyncResult * res,GError ** error)4267 photos_base_item_pipeline_revert_finish (PhotosBaseItem *self, GAsyncResult *res, GError **error)
4268 {
4269   GTask *task;
4270 
4271   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), FALSE);
4272 
4273   g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
4274   task = G_TASK (res);
4275 
4276   g_return_val_if_fail (g_task_get_source_tag (task) == photos_base_item_pipeline_revert_async, FALSE);
4277   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
4278 
4279   return g_task_propagate_boolean (task, error);
4280 }
4281 
4282 
4283 void
photos_base_item_pipeline_revert_to_original_async(PhotosBaseItem * self,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)4284 photos_base_item_pipeline_revert_to_original_async (PhotosBaseItem *self,
4285                                                     GCancellable *cancellable,
4286                                                     GAsyncReadyCallback callback,
4287                                                     gpointer user_data)
4288 
4289 {
4290   PhotosBaseItemPrivate *priv;
4291   g_autoptr (GTask) task = NULL;
4292   PhotosPipeline *pipeline;
4293 
4294   g_return_if_fail (PHOTOS_IS_BASE_ITEM (self));
4295   priv = photos_base_item_get_instance_private (self);
4296 
4297   g_return_if_fail (!priv->collection);
4298 
4299   pipeline = PHOTOS_PIPELINE (dzl_task_cache_peek (pipeline_cache, self));
4300   g_return_if_fail (PHOTOS_IS_PIPELINE (pipeline));
4301 
4302   photos_pipeline_revert_to_original (pipeline);
4303 
4304   task = g_task_new (self, cancellable, callback, user_data);
4305   g_task_set_source_tag (task, photos_base_item_pipeline_revert_to_original_async);
4306 
4307   if (priv->edit_graph == NULL)
4308     {
4309       g_task_return_boolean (task, TRUE);
4310       goto out;
4311     }
4312 
4313   photos_base_item_process_async (self, cancellable, photos_base_item_common_process, g_object_ref (task));
4314 
4315  out:
4316   return;
4317 }
4318 
4319 
4320 gboolean
photos_base_item_pipeline_revert_to_original_finish(PhotosBaseItem * self,GAsyncResult * res,GError ** error)4321 photos_base_item_pipeline_revert_to_original_finish (PhotosBaseItem *self, GAsyncResult *res, GError **error)
4322 {
4323   GTask *task;
4324 
4325   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), FALSE);
4326 
4327   g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
4328   task = G_TASK (res);
4329 
4330   g_return_val_if_fail (g_task_get_source_tag (task) == photos_base_item_pipeline_revert_to_original_async, FALSE);
4331   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
4332 
4333   return g_task_propagate_boolean (task, error);
4334 }
4335 
4336 
4337 void
photos_base_item_pipeline_save_async(PhotosBaseItem * self,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)4338 photos_base_item_pipeline_save_async (PhotosBaseItem *self,
4339                                       GCancellable *cancellable,
4340                                       GAsyncReadyCallback callback,
4341                                       gpointer user_data)
4342 {
4343   PhotosBaseItemPrivate *priv;
4344   g_autoptr (GTask) task = NULL;
4345   PhotosPipeline *pipeline;
4346 
4347   g_return_if_fail (PHOTOS_IS_BASE_ITEM (self));
4348   priv = photos_base_item_get_instance_private (self);
4349 
4350   g_return_if_fail (!priv->collection);
4351 
4352   pipeline = PHOTOS_PIPELINE (dzl_task_cache_peek (pipeline_cache, self));
4353   g_return_if_fail (pipeline != NULL);
4354 
4355   task = g_task_new (self, cancellable, callback, user_data);
4356   g_task_set_source_tag (task, photos_base_item_pipeline_save_async);
4357 
4358   photos_pipeline_save_async (pipeline, cancellable, photos_base_item_pipeline_save_save, g_object_ref (task));
4359 }
4360 
4361 
4362 gboolean
photos_base_item_pipeline_save_finish(PhotosBaseItem * self,GAsyncResult * res,GError ** error)4363 photos_base_item_pipeline_save_finish (PhotosBaseItem *self, GAsyncResult *res, GError **error)
4364 {
4365   GTask *task;
4366 
4367   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), FALSE);
4368 
4369   g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
4370   task = G_TASK (res);
4371 
4372   g_return_val_if_fail (g_task_get_source_tag (task) == photos_base_item_pipeline_save_async, FALSE);
4373   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
4374 
4375   return g_task_propagate_boolean (task, error);
4376 }
4377 
4378 
4379 void
photos_base_item_pipeline_snapshot(PhotosBaseItem * self)4380 photos_base_item_pipeline_snapshot (PhotosBaseItem *self)
4381 {
4382   PhotosBaseItemPrivate *priv;
4383   PhotosPipeline *pipeline;
4384 
4385   g_return_if_fail (PHOTOS_IS_BASE_ITEM (self));
4386   priv = photos_base_item_get_instance_private (self);
4387 
4388   g_return_if_fail (!priv->collection);
4389 
4390   pipeline = PHOTOS_PIPELINE (dzl_task_cache_peek (pipeline_cache, self));
4391   g_return_if_fail (PHOTOS_IS_PIPELINE (pipeline));
4392 
4393   photos_pipeline_snapshot (pipeline);
4394 }
4395 
4396 
4397 void
photos_base_item_print(PhotosBaseItem * self,GtkWidget * toplevel)4398 photos_base_item_print (PhotosBaseItem *self, GtkWidget *toplevel)
4399 {
4400   PhotosBaseItemPrivate *priv;
4401 
4402   g_return_if_fail (PHOTOS_IS_BASE_ITEM (self));
4403   priv = photos_base_item_get_instance_private (self);
4404 
4405   g_return_if_fail (!priv->collection);
4406 
4407   photos_base_item_load_async (self, NULL, photos_base_item_print_load, g_object_ref (toplevel));
4408 }
4409 
4410 
4411 GFileInfo *
photos_base_item_query_info(PhotosBaseItem * self,const gchar * attributes,GFileQueryInfoFlags flags,GCancellable * cancellable,GError ** error)4412 photos_base_item_query_info (PhotosBaseItem *self,
4413                              const gchar *attributes,
4414                              GFileQueryInfoFlags flags,
4415                              GCancellable *cancellable,
4416                              GError **error)
4417 {
4418   PhotosBaseItemPrivate *priv;
4419   GFileAttributeMatcher *matcher = NULL; /* TODO: use g_autoptr */
4420   g_autoptr (GFile) file = NULL;
4421   g_autoptr (GFileInfo) info = NULL;
4422   GFileInfo *ret_val = NULL;
4423 
4424   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), NULL);
4425   priv = photos_base_item_get_instance_private (self);
4426 
4427   g_return_val_if_fail (attributes != NULL && attributes[0] != '\0', NULL);
4428   g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
4429   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
4430 
4431   file = g_file_new_for_uri (priv->uri);
4432   info = g_file_query_info (file, attributes, flags, cancellable, error);
4433   if (info == NULL)
4434     goto out;
4435 
4436   matcher = g_file_attribute_matcher_new (attributes);
4437   if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_THUMBNAIL_IS_VALID)
4438       || g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_THUMBNAIL_PATH)
4439       || g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED))
4440     {
4441       g_autofree gchar *path = NULL;
4442 
4443       g_file_info_remove_attribute (info, G_FILE_ATTRIBUTE_THUMBNAIL_IS_VALID);
4444       g_file_info_remove_attribute (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
4445       g_file_info_remove_attribute (info, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED);
4446 
4447       path = photos_base_item_create_thumbnail_path (self);
4448       if (g_file_test (path, G_FILE_TEST_IS_REGULAR))
4449         {
4450           g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_THUMBNAIL_IS_VALID, TRUE);
4451           g_file_info_set_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH, path);
4452           g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED, FALSE);
4453         }
4454     }
4455 
4456   ret_val = g_object_ref (info);
4457 
4458  out:
4459   g_clear_pointer (&matcher, g_file_attribute_matcher_unref);
4460   return ret_val;
4461 }
4462 
4463 
4464 static void
photos_base_item_query_info_in_thread_func(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)4465 photos_base_item_query_info_in_thread_func (GTask *task,
4466                                             gpointer source_object,
4467                                             gpointer task_data,
4468                                             GCancellable *cancellable)
4469 {
4470   PhotosBaseItem *self = PHOTOS_BASE_ITEM (source_object);
4471   g_autoptr (GFileInfo) info = NULL;
4472   PhotosBaseItemQueryInfoData *data = (PhotosBaseItemQueryInfoData *) task_data;
4473 
4474   {
4475     g_autoptr (GError) error = NULL;
4476 
4477     info = photos_base_item_query_info (self, data->attributes, data->flags, cancellable, &error);
4478     if (error != NULL)
4479       {
4480         g_task_return_error (task, g_steal_pointer (&error));
4481         goto out;
4482       }
4483   }
4484 
4485   g_task_return_pointer (task, g_object_ref (info), g_object_unref);
4486 
4487  out:
4488   return;
4489 }
4490 
4491 
4492 void
photos_base_item_query_info_async(PhotosBaseItem * self,const gchar * attributes,GFileQueryInfoFlags flags,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)4493 photos_base_item_query_info_async (PhotosBaseItem *self,
4494                                    const gchar *attributes,
4495                                    GFileQueryInfoFlags flags,
4496                                    gint io_priority,
4497                                    GCancellable *cancellable,
4498                                    GAsyncReadyCallback callback,
4499                                    gpointer user_data)
4500 {
4501   g_autoptr (GTask) task = NULL;
4502   PhotosBaseItemQueryInfoData *data;
4503   const gchar *wildcard;
4504 
4505   g_return_if_fail (PHOTOS_IS_BASE_ITEM (self));
4506   g_return_if_fail (attributes != NULL && attributes[0] != '\0');
4507   g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
4508 
4509   wildcard = strstr (attributes, "*");
4510   g_return_if_fail (wildcard == NULL);
4511 
4512   data = photos_base_item_query_info_data_new (attributes, flags);
4513 
4514   task = g_task_new (self, cancellable, callback, user_data);
4515   g_task_set_priority (task, io_priority);
4516   g_task_set_source_tag (task, photos_base_item_query_info_async);
4517   g_task_set_task_data (task, data, (GDestroyNotify) photos_base_item_query_info_data_free);
4518 
4519   g_task_run_in_thread (task, photos_base_item_query_info_in_thread_func);
4520 }
4521 
4522 
4523 GFileInfo *
photos_base_item_query_info_finish(PhotosBaseItem * self,GAsyncResult * res,GError ** error)4524 photos_base_item_query_info_finish (PhotosBaseItem *self, GAsyncResult *res, GError **error)
4525 {
4526   GTask *task;
4527 
4528   g_return_val_if_fail (g_task_is_valid (res, self), NULL);
4529   task = G_TASK (res);
4530 
4531   g_return_val_if_fail (g_task_get_source_tag (task) == photos_base_item_query_info_async, NULL);
4532   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
4533 
4534   return g_task_propagate_pointer (task, error);
4535 }
4536 
4537 
4538 void
photos_base_item_refresh(PhotosBaseItem * self)4539 photos_base_item_refresh (PhotosBaseItem *self)
4540 {
4541   PhotosBaseItemPrivate *priv;
4542   GApplication *app;
4543   PhotosSearchContextState *state;
4544   g_autoptr (PhotosSingleItemJob) job = NULL;
4545 
4546   g_return_if_fail (PHOTOS_IS_BASE_ITEM (self));
4547   priv = photos_base_item_get_instance_private (self);
4548 
4549   app = g_application_get_default ();
4550   state = photos_search_context_get_state (PHOTOS_SEARCH_CONTEXT (app));
4551 
4552   job = photos_single_item_job_new (priv->id);
4553   photos_single_item_job_run (job,
4554                               state,
4555                               PHOTOS_QUERY_FLAGS_UNFILTERED,
4556                               NULL,
4557                               photos_base_item_refresh_executed,
4558                               g_object_ref (self));
4559 }
4560 
4561 
4562 void
photos_base_item_save_to_dir_async(PhotosBaseItem * self,GFile * dir,gdouble zoom,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)4563 photos_base_item_save_to_dir_async (PhotosBaseItem *self,
4564                                     GFile *dir,
4565                                     gdouble zoom,
4566                                     GCancellable *cancellable,
4567                                     GAsyncReadyCallback callback,
4568                                     gpointer user_data)
4569 {
4570   PhotosBaseItemPrivate *priv;
4571   g_autoptr (GTask) task = NULL;
4572   PhotosBaseItemSaveData *data;
4573 
4574   g_return_if_fail (PHOTOS_IS_BASE_ITEM (self));
4575   priv = photos_base_item_get_instance_private (self);
4576 
4577   g_return_if_fail (!priv->collection);
4578   g_return_if_fail (G_IS_FILE (dir));
4579   g_return_if_fail (priv->filename != NULL && priv->filename[0] != '\0');
4580 
4581   data = photos_base_item_save_data_new (dir, NULL, NULL, zoom);
4582 
4583   task = g_task_new (self, cancellable, callback, user_data);
4584   g_task_set_source_tag (task, photos_base_item_save_to_dir_async);
4585   g_task_set_task_data (task, data, (GDestroyNotify) photos_base_item_save_data_free);
4586 
4587   photos_base_item_load_async (self, cancellable, photos_base_item_save_to_dir_load, g_object_ref (task));
4588 }
4589 
4590 
4591 GFile *
photos_base_item_save_to_dir_finish(PhotosBaseItem * self,GAsyncResult * res,GError ** error)4592 photos_base_item_save_to_dir_finish (PhotosBaseItem *self, GAsyncResult *res, GError **error)
4593 {
4594   GTask *task;
4595 
4596   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), FALSE);
4597 
4598   g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
4599   task = G_TASK (res);
4600 
4601   g_return_val_if_fail (g_task_get_source_tag (task) == photos_base_item_save_to_dir_async, FALSE);
4602   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
4603 
4604   return g_task_propagate_pointer (task, error);
4605 }
4606 
4607 
4608 void
photos_base_item_save_to_file_async(PhotosBaseItem * self,GFile * file,GFileCreateFlags flags,gdouble zoom,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)4609 photos_base_item_save_to_file_async (PhotosBaseItem *self,
4610                                      GFile *file,
4611                                      GFileCreateFlags flags,
4612                                      gdouble zoom,
4613                                      GCancellable *cancellable,
4614                                      GAsyncReadyCallback callback,
4615                                      gpointer user_data)
4616 {
4617   PhotosBaseItemPrivate *priv;
4618   g_autoptr (GTask) task = NULL;
4619   PhotosBaseItemSaveToFileData *data;
4620 
4621   g_return_if_fail (PHOTOS_IS_BASE_ITEM (self));
4622   priv = photos_base_item_get_instance_private (self);
4623 
4624   g_return_if_fail (!priv->collection);
4625   g_return_if_fail (G_IS_FILE (file));
4626 
4627   data = photos_base_item_save_to_file_data_new (file, flags, zoom);
4628 
4629   task = g_task_new (self, cancellable, callback, user_data);
4630   g_task_set_source_tag (task, photos_base_item_save_to_file_async);
4631   g_task_set_task_data (task, data, (GDestroyNotify) photos_base_item_save_to_file_data_free);
4632 
4633   photos_base_item_load_async (self, cancellable, photos_base_item_save_to_file_load, g_object_ref (task));
4634 }
4635 
4636 
4637 gboolean
photos_base_item_save_to_file_finish(PhotosBaseItem * self,GAsyncResult * res,GError ** error)4638 photos_base_item_save_to_file_finish (PhotosBaseItem *self, GAsyncResult *res, GError **error)
4639 {
4640   GTask *task;
4641 
4642   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), FALSE);
4643 
4644   g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
4645   task = G_TASK (res);
4646 
4647   g_return_val_if_fail (g_task_get_source_tag (task) == photos_base_item_save_to_file_async, FALSE);
4648   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
4649 
4650   return g_task_propagate_boolean (task, error);
4651 }
4652 
4653 
4654 void
photos_base_item_save_to_stream_async(PhotosBaseItem * self,GOutputStream * stream,gdouble zoom,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)4655 photos_base_item_save_to_stream_async (PhotosBaseItem *self,
4656                                        GOutputStream *stream,
4657                                        gdouble zoom,
4658                                        GCancellable *cancellable,
4659                                        GAsyncReadyCallback callback,
4660                                        gpointer user_data)
4661 {
4662   PhotosBaseItemPrivate *priv;
4663   g_autoptr (GTask) task = NULL;
4664   PhotosBaseItemSaveToStreamData *data;
4665 
4666   g_return_if_fail (PHOTOS_IS_BASE_ITEM (self));
4667   priv = photos_base_item_get_instance_private (self);
4668 
4669   g_return_if_fail (!priv->collection);
4670   g_return_if_fail (G_IS_OUTPUT_STREAM (stream));
4671   g_return_if_fail (priv->filename != NULL && priv->filename[0] != '\0');
4672 
4673   data = photos_base_item_save_to_stream_data_new (stream, zoom);
4674 
4675   task = g_task_new (self, cancellable, callback, user_data);
4676   g_task_set_source_tag (task, photos_base_item_save_to_stream_async);
4677   g_task_set_task_data (task, data, (GDestroyNotify) photos_base_item_save_to_stream_data_free);
4678 
4679   photos_base_item_load_async (self, cancellable, photos_base_item_save_to_stream_load, g_object_ref (task));
4680 }
4681 
4682 
4683 gboolean
photos_base_item_save_to_stream_finish(PhotosBaseItem * self,GAsyncResult * res,GError ** error)4684 photos_base_item_save_to_stream_finish (PhotosBaseItem *self, GAsyncResult *res, GError **error)
4685 {
4686   GTask *task;
4687 
4688   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), FALSE);
4689 
4690   g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
4691   task = G_TASK (res);
4692 
4693   g_return_val_if_fail (g_task_get_source_tag (task) == photos_base_item_save_to_stream_async, FALSE);
4694   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
4695 
4696   return g_task_propagate_boolean (task, error);
4697 }
4698 
4699 
4700 void
photos_base_item_set_default_app(PhotosBaseItem * self,GAppInfo * default_app)4701 photos_base_item_set_default_app (PhotosBaseItem *self, GAppInfo *default_app)
4702 {
4703   PhotosBaseItemPrivate *priv;
4704   const gchar *default_app_name;
4705 
4706   g_return_if_fail (PHOTOS_IS_BASE_ITEM (self));
4707   priv = photos_base_item_get_instance_private (self);
4708 
4709   if (priv->default_app == NULL && default_app == NULL)
4710     return;
4711 
4712   if (priv->default_app != NULL && default_app != NULL && g_app_info_equal (priv->default_app, default_app))
4713     return;
4714 
4715   g_set_object (&priv->default_app, default_app);
4716   g_clear_pointer (&priv->default_app_name, g_free);
4717 
4718   if (default_app == NULL)
4719     return;
4720 
4721   default_app_name = g_app_info_get_name (default_app);
4722   priv->default_app_name = g_strdup (default_app_name);
4723 }
4724 
4725 
4726 void
photos_base_item_set_default_app_name(PhotosBaseItem * self,const gchar * default_app_name)4727 photos_base_item_set_default_app_name (PhotosBaseItem *self, const gchar *default_app_name)
4728 {
4729   PhotosBaseItemPrivate *priv;
4730 
4731   g_return_if_fail (PHOTOS_IS_BASE_ITEM (self));
4732   priv = photos_base_item_get_instance_private (self);
4733 
4734   g_clear_object (&priv->default_app);
4735   g_free (priv->default_app_name);
4736   priv->default_app_name = g_strdup (default_app_name);
4737 }
4738 
4739 
4740 void
photos_base_item_set_favorite(PhotosBaseItem * self,gboolean favorite)4741 photos_base_item_set_favorite (PhotosBaseItem *self, gboolean favorite)
4742 {
4743   PhotosBaseItemPrivate *priv;
4744 
4745   g_return_if_fail (PHOTOS_IS_BASE_ITEM (self));
4746   priv = photos_base_item_get_instance_private (self);
4747 
4748   g_return_if_fail (!priv->collection);
4749 
4750   PHOTOS_BASE_ITEM_GET_CLASS (self)->set_favorite (self, favorite);
4751 }
4752 
4753 
4754 void
photos_base_item_trash_async(PhotosBaseItem * self,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)4755 photos_base_item_trash_async (PhotosBaseItem *self,
4756                               GCancellable *cancellable,
4757                               GAsyncReadyCallback callback,
4758                               gpointer user_data)
4759 {
4760   GApplication *app;
4761   g_autoptr (GTask) task = NULL;
4762 
4763   g_return_if_fail (PHOTOS_IS_BASE_ITEM (self));
4764 
4765   app = g_application_get_default ();
4766 
4767   task = g_task_new (self, cancellable, callback, user_data);
4768   g_task_set_source_tag (task, photos_base_item_trash_async);
4769 
4770   g_application_hold (app);
4771   PHOTOS_BASE_ITEM_GET_CLASS (self)->trash_async (self,
4772                                                   cancellable,
4773                                                   photos_base_item_trash_trash,
4774                                                   g_object_ref (task));
4775 }
4776 
4777 
4778 gboolean
photos_base_item_trash_finish(PhotosBaseItem * self,GAsyncResult * res,GError ** error)4779 photos_base_item_trash_finish (PhotosBaseItem *self, GAsyncResult *res, GError **error)
4780 {
4781   GTask *task;
4782 
4783   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (self), FALSE);
4784 
4785   g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
4786   task = G_TASK (res);
4787 
4788   g_return_val_if_fail (g_task_get_source_tag (task) == photos_base_item_trash_async, FALSE);
4789   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
4790 
4791   return g_task_propagate_boolean (task, error);
4792 }
4793 
4794 
4795 void
photos_base_item_unmark_busy(PhotosBaseItem * self)4796 photos_base_item_unmark_busy (PhotosBaseItem *self)
4797 {
4798   PhotosBaseItemPrivate *priv;
4799 
4800   g_return_if_fail (PHOTOS_IS_BASE_ITEM (self));
4801   priv = photos_base_item_get_instance_private (self);
4802 
4803   g_return_if_fail (priv->busy_count > 0);
4804 
4805   priv->busy_count--;
4806   if (priv->busy_count == 0)
4807     g_object_notify (G_OBJECT (self), "pulse");
4808 }
4809