1 /*
2 * Photos - access, organize and share your photos on GNOME
3 * Copyright © 2012 – 2020 Red Hat, Inc.
4 * Copyright © 2009 Yorba Foundation
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /* Based on code from:
21 * + Documents
22 * + Eye of GNOME
23 * + Shotwell
24 */
25
26
27 #include "config.h"
28
29 #include <math.h>
30
31 #include <gdk/gdk.h>
32 #include <glib.h>
33 #include <tracker-sparql.h>
34 #include <libgd/gd.h>
35
36 #include "photos-application.h"
37 #include "photos-device-item.h"
38 #include "photos-enums.h"
39 #include "photos-error.h"
40 #include "photos-facebook-item.h"
41 #include "photos-flickr-item.h"
42 #include "photos-gegl.h"
43 #include "photos-google-item.h"
44 #include "photos-local-item.h"
45 #include "photos-media-server-item.h"
46 #include "photos-offset-collection-view-controller.h"
47 #include "photos-offset-collections-controller.h"
48 #include "photos-offset-favorites-controller.h"
49 #include "photos-offset-import-controller.h"
50 #include "photos-offset-overview-controller.h"
51 #include "photos-offset-search-controller.h"
52 #include "photos-query.h"
53 #include "photos-share-point.h"
54 #include "photos-share-point-email.h"
55 #include "photos-share-point-google.h"
56 #include "photos-share-point-online.h"
57 #include "photos-source.h"
58 #include "photos-thumbnail-factory.h"
59 #include "photos-tool.h"
60 #include "photos-tool-colors.h"
61 #include "photos-tool-crop.h"
62 #include "photos-tool-enhance.h"
63 #include "photos-tool-filters.h"
64 #include "photos-tracker-collection-view-controller.h"
65 #include "photos-tracker-collections-controller.h"
66 #include "photos-tracker-favorites-controller.h"
67 #include "photos-tracker-import-controller.h"
68 #include "photos-tracker-overview-controller.h"
69 #include "photos-tracker-queue.h"
70 #include "photos-tracker-search-controller.h"
71 #include "photos-utils.h"
72
73
74 GdkPixbuf *
photos_utils_center_pixbuf(GdkPixbuf * pixbuf,gint size)75 photos_utils_center_pixbuf (GdkPixbuf *pixbuf, gint size)
76 {
77 GdkPixbuf *ret_val;
78 gint height;
79 gint pixbuf_size;
80 gint width;
81
82 height = gdk_pixbuf_get_height (pixbuf);
83 width = gdk_pixbuf_get_width (pixbuf);
84
85 pixbuf_size = MAX (height, width);
86 if (pixbuf_size >= size)
87 {
88 ret_val = g_object_ref (pixbuf);
89 goto out;
90 }
91
92 ret_val = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, size, size);
93 gdk_pixbuf_fill (ret_val, 0x00000000);
94 gdk_pixbuf_copy_area (pixbuf, 0, 0, width, height, ret_val, (size - width) / 2, (size - height) / 2);
95
96 out:
97 return ret_val;
98 }
99
100
101 gchar *
photos_utils_convert_path_to_uri(const gchar * path)102 photos_utils_convert_path_to_uri (const gchar *path)
103 {
104 g_autoptr (GFile) file = NULL;
105 gchar *uri = NULL;
106
107 if (path == NULL)
108 {
109 uri = g_strdup ("");
110 goto out;
111 }
112
113 file = g_file_new_for_path (path);
114 uri = g_file_get_uri (file);
115
116 out:
117 g_return_val_if_fail (uri != NULL, NULL);
118 return uri;
119 }
120
121
122 GStrv
photos_utils_convert_paths_to_uris(const gchar * const * paths)123 photos_utils_convert_paths_to_uris (const gchar *const *paths)
124 {
125 GStrv uris = NULL;
126 guint i;
127 guint n_paths;
128
129 if (paths == NULL)
130 goto out;
131
132 n_paths = g_strv_length ((GStrv) paths);
133 uris = (GStrv) g_malloc0_n (n_paths + 1, sizeof (gchar *));
134
135 for (i = 0; paths[i] != NULL; i++)
136 {
137 g_autofree gchar *uri = NULL;
138
139 uri = photos_utils_convert_path_to_uri (paths[i]);
140 uris[i] = g_steal_pointer (&uri);
141 }
142
143 out:
144 return uris;
145 }
146
147
148 GIcon *
photos_utils_create_collection_icon(gint base_size,GList * pixbufs)149 photos_utils_create_collection_icon (gint base_size, GList *pixbufs)
150 {
151 cairo_surface_t *surface; /* TODO: use g_autoptr */
152 cairo_t *cr; /* TODO: use g_autoptr */
153 GdkPixbuf *pix;
154 GIcon *ret_val;
155 GList *l;
156 g_autoptr (GtkStyleContext) context = NULL;
157 g_autoptr (GtkWidgetPath) path = NULL;
158 gint cur_x;
159 gint cur_y;
160 gint padding;
161 gint pix_height;
162 gint pix_width;
163 gint scale_size;
164 gint tile_size;
165 guint idx;
166 guint n_grid;
167 guint n_pixbufs;
168 guint n_tiles;
169
170 n_pixbufs = g_list_length (pixbufs);
171 if (n_pixbufs < 3)
172 {
173 n_grid = 1;
174 n_tiles = 1;
175 }
176 else
177 {
178 n_grid = 2;
179 n_tiles = 4;
180 }
181
182 padding = MAX (base_size / 10, 4);
183 tile_size = (base_size - ((n_grid + 1) * padding)) / n_grid;
184
185 context = gtk_style_context_new ();
186 gtk_style_context_add_class (context, "photos-collection-icon");
187
188 path = gtk_widget_path_new ();
189 gtk_widget_path_append_type (path, GTK_TYPE_ICON_VIEW);
190 gtk_style_context_set_path (context, path);
191
192 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, base_size, base_size);
193 cr = cairo_create (surface);
194
195 gtk_render_background (context, cr, 0, 0, base_size, base_size);
196
197 l = pixbufs;
198 idx = 0;
199 cur_x = padding;
200 cur_y = padding;
201
202 while (l != NULL && idx < n_tiles)
203 {
204 pix = l->data;
205 pix_width = gdk_pixbuf_get_width (pix);
206 pix_height = gdk_pixbuf_get_height (pix);
207
208 scale_size = MIN (pix_width, pix_height);
209
210 cairo_save (cr);
211
212 cairo_translate (cr, cur_x, cur_y);
213
214 cairo_rectangle (cr, 0, 0,
215 tile_size, tile_size);
216 cairo_clip (cr);
217
218 cairo_scale (cr, (gdouble) tile_size / (gdouble) scale_size, (gdouble) tile_size / (gdouble) scale_size);
219 gdk_cairo_set_source_pixbuf (cr, pix, 0, 0);
220
221 cairo_paint (cr);
222 cairo_restore (cr);
223
224 idx++;
225 l = l->next;
226
227 if ((idx % n_grid) == 0)
228 {
229 cur_x = padding;
230 cur_y += tile_size + padding;
231 }
232 else
233 {
234 cur_x += tile_size + padding;
235 }
236 }
237
238 ret_val = G_ICON (gdk_pixbuf_get_from_surface (surface, 0, 0, base_size, base_size));
239
240 cairo_surface_destroy (surface);
241 cairo_destroy (cr);
242
243 return ret_val;
244 }
245
246
247 GdkPixbuf *
photos_utils_create_placeholder_icon_for_scale(const gchar * name,gint size,gint scale)248 photos_utils_create_placeholder_icon_for_scale (const gchar *name, gint size, gint scale)
249 {
250 GApplication *app;
251 g_autoptr (GdkPixbuf) centered_pixbuf = NULL;
252 g_autoptr (GdkPixbuf) pixbuf = NULL;
253 GdkPixbuf *ret_val = NULL;
254 g_autoptr (GIcon) icon = NULL;
255 GList *windows;
256 g_autoptr (GtkIconInfo) info = NULL;
257 GtkIconTheme *theme;
258 GtkStyleContext *context;
259 gint size_scaled;
260
261 app = g_application_get_default ();
262 windows = gtk_application_get_windows (GTK_APPLICATION (app));
263 if (windows == NULL)
264 goto out;
265
266 icon = g_themed_icon_new (name);
267 theme = gtk_icon_theme_get_default ();
268 info = gtk_icon_theme_lookup_by_gicon_for_scale (theme,
269 icon,
270 16,
271 scale,
272 GTK_ICON_LOOKUP_FORCE_SIZE | GTK_ICON_LOOKUP_FORCE_SYMBOLIC);
273 if (info == NULL)
274 goto out;
275
276 context = gtk_widget_get_style_context (GTK_WIDGET (windows->data));
277
278 {
279 g_autoptr (GError) error = NULL;
280
281 pixbuf = gtk_icon_info_load_symbolic_for_context (info, context, NULL, &error);
282 if (error != NULL)
283 {
284 g_warning ("Unable to load icon '%s': %s", name, error->message);
285 goto out;
286 }
287 }
288
289 size_scaled = size * scale;
290 centered_pixbuf = photos_utils_center_pixbuf (pixbuf, size_scaled);
291
292 ret_val = centered_pixbuf;
293 centered_pixbuf = NULL;
294
295 out:
296 return ret_val;
297 }
298
299
300 GIcon *
photos_utils_create_symbolic_icon_for_scale(const gchar * name,gint base_size,gint scale)301 photos_utils_create_symbolic_icon_for_scale (const gchar *name, gint base_size, gint scale)
302 {
303 g_autoptr (GIcon) icon = NULL;
304 GIcon *ret_val = NULL;
305 g_autoptr (GdkPixbuf) pixbuf = NULL;
306 g_autoptr (GtkIconInfo) info = NULL;
307 GtkIconTheme *theme;
308 g_autoptr (GtkStyleContext) style = NULL;
309 g_autoptr (GtkWidgetPath) path = NULL;
310 cairo_surface_t *icon_surface = NULL; /* TODO: use g_autoptr */
311 cairo_surface_t *surface; /* TODO: use g_autoptr */
312 cairo_t *cr; /* TODO: use g_autoptr */
313 g_autofree gchar *symbolic_name = NULL;
314 const gint bg_size = 24;
315 const gint emblem_margin = 4;
316 gint emblem_pos;
317 gint emblem_size;
318 gint total_size;
319 gint total_size_scaled;
320
321 total_size = base_size / 2;
322 total_size_scaled = total_size * scale;
323 emblem_size = bg_size - emblem_margin * 2;
324
325 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, total_size_scaled, total_size_scaled);
326 cairo_surface_set_device_scale (surface, (gdouble) scale, (gdouble) scale);
327 cr = cairo_create (surface);
328
329 style = gtk_style_context_new ();
330
331 path = gtk_widget_path_new ();
332 gtk_widget_path_append_type (path, GTK_TYPE_ICON_VIEW);
333 gtk_style_context_set_path (style, path);
334
335 gtk_style_context_add_class (style, "photos-icon-bg");
336
337 gtk_render_background (style, cr, total_size - bg_size, total_size - bg_size, bg_size, bg_size);
338
339 symbolic_name = g_strconcat (name, "-symbolic", NULL);
340 icon = g_themed_icon_new_with_default_fallbacks (symbolic_name);
341
342 theme = gtk_icon_theme_get_default();
343 info = gtk_icon_theme_lookup_by_gicon_for_scale (theme, icon, emblem_size, scale, GTK_ICON_LOOKUP_FORCE_SIZE);
344 if (info == NULL)
345 goto out;
346
347 pixbuf = gtk_icon_info_load_symbolic_for_context (info, style, NULL, NULL);
348 if (pixbuf == NULL)
349 goto out;
350
351 icon_surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, scale, NULL);
352
353 emblem_pos = total_size - emblem_size - emblem_margin;
354 gtk_render_icon_surface (style, cr, icon_surface, emblem_pos, emblem_pos);
355
356 ret_val = G_ICON (gdk_pixbuf_get_from_surface (surface, 0, 0, total_size_scaled, total_size_scaled));
357
358 out:
359 cairo_surface_destroy (icon_surface);
360 cairo_surface_destroy (surface);
361 cairo_destroy (cr);
362
363 return ret_val;
364 }
365
366
367 gboolean
photos_utils_create_thumbnail(GFile * file,const gchar * mime_type,gint64 mtime,GQuark orientation,gint64 original_height,gint64 original_width,const gchar * const * pipeline_uris,const gchar * thumbnail_path,GCancellable * cancellable,GError ** error)368 photos_utils_create_thumbnail (GFile *file,
369 const gchar *mime_type,
370 gint64 mtime,
371 GQuark orientation,
372 gint64 original_height,
373 gint64 original_width,
374 const gchar *const *pipeline_uris,
375 const gchar *thumbnail_path,
376 GCancellable *cancellable,
377 GError **error)
378 {
379 g_autoptr (PhotosThumbnailFactory) factory = NULL;
380 gboolean ret_val = FALSE;
381
382 factory = photos_thumbnail_factory_dup_singleton (NULL, NULL);
383 if (!photos_thumbnail_factory_generate_thumbnail (factory,
384 file,
385 mime_type,
386 orientation,
387 original_height,
388 original_width,
389 pipeline_uris,
390 thumbnail_path,
391 cancellable,
392 error))
393 goto out;
394
395 ret_val = TRUE;
396
397 out:
398 return ret_val;
399 }
400
401
402 GVariant *
photos_utils_create_zoom_target_value(gdouble delta,PhotosZoomEvent event)403 photos_utils_create_zoom_target_value (gdouble delta, PhotosZoomEvent event)
404 {
405 GEnumClass *zoom_event_class = NULL; /* TODO: use g_autoptr */
406 GEnumValue *event_value;
407 GVariant *delta_value;
408 GVariant *event_nick_value;
409 GVariant *ret_val = NULL;
410 g_auto (GVariantBuilder) builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_VARDICT);
411 const gchar *event_nick = "none";
412
413 g_return_val_if_fail (delta >= 0.0, NULL);
414 g_return_val_if_fail (event != PHOTOS_ZOOM_EVENT_NONE, NULL);
415
416 delta_value = g_variant_new_double (delta);
417 g_variant_builder_add (&builder, "{sv}", "delta", delta_value);
418
419 zoom_event_class = G_ENUM_CLASS (g_type_class_ref (PHOTOS_TYPE_ZOOM_EVENT));
420
421 event_value = g_enum_get_value (zoom_event_class, (gint) event);
422 if (event_value != NULL)
423 event_nick = event_value->value_nick;
424
425 event_nick_value = g_variant_new_string (event_nick);
426 g_variant_builder_add (&builder, "{sv}", "event", event_nick_value);
427
428 ret_val = g_variant_builder_end (&builder);
429
430 g_type_class_unref (zoom_event_class);
431 g_return_val_if_fail (g_variant_is_floating (ret_val), ret_val);
432 return ret_val;
433 }
434
435
436 static GIcon *
photos_utils_get_thumbnail_icon(PhotosBaseItem * item)437 photos_utils_get_thumbnail_icon (PhotosBaseItem *item)
438 {
439 g_autoptr (GFile) thumb_file = NULL;
440 g_autoptr (GFileInfo) info = NULL;
441 GIcon *icon = NULL;
442 const gchar *thumb_path;
443 const gchar *uri;
444
445 uri = photos_base_item_get_uri (item);
446 if (uri == NULL || uri[0] == '\0')
447 goto out;
448
449 {
450 g_autoptr (GError) error = NULL;
451
452 info = photos_base_item_query_info (item,
453 G_FILE_ATTRIBUTE_THUMBNAIL_PATH,
454 G_FILE_QUERY_INFO_NONE,
455 NULL,
456 &error);
457 if (error != NULL)
458 {
459 g_warning ("Unable to fetch thumbnail path for %s: %s", uri, error->message);
460 goto out;
461 }
462 }
463
464 thumb_path = g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
465 if (thumb_path == NULL)
466 goto out;
467
468 thumb_file = g_file_new_for_path (thumb_path);
469 icon = g_file_icon_new (thumb_file);
470
471 out:
472 return icon;
473 }
474
475
476 GIcon *
photos_utils_get_icon_from_item(PhotosBaseItem * item)477 photos_utils_get_icon_from_item (PhotosBaseItem *item)
478 {
479 GIcon *icon = NULL;
480 gboolean is_remote = FALSE;
481 const gchar *identifier;
482 const gchar *mime_type;
483
484 identifier = photos_base_item_get_identifier (item);
485 if (identifier != NULL)
486 {
487 if (g_str_has_prefix (identifier, "facebook:") ||
488 g_str_has_prefix (identifier, "flickr:") ||
489 g_str_has_prefix (identifier, "google:"))
490 is_remote = TRUE;
491 }
492
493 if (!is_remote)
494 icon = photos_utils_get_thumbnail_icon (item);
495
496 if (icon != NULL)
497 goto out;
498
499 mime_type = photos_base_item_get_mime_type (item);
500 if (mime_type != NULL)
501 icon = g_content_type_get_icon (mime_type);
502
503 if (icon != NULL)
504 goto out;
505
506 if (photos_base_item_is_collection (item))
507 {
508 gint size;
509
510 size = photos_utils_get_icon_size ();
511 icon = photos_utils_create_collection_icon (size, NULL);
512 }
513
514 if (icon != NULL)
515 goto out;
516
517 icon = g_themed_icon_new ("image-x-generic");
518
519 out:
520 return icon;
521 }
522
523
524 gdouble
photos_utils_get_zoom_delta(GVariant * dictionary)525 photos_utils_get_zoom_delta (GVariant *dictionary)
526 {
527 gdouble delta;
528 gdouble ret_val = -1.0;
529
530 g_return_val_if_fail (dictionary != NULL, -1.0);
531 g_return_val_if_fail (g_variant_is_of_type (dictionary, G_VARIANT_TYPE_VARDICT), -1.0);
532
533 if (!g_variant_lookup (dictionary, "delta", "d", &delta))
534 goto out;
535
536 ret_val = delta;
537
538 out:
539 g_return_val_if_fail (ret_val >= 0.0, -1.0);
540 return ret_val;
541 }
542
543
544 PhotosZoomEvent
photos_utils_get_zoom_event(GVariant * dictionary)545 photos_utils_get_zoom_event (GVariant *dictionary)
546 {
547 GEnumClass *zoom_event_class = NULL;
548 GEnumValue *event_value;
549 PhotosZoomEvent ret_val = PHOTOS_ZOOM_EVENT_NONE;
550 const gchar *event_str;
551
552 g_return_val_if_fail (dictionary != NULL, PHOTOS_ZOOM_EVENT_NONE);
553 g_return_val_if_fail (g_variant_is_of_type (dictionary, G_VARIANT_TYPE_VARDICT), PHOTOS_ZOOM_EVENT_NONE);
554
555 if (!g_variant_lookup (dictionary, "event", "&s", &event_str))
556 goto out;
557
558 zoom_event_class = G_ENUM_CLASS (g_type_class_ref (PHOTOS_TYPE_ZOOM_EVENT));
559
560 event_value = g_enum_get_value_by_nick (zoom_event_class, event_str);
561 if (event_value == NULL)
562 event_value = g_enum_get_value_by_name (zoom_event_class, event_str);
563 if (event_value == NULL)
564 goto out;
565
566 ret_val = (PhotosZoomEvent) event_value->value;
567
568 out:
569 g_clear_pointer (&zoom_event_class, g_type_class_unref);
570 g_return_val_if_fail (ret_val != PHOTOS_ZOOM_EVENT_NONE, PHOTOS_ZOOM_EVENT_NONE);
571 return ret_val;
572 }
573
574
575 GdkPixbuf *
photos_utils_downscale_pixbuf_for_scale(GdkPixbuf * pixbuf,gint size,gint scale)576 photos_utils_downscale_pixbuf_for_scale (GdkPixbuf *pixbuf, gint size, gint scale)
577 {
578 GdkPixbuf *ret_val;
579 gint height;
580 gint pixbuf_size;
581 gint scaled_size;
582 gint width;
583
584 height = gdk_pixbuf_get_height (pixbuf);
585 width = gdk_pixbuf_get_width (pixbuf);
586 pixbuf_size = MAX (height, width);
587
588 scaled_size = size * scale;
589
590 /* On Hi-Dpi displays, a pixbuf should never appear smaller than on
591 * Lo-Dpi.
592 *
593 * Sometimes, a pixbuf can be slightly smaller than size. eg.,
594 * server-generated thumbnails for remote tems. Scaling them up
595 * won't cause any discernible loss of quality and will make our
596 * letterboxed grid look nicer. 75% of 'scale' has been chosen as
597 * the arbitrary definition of 'slightly smaller'.
598 *
599 * Therefore, if a pixbuf lies between (3 * size / 4, size * scale)
600 * we scale it up to size * scale, so that it doesn't look smaller.
601 * Similarly, if a pixbuf is smaller than size, then we increase its
602 * dimensions by the scale factor.
603 */
604
605 if (pixbuf_size == scaled_size)
606 {
607 ret_val = g_object_ref (pixbuf);
608 }
609 else if (pixbuf_size > 3 * size / 4)
610 {
611 if (height == width)
612 {
613 height = scaled_size;
614 width = scaled_size;
615 }
616 else if (height > width)
617 {
618 width = (gint) (0.5 + (gdouble) (width * scaled_size) / (gdouble) height);
619 height = scaled_size;
620 }
621 else
622 {
623 height = (gint) (0.5 + (gdouble) (height * scaled_size) / (gdouble) width);
624 width = scaled_size;
625 }
626
627 height = MAX (height, 1);
628 width = MAX (width, 1);
629 ret_val = gdk_pixbuf_scale_simple (pixbuf, width, height, GDK_INTERP_BILINEAR);
630 }
631 else /* pixbuf_size <= size */
632 {
633 if (scale == 1)
634 {
635 ret_val = g_object_ref (pixbuf);
636 }
637 else
638 {
639 height *= scale;
640 width *= scale;
641
642 height = MAX (height, 1);
643 width = MAX (width, 1);
644 ret_val = gdk_pixbuf_scale_simple (pixbuf, width, height, GDK_INTERP_BILINEAR);
645 }
646 }
647
648 return ret_val;
649 }
650
651
652 void
photos_utils_draw_rectangle_handles(cairo_t * cr,gdouble x,gdouble y,gdouble width,gdouble height,gdouble offset,gdouble radius)653 photos_utils_draw_rectangle_handles (cairo_t *cr,
654 gdouble x,
655 gdouble y,
656 gdouble width,
657 gdouble height,
658 gdouble offset,
659 gdouble radius)
660 {
661 cairo_save (cr);
662
663 cairo_new_sub_path (cr);
664 cairo_arc (cr, x - offset, y - offset, radius, 0.0, 2.0 * M_PI);
665 cairo_fill (cr);
666
667 cairo_new_sub_path (cr);
668 cairo_arc (cr, x + width + offset, y - offset, radius, 0.0, 2.0 * M_PI);
669 cairo_fill (cr);
670
671 cairo_new_sub_path (cr);
672 cairo_arc (cr, x + width + offset, y + height + offset, radius, 0.0, 2.0 * M_PI);
673 cairo_fill (cr);
674
675 cairo_new_sub_path (cr);
676 cairo_arc (cr, x - offset, y + height + offset, radius, 0.0, 2.0 * M_PI);
677 cairo_fill (cr);
678
679 cairo_restore (cr);
680 }
681
682
683 void
photos_utils_draw_rectangle_thirds(cairo_t * cr,gdouble x,gdouble y,gdouble width,gdouble height)684 photos_utils_draw_rectangle_thirds (cairo_t *cr, gdouble x, gdouble y, gdouble width, gdouble height)
685 {
686 const gdouble one_third_x = width / 3.0;
687 const gdouble one_third_y = height / 3.0;
688
689 cairo_save (cr);
690
691 cairo_move_to (cr, x + one_third_x, y);
692 cairo_line_to (cr, x + one_third_x, y + height);
693 cairo_stroke (cr);
694
695 cairo_move_to (cr, x + 2.0 * one_third_x, y);
696 cairo_line_to (cr, x + 2.0 * one_third_x, y + height);
697 cairo_stroke (cr);
698
699 cairo_move_to (cr, x, y + one_third_y);
700 cairo_line_to (cr, x + width, y + one_third_y);
701 cairo_stroke (cr);
702
703 cairo_move_to (cr, x, y + 2.0 * one_third_y);
704 cairo_line_to (cr, x + width, y + 2.0 * one_third_y);
705 cairo_stroke (cr);
706
707 cairo_restore (cr);
708 }
709
710
711 void
photos_utils_ensure_builtins(void)712 photos_utils_ensure_builtins (void)
713 {
714 static gsize once_init_value = 0;
715
716 photos_utils_ensure_extension_points ();
717 photos_gegl_ensure_builtins ();
718
719 if (g_once_init_enter (&once_init_value))
720 {
721 g_type_ensure (PHOTOS_TYPE_DEVICE_ITEM);
722 g_type_ensure (PHOTOS_TYPE_FACEBOOK_ITEM);
723 g_type_ensure (PHOTOS_TYPE_FLICKR_ITEM);
724 g_type_ensure (PHOTOS_TYPE_GOOGLE_ITEM);
725 g_type_ensure (PHOTOS_TYPE_LOCAL_ITEM);
726 g_type_ensure (PHOTOS_TYPE_MEDIA_SERVER_ITEM);
727
728 g_type_ensure (PHOTOS_TYPE_SHARE_POINT_EMAIL);
729 g_type_ensure (PHOTOS_TYPE_SHARE_POINT_GOOGLE);
730
731 g_type_ensure (PHOTOS_TYPE_TOOL_COLORS);
732 g_type_ensure (PHOTOS_TYPE_TOOL_CROP);
733 g_type_ensure (PHOTOS_TYPE_TOOL_ENHANCE);
734 g_type_ensure (PHOTOS_TYPE_TOOL_FILTERS);
735
736 g_type_ensure (PHOTOS_TYPE_TRACKER_COLLECTION_VIEW_CONTROLLER);
737 g_type_ensure (PHOTOS_TYPE_TRACKER_COLLECTIONS_CONTROLLER);
738 g_type_ensure (PHOTOS_TYPE_TRACKER_FAVORITES_CONTROLLER);
739 g_type_ensure (PHOTOS_TYPE_TRACKER_IMPORT_CONTROLLER);
740 g_type_ensure (PHOTOS_TYPE_TRACKER_OVERVIEW_CONTROLLER);
741 g_type_ensure (PHOTOS_TYPE_TRACKER_SEARCH_CONTROLLER);
742
743 g_once_init_leave (&once_init_value, 1);
744 }
745 }
746
747
748 void
photos_utils_ensure_extension_points(void)749 photos_utils_ensure_extension_points (void)
750 {
751 static gsize once_init_value = 0;
752
753 if (g_once_init_enter (&once_init_value))
754 {
755 GIOExtensionPoint *extension_point;
756
757 extension_point = g_io_extension_point_register (PHOTOS_BASE_ITEM_EXTENSION_POINT_NAME);
758 g_io_extension_point_set_required_type (extension_point, PHOTOS_TYPE_BASE_ITEM);
759
760 extension_point = g_io_extension_point_register (PHOTOS_SHARE_POINT_EXTENSION_POINT_NAME);
761 g_io_extension_point_set_required_type (extension_point, PHOTOS_TYPE_SHARE_POINT);
762
763 extension_point = g_io_extension_point_register (PHOTOS_SHARE_POINT_ONLINE_EXTENSION_POINT_NAME);
764 g_io_extension_point_set_required_type (extension_point, PHOTOS_TYPE_SHARE_POINT_ONLINE);
765
766 extension_point = g_io_extension_point_register (PHOTOS_TOOL_EXTENSION_POINT_NAME);
767 g_io_extension_point_set_required_type (extension_point, PHOTOS_TYPE_TOOL);
768
769 extension_point = g_io_extension_point_register (PHOTOS_TRACKER_CONTROLLER_EXTENSION_POINT_NAME);
770 g_io_extension_point_set_required_type (extension_point, PHOTOS_TYPE_TRACKER_CONTROLLER);
771
772 g_once_init_leave (&once_init_value, 1);
773 }
774 }
775
776
777 gdouble
photos_utils_eval_radial_line(gdouble crop_center_x,gdouble crop_center_y,gdouble corner_x,gdouble corner_y,gdouble event_x)778 photos_utils_eval_radial_line (gdouble crop_center_x,
779 gdouble crop_center_y,
780 gdouble corner_x,
781 gdouble corner_y,
782 gdouble event_x)
783 {
784 gdouble decision_intercept;
785 gdouble decision_slope;
786 gdouble projected_y;
787
788 decision_slope = (corner_y - crop_center_y) / (corner_x - crop_center_x);
789 decision_intercept = corner_y - (decision_slope * corner_x);
790 projected_y = decision_slope * event_x + decision_intercept;
791
792 return projected_y;
793 }
794
795
796 gboolean
photos_utils_file_copy_as_thumbnail(GFile * source,GFile * destination,const gchar * original_uri,gint64 original_height,gint64 original_width,GCancellable * cancellable,GError ** error)797 photos_utils_file_copy_as_thumbnail (GFile *source,
798 GFile *destination,
799 const gchar *original_uri,
800 gint64 original_height,
801 gint64 original_width,
802 GCancellable *cancellable,
803 GError **error)
804 {
805 g_autoptr (GFileInputStream) istream = NULL;
806 g_autoptr (GFileOutputStream) ostream = NULL;
807 g_autoptr (GdkPixbuf) pixbuf = NULL;
808 gboolean ret_val = FALSE;
809 const gchar *prgname;
810 g_autofree gchar *original_height_str = NULL;
811 g_autofree gchar *original_width_str = NULL;
812
813 g_return_val_if_fail (G_IS_FILE (source), FALSE);
814 g_return_val_if_fail (G_IS_FILE (destination), FALSE);
815 g_return_val_if_fail (original_uri != NULL && original_uri[0] != '\0', FALSE);
816 g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
817 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
818
819 istream = g_file_read (source, cancellable, error);
820 if (istream == NULL)
821 goto out;
822
823 pixbuf = gdk_pixbuf_new_from_stream (G_INPUT_STREAM (istream), cancellable, error);
824 if (pixbuf == NULL)
825 goto out;
826
827 ostream = g_file_replace (destination,
828 NULL,
829 FALSE,
830 G_FILE_CREATE_PRIVATE | G_FILE_CREATE_REPLACE_DESTINATION,
831 cancellable,
832 error);
833 if (ostream == NULL)
834 goto out;
835
836 original_height_str = g_strdup_printf ("%" G_GINT64_FORMAT, original_height);
837 original_width_str = g_strdup_printf ("%" G_GINT64_FORMAT, original_width);
838 prgname = g_get_prgname ();
839 if (!gdk_pixbuf_save_to_stream (pixbuf,
840 G_OUTPUT_STREAM (ostream),
841 "png",
842 cancellable,
843 error,
844 "tEXt::Software", prgname,
845 "tEXt::Thumb::URI", original_uri,
846 "tEXt::Thumb::Image::Height", original_height_str,
847 "tEXt::Thumb::Image::Width", original_width_str,
848 NULL))
849 {
850 goto out;
851 }
852
853 ret_val = TRUE;
854
855 out:
856 return ret_val;
857 }
858
859
860 void
photos_utils_get_controller(PhotosWindowMode mode,PhotosOffsetController ** out_offset_cntrlr,PhotosTrackerController ** out_trk_cntrlr)861 photos_utils_get_controller (PhotosWindowMode mode,
862 PhotosOffsetController **out_offset_cntrlr,
863 PhotosTrackerController **out_trk_cntrlr)
864 {
865 g_autoptr (PhotosOffsetController) offset_cntrlr = NULL;
866 g_autoptr (PhotosTrackerController) trk_cntrlr = NULL;
867
868 g_return_if_fail (mode != PHOTOS_WINDOW_MODE_NONE);
869 g_return_if_fail (mode != PHOTOS_WINDOW_MODE_EDIT);
870 g_return_if_fail (mode != PHOTOS_WINDOW_MODE_PREVIEW);
871
872 switch (mode)
873 {
874 case PHOTOS_WINDOW_MODE_COLLECTION_VIEW:
875 offset_cntrlr = photos_offset_collection_view_controller_dup_singleton ();
876 trk_cntrlr = photos_tracker_collection_view_controller_dup_singleton ();
877 break;
878
879 case PHOTOS_WINDOW_MODE_COLLECTIONS:
880 offset_cntrlr = photos_offset_collections_controller_dup_singleton ();
881 trk_cntrlr = photos_tracker_collections_controller_dup_singleton ();
882 break;
883
884 case PHOTOS_WINDOW_MODE_FAVORITES:
885 offset_cntrlr = photos_offset_favorites_controller_dup_singleton ();
886 trk_cntrlr = photos_tracker_favorites_controller_dup_singleton ();
887 break;
888
889 case PHOTOS_WINDOW_MODE_IMPORT:
890 offset_cntrlr = photos_offset_import_controller_dup_singleton ();
891 trk_cntrlr = photos_tracker_import_controller_dup_singleton ();
892 break;
893
894 case PHOTOS_WINDOW_MODE_OVERVIEW:
895 offset_cntrlr = photos_offset_overview_controller_dup_singleton ();
896 trk_cntrlr = photos_tracker_overview_controller_dup_singleton ();
897 break;
898
899 case PHOTOS_WINDOW_MODE_SEARCH:
900 offset_cntrlr = photos_offset_search_controller_dup_singleton ();
901 trk_cntrlr = photos_tracker_search_controller_dup_singleton ();
902 break;
903
904 case PHOTOS_WINDOW_MODE_NONE:
905 case PHOTOS_WINDOW_MODE_EDIT:
906 case PHOTOS_WINDOW_MODE_PREVIEW:
907 default:
908 g_assert_not_reached ();
909 break;
910 }
911
912 if (out_offset_cntrlr != NULL)
913 g_set_object (out_offset_cntrlr, offset_cntrlr);
914
915 if (out_trk_cntrlr != NULL)
916 g_set_object (out_trk_cntrlr, trk_cntrlr);
917 }
918
919
920 gdouble
photos_utils_get_double_from_sparql_cursor_with_default(TrackerSparqlCursor * cursor,PhotosQueryColumns column,gdouble default_value)921 photos_utils_get_double_from_sparql_cursor_with_default (TrackerSparqlCursor *cursor,
922 PhotosQueryColumns column,
923 gdouble default_value)
924 {
925 TrackerSparqlValueType value_type;
926 gdouble ret_val = default_value;
927
928 value_type = tracker_sparql_cursor_get_value_type (cursor, column);
929 if (value_type == TRACKER_SPARQL_VALUE_TYPE_UNBOUND)
930 goto out;
931
932 ret_val = tracker_sparql_cursor_get_double (cursor, column);
933
934 out:
935 return ret_val;
936 }
937
938
939 gchar *
photos_utils_get_extension_from_mime_type(const gchar * mime_type)940 photos_utils_get_extension_from_mime_type (const gchar *mime_type)
941 {
942 g_autoptr (GSList) formats = NULL;
943 GSList *l;
944 gchar *ret_val = NULL;
945
946 formats = gdk_pixbuf_get_formats ();
947
948 for (l = formats; l != NULL; l = l->next)
949 {
950 GdkPixbufFormat *format = (GdkPixbufFormat*) l->data;
951 g_auto (GStrv) supported_mime_types = NULL;
952 guint i;
953
954 supported_mime_types = gdk_pixbuf_format_get_mime_types (format);
955 for (i = 0; supported_mime_types[i] != NULL; i++)
956 {
957 if (g_strcmp0 (mime_type, supported_mime_types[i]) == 0)
958 {
959 ret_val = photos_utils_get_pixbuf_common_suffix (format);
960 break;
961 }
962 }
963
964 if (ret_val != NULL)
965 break;
966 }
967
968 return ret_val;
969 }
970
971
972 gint
photos_utils_get_icon_size(void)973 photos_utils_get_icon_size (void)
974 {
975 GApplication *app;
976 gint scale;
977 gint size;
978
979 app = g_application_get_default ();
980 scale = photos_application_get_scale_factor (PHOTOS_APPLICATION (app));
981 size = photos_utils_get_icon_size_unscaled ();
982 return scale * size;
983 }
984
985
986 gint
photos_utils_get_icon_size_unscaled(void)987 photos_utils_get_icon_size_unscaled (void)
988 {
989 return 256;
990 }
991
992
993 gint64
photos_utils_get_integer_from_sparql_cursor_with_default(TrackerSparqlCursor * cursor,PhotosQueryColumns column,gint64 default_value)994 photos_utils_get_integer_from_sparql_cursor_with_default (TrackerSparqlCursor *cursor,
995 PhotosQueryColumns column,
996 gint64 default_value)
997 {
998 TrackerSparqlValueType value_type;
999 gint64 ret_val = default_value;
1000
1001 value_type = tracker_sparql_cursor_get_value_type (cursor, column);
1002 if (value_type == TRACKER_SPARQL_VALUE_TYPE_UNBOUND)
1003 goto out;
1004
1005 ret_val = tracker_sparql_cursor_get_integer (cursor, column);
1006
1007 out:
1008 return ret_val;
1009 }
1010
1011
1012 gint64
photos_utils_get_mtime_from_sparql_cursor(TrackerSparqlCursor * cursor)1013 photos_utils_get_mtime_from_sparql_cursor (TrackerSparqlCursor *cursor)
1014 {
1015 const gchar *mtime_str;
1016 gint64 mtime = -1;
1017
1018 mtime_str = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_MTIME, NULL);
1019 if (mtime_str != NULL)
1020 {
1021 g_autoptr (GDateTime) date_modified = NULL;
1022
1023 date_modified = g_date_time_new_from_iso8601 (mtime_str, NULL);
1024 if (date_modified != NULL)
1025 mtime = g_date_time_to_unix (date_modified);
1026 }
1027
1028 if (mtime == -1)
1029 mtime = g_get_real_time () / 1000000;
1030
1031 return mtime;
1032 }
1033
1034
1035 gchar *
photos_utils_get_pixbuf_common_suffix(GdkPixbufFormat * format)1036 photos_utils_get_pixbuf_common_suffix (GdkPixbufFormat *format)
1037 {
1038 g_auto (GStrv) extensions = NULL;
1039 gchar *result = NULL;
1040 gint i;
1041
1042 if (format == NULL)
1043 return NULL;
1044
1045 extensions = gdk_pixbuf_format_get_extensions (format);
1046 if (extensions[0] == NULL)
1047 return NULL;
1048
1049 /* try to find 3-char suffix first, use the last occurence */
1050 for (i = 0; extensions [i] != NULL; i++)
1051 {
1052 if (strlen (extensions[i]) <= 3)
1053 {
1054 g_free (result);
1055 result = g_ascii_strdown (extensions[i], -1);
1056 }
1057 }
1058
1059 /* otherwise take the first one */
1060 if (result == NULL)
1061 result = g_ascii_strdown (extensions[0], -1);
1062
1063 return result;
1064 }
1065
1066
1067 const gchar *
photos_utils_get_provider_name(PhotosBaseManager * src_mngr,PhotosBaseItem * item)1068 photos_utils_get_provider_name (PhotosBaseManager *src_mngr, PhotosBaseItem *item)
1069 {
1070 PhotosSource *source;
1071 const gchar *name;
1072 const gchar *resource_urn;
1073
1074 resource_urn = photos_base_item_get_resource_urn (item);
1075 source = PHOTOS_SOURCE (photos_base_manager_get_object_by_id (src_mngr, resource_urn));
1076 name = photos_source_get_name (source);
1077 return name;
1078 }
1079
1080
1081 gboolean
photos_utils_get_selection_mode(void)1082 photos_utils_get_selection_mode (void)
1083 {
1084 GAction *action;
1085 GApplication *app;
1086 g_autoptr (GVariant) state = NULL;
1087 gboolean selection_mode;
1088
1089 app = g_application_get_default ();
1090 action = g_action_map_lookup_action (G_ACTION_MAP (app), "selection-mode");
1091
1092 state = g_action_get_state (action);
1093 g_return_val_if_fail (state != NULL, FALSE);
1094
1095 selection_mode = g_variant_get_boolean (state);
1096
1097 return selection_mode;
1098 }
1099
1100
1101 GList *
photos_utils_get_urns_from_items(GList * items)1102 photos_utils_get_urns_from_items (GList *items)
1103 {
1104 GList *l;
1105 GList *urns = NULL;
1106
1107 for (l = items; l != NULL; l = l->next)
1108 {
1109 GdMainBoxItem *box_item = GD_MAIN_BOX_ITEM (l->data);
1110 const gchar *id;
1111
1112 id = gd_main_box_item_get_id (box_item);
1113 urns = g_list_prepend (urns, g_strdup (id));
1114 }
1115
1116 return g_list_reverse (urns);
1117 }
1118
1119
1120 const gchar *
photos_utils_get_version(void)1121 photos_utils_get_version (void)
1122 {
1123 const gchar *ret_val = NULL;
1124
1125 #ifdef PACKAGE_COMMIT_ID
1126 ret_val = PACKAGE_COMMIT_ID;
1127 #else
1128 ret_val = PACKAGE_VERSION;
1129 #endif
1130
1131 return ret_val;
1132 }
1133
1134
1135 gboolean
photos_utils_is_flatpak(void)1136 photos_utils_is_flatpak (void)
1137 {
1138 gboolean ret_val;
1139
1140 ret_val = g_file_test ("/.flatpak-info", G_FILE_TEST_EXISTS);
1141 return ret_val;
1142 }
1143
1144
1145 void
photos_utils_launch_online_accounts(const gchar * account_id)1146 photos_utils_launch_online_accounts (const gchar *account_id)
1147 {
1148 GApplication *app;
1149 g_autoptr (GDBusActionGroup) control_center = NULL;
1150 GDBusConnection *connection;
1151 GVariant *parameters;
1152 g_auto (GVariantBuilder) panel_parameters = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("av"));
1153
1154 app = g_application_get_default ();
1155 connection = g_application_get_dbus_connection (app);
1156 control_center = g_dbus_action_group_get (connection, "org.gnome.ControlCenter", "/org/gnome/ControlCenter");
1157
1158 if (account_id != NULL && account_id[0] != '\0')
1159 {
1160 GVariant *account_id_variant;
1161
1162 account_id_variant = g_variant_new_string (account_id);
1163 g_variant_builder_add (&panel_parameters, "v", account_id_variant);
1164 }
1165
1166 parameters = g_variant_new ("(s@av)", "online-accounts", g_variant_builder_end (&panel_parameters));
1167 g_action_group_activate_action (G_ACTION_GROUP (control_center), "launch-panel", parameters);
1168 }
1169
1170
1171 void
photos_utils_list_box_header_func(GtkListBoxRow * row,GtkListBoxRow * before,gpointer user_data)1172 photos_utils_list_box_header_func (GtkListBoxRow *row, GtkListBoxRow *before, gpointer user_data)
1173 {
1174 GtkWidget *header;
1175
1176 if (before == NULL)
1177 {
1178 gtk_list_box_row_set_header (row, NULL);
1179 return;
1180 }
1181
1182 header = gtk_list_box_row_get_header (row);
1183 if (header == NULL)
1184 {
1185 header = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
1186 gtk_widget_show (header);
1187 gtk_list_box_row_set_header (row, header);
1188 }
1189 }
1190
1191
1192 GAppLaunchContext *
photos_utils_new_app_launch_context_from_widget(GtkWidget * widget)1193 photos_utils_new_app_launch_context_from_widget (GtkWidget *widget)
1194 {
1195 GAppLaunchContext *ret_val = NULL;
1196 g_autoptr (GdkAppLaunchContext) ctx = NULL;
1197 GdkDisplay *display = NULL;
1198 GdkScreen *screen = NULL;
1199
1200 if (widget != NULL)
1201 {
1202 screen = gtk_widget_get_screen (widget);
1203 display = gdk_screen_get_display (screen);
1204 }
1205
1206 if (display == NULL)
1207 display = gdk_display_get_default ();
1208
1209 ctx = gdk_display_get_app_launch_context (display);
1210 if (screen != NULL)
1211 gdk_app_launch_context_set_screen (ctx, screen);
1212
1213 ret_val = G_APP_LAUNCH_CONTEXT (g_steal_pointer (&ctx));
1214 return ret_val;
1215 }
1216
1217
1218 void
photos_utils_object_list_free_full(GList * objects)1219 photos_utils_object_list_free_full (GList *objects)
1220 {
1221 g_list_free_full (objects, g_object_unref);
1222 }
1223
1224
1225 gchar *
photos_utils_print_zoom_action_detailed_name(const gchar * action_name,gdouble delta,PhotosZoomEvent event)1226 photos_utils_print_zoom_action_detailed_name (const gchar *action_name, gdouble delta, PhotosZoomEvent event)
1227 {
1228 g_autoptr (GVariant) target_value = NULL;
1229 gchar *ret_val = NULL;
1230
1231 g_return_val_if_fail (action_name != NULL && action_name[0] != '\0', NULL);
1232 g_return_val_if_fail (delta >= 0.0, NULL);
1233 g_return_val_if_fail (event != PHOTOS_ZOOM_EVENT_NONE, NULL);
1234
1235 target_value = photos_utils_create_zoom_target_value (delta, event);
1236 target_value = g_variant_ref_sink (target_value);
1237
1238 ret_val = g_action_print_detailed_name (action_name, target_value);
1239
1240 return ret_val;
1241 }
1242
1243
1244 static gboolean
photos_utils_adjustment_can_scroll(GtkAdjustment * adjustment)1245 photos_utils_adjustment_can_scroll (GtkAdjustment *adjustment)
1246 {
1247 gdouble lower;
1248 gdouble page_size;
1249 gdouble upper;
1250
1251 g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), FALSE);
1252
1253 lower = gtk_adjustment_get_lower (adjustment);
1254 page_size = gtk_adjustment_get_page_size (adjustment);
1255 upper = gtk_adjustment_get_upper (adjustment);
1256
1257 return upper - lower > page_size;
1258 }
1259
1260
1261 gboolean
photos_utils_scrolled_window_can_scroll(GtkScrolledWindow * scrolled_window)1262 photos_utils_scrolled_window_can_scroll (GtkScrolledWindow *scrolled_window)
1263 {
1264 GtkAdjustment *adjustment;
1265 gboolean ret_val = TRUE;
1266
1267 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), FALSE);
1268
1269 adjustment = gtk_scrolled_window_get_hadjustment (scrolled_window);
1270 if (photos_utils_adjustment_can_scroll (adjustment))
1271 goto out;
1272
1273 adjustment = gtk_scrolled_window_get_vadjustment (scrolled_window);
1274 if (photos_utils_adjustment_can_scroll (adjustment))
1275 goto out;
1276
1277 ret_val = FALSE;
1278
1279 out:
1280 return ret_val;
1281 }
1282
1283
1284 static void
photos_utils_adjustment_scroll(GtkAdjustment * adjustment,gdouble delta)1285 photos_utils_adjustment_scroll (GtkAdjustment *adjustment, gdouble delta)
1286 {
1287 gdouble value;
1288
1289 g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
1290
1291 value = gtk_adjustment_get_value (adjustment);
1292 value += delta;
1293 gtk_adjustment_set_value (adjustment, value);
1294 }
1295
1296
1297 void
photos_utils_scrolled_window_scroll(GtkScrolledWindow * scrolled_window,gdouble delta_x,gdouble delta_y)1298 photos_utils_scrolled_window_scroll (GtkScrolledWindow *scrolled_window, gdouble delta_x, gdouble delta_y)
1299 {
1300 GtkAdjustment *adjustment;
1301
1302 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
1303 g_return_if_fail (photos_utils_scrolled_window_can_scroll (scrolled_window));
1304
1305 adjustment = gtk_scrolled_window_get_hadjustment (scrolled_window);
1306 photos_utils_adjustment_scroll (adjustment, delta_x);
1307
1308 adjustment = gtk_scrolled_window_get_vadjustment (scrolled_window);
1309 photos_utils_adjustment_scroll (adjustment, delta_y);
1310 }
1311
1312
1313 static void
photos_utils_update_executed(GObject * source_object,GAsyncResult * res,gpointer user_data)1314 photos_utils_update_executed (GObject *source_object, GAsyncResult *res, gpointer user_data)
1315 {
1316 TrackerSparqlConnection *connection = TRACKER_SPARQL_CONNECTION (source_object);
1317 const gchar *urn = (gchar *) user_data;
1318
1319 {
1320 g_autoptr (GError) error = NULL;
1321
1322 tracker_sparql_connection_update_finish (connection, res, &error);
1323 if (error != NULL)
1324 g_warning ("Unable to update %s: %s", urn, error->message);
1325 }
1326 }
1327
1328
1329 void
photos_utils_set_edited_name(const gchar * urn,const gchar * title)1330 photos_utils_set_edited_name (const gchar *urn, const gchar *title)
1331 {
1332 g_autoptr (PhotosQuery) query = NULL;
1333 g_autoptr (PhotosTrackerQueue) queue = NULL;
1334 g_autofree gchar *sparql = NULL;
1335
1336 sparql = g_strdup_printf ("WITH tracker:Pictures "
1337 "DELETE { <%s> nie:title ?title } "
1338 "INSERT { "
1339 " <%s> a nmm:Photo ; nie:title \"%s\" . "
1340 "}"
1341 "WHERE { <%s> nie:title ?title }", urn, urn, title, urn);
1342
1343 query = photos_query_new (NULL, sparql);
1344
1345 {
1346 g_autoptr (GError) error = NULL;
1347
1348 queue = photos_tracker_queue_dup_singleton (NULL, &error);
1349 if (G_UNLIKELY (error != NULL))
1350 {
1351 g_warning ("Unable to set edited name %s: %s", urn, error->message);
1352 goto out;
1353 }
1354 }
1355
1356 photos_tracker_queue_update (queue, query, NULL, photos_utils_update_executed, g_strdup (urn), g_free);
1357
1358 out:
1359 return;
1360 }
1361
1362
1363 void
photos_utils_set_favorite(const gchar * urn,gboolean is_favorite)1364 photos_utils_set_favorite (const gchar *urn, gboolean is_favorite)
1365 {
1366 g_autoptr (PhotosQuery) query = NULL;
1367 g_autoptr (PhotosTrackerQueue) queue = NULL;
1368 g_autofree gchar *sparql = NULL;
1369
1370 if (is_favorite)
1371 {
1372 sparql = g_strdup_printf ("INSERT DATA { "
1373 " GRAPH tracker:Pictures {"
1374 " <%s> a nmm:Photo ; nao:hasTag nao:predefined-tag-favorite ."
1375 " } "
1376 "}", urn);
1377 }
1378 else
1379 {
1380 sparql = g_strdup_printf ("DELETE DATA {"
1381 " GRAPH tracker:Pictures {"
1382 " <%s> nao:hasTag nao:predefined-tag-favorite "
1383 " } "
1384 "}", urn);
1385 }
1386
1387 query = photos_query_new (NULL, sparql);
1388
1389 {
1390 g_autoptr (GError) error = NULL;
1391
1392 queue = photos_tracker_queue_dup_singleton (NULL, &error);
1393 if (G_UNLIKELY (error != NULL))
1394 {
1395 g_warning ("Unable to set favorite %s: %s", urn, error->message);
1396 goto out;
1397 }
1398 }
1399
1400 photos_tracker_queue_update (queue, query, NULL, photos_utils_update_executed, g_strdup (urn), g_free);
1401
1402 out:
1403 return;
1404 }
1405
1406
1407 gboolean
photos_utils_set_string(gchar ** string_ptr,const gchar * new_string)1408 photos_utils_set_string (gchar **string_ptr, const gchar *new_string)
1409 {
1410 gboolean ret_val = FALSE;
1411
1412 g_return_val_if_fail (string_ptr != NULL, FALSE);
1413
1414 if (*string_ptr == new_string)
1415 goto out;
1416
1417 if (g_strcmp0 (*string_ptr, new_string) == 0)
1418 goto out;
1419
1420 g_free (*string_ptr);
1421 *string_ptr = g_strdup (new_string);
1422
1423 ret_val = TRUE;
1424
1425 out:
1426 return ret_val;
1427 }
1428
1429
1430 gboolean
photos_utils_take_string(gchar ** string_ptr,gchar * new_string)1431 photos_utils_take_string (gchar **string_ptr, gchar *new_string)
1432 {
1433 gboolean ret_val = FALSE;
1434
1435 g_return_val_if_fail (string_ptr != NULL, FALSE);
1436
1437 if (*string_ptr == new_string)
1438 {
1439 new_string = NULL;
1440 goto out;
1441 }
1442
1443 if (g_strcmp0 (*string_ptr, new_string) == 0)
1444 goto out;
1445
1446 g_free (*string_ptr);
1447 *string_ptr = new_string;
1448 new_string = NULL;
1449
1450 ret_val = TRUE;
1451
1452 out:
1453 g_free (new_string);
1454 return ret_val;
1455 }
1456