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