1 /*
2  * Photos - access, organize and share your photos on GNOME
3  * Copyright © 2012 – 2019 Red Hat, Inc.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 /* Based on code from:
20  *   + Documents
21  */
22 
23 
24 #include "config.h"
25 
26 #include <string.h>
27 
28 #include <dazzle.h>
29 #include <gio/gio.h>
30 #include <glib.h>
31 #include <glib/gi18n.h>
32 #include <tracker-sparql.h>
33 
34 #include "photos-application.h"
35 #include "photos-debug.h"
36 #include "photos-device-item.h"
37 #include "photos-enums.h"
38 #include "photos-filterable.h"
39 #include "photos-item-manager.h"
40 #include "photos-local-item.h"
41 #include "photos-marshalers.h"
42 #include "photos-query.h"
43 #include "photos-search-context.h"
44 #include "photos-single-item-job.h"
45 #include "photos-tracker-queue.h"
46 #include "photos-utils.h"
47 
48 
49 struct _PhotosItemManager
50 {
51   PhotosBaseManager parent_instance;
52   GObject *active_object;
53   GCancellable *loader_cancellable;
54   GHashTable *collections;
55   GHashTable *hidden_items;
56   GHashTable *wait_for_changes_table;
57   GIOExtensionPoint *extension_point;
58   GQueue *history;
59   PhotosBaseItem *active_collection;
60   PhotosBaseManager **item_mngr_chldrn;
61   PhotosLoadState load_state;
62   PhotosTrackerQueue *queue;
63   PhotosWindowMode mode;
64   TrackerNotifier *notifier;
65   gboolean fullscreen;
66   gboolean *constrain_additions;
67   guint wait_for_changes_id;
68 };
69 
70 enum
71 {
72   ACTIVE_COLLECTION_CHANGED,
73   CAN_FULLSCREEN_CHANGED,
74   FULLSCREEN_CHANGED,
75   LOAD_ERROR,
76   LOAD_FINISHED,
77   LOAD_STARTED,
78   WINDOW_MODE_CHANGED,
79   LAST_SIGNAL
80 };
81 
82 static guint signals[LAST_SIGNAL] = { 0 };
83 
84 static void photos_item_manager_list_model_iface_init (GListModelInterface *iface);
85 
86 
87 G_DEFINE_TYPE_WITH_CODE (PhotosItemManager, photos_item_manager, PHOTOS_TYPE_BASE_MANAGER,
88                          G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, photos_item_manager_list_model_iface_init));
89 DZL_DEFINE_COUNTER (instances, "PhotosItemManager", "Instances", "Number of PhotosItemManager instances")
90 
91 
92 typedef struct _PhotosItemManagerHiddenItem PhotosItemManagerHiddenItem;
93 
94 struct _PhotosItemManagerHiddenItem
95 {
96   PhotosBaseItem *item;
97   gboolean *modes;
98   guint n_modes;
99 };
100 
101 
102 enum
103 {
104   WAIT_FOR_CHANGES_TIMEOUT = 1 /* s */
105 };
106 
107 
108 static gboolean photos_item_manager_wait_for_changes_timeout (gpointer user_data);
109 
110 
111 static PhotosItemManagerHiddenItem *
photos_item_manager_hidden_item_new(PhotosBaseItem * item)112 photos_item_manager_hidden_item_new (PhotosBaseItem *item)
113 {
114   GEnumClass *window_mode_class; /* TODO: use g_autoptr */
115   PhotosItemManagerHiddenItem *hidden_item;
116 
117   hidden_item = g_slice_new0 (PhotosItemManagerHiddenItem);
118   hidden_item->item = g_object_ref (item);
119 
120   window_mode_class = G_ENUM_CLASS (g_type_class_ref (PHOTOS_TYPE_WINDOW_MODE));
121   hidden_item->n_modes = window_mode_class->n_values;
122   hidden_item->modes = (gboolean *) g_malloc0_n (hidden_item->n_modes, sizeof (gboolean));
123 
124   g_type_class_unref (window_mode_class);
125   return hidden_item;
126 }
127 
128 
129 static void
photos_item_manager_hidden_item_free(PhotosItemManagerHiddenItem * hidden_item)130 photos_item_manager_hidden_item_free (PhotosItemManagerHiddenItem *hidden_item)
131 {
132   g_free (hidden_item->modes);
133   g_object_unref (hidden_item->item);
134   g_slice_free (PhotosItemManagerHiddenItem, hidden_item);
135 }
136 
137 
138 static void
photos_item_manager_add_object(PhotosBaseManager * mngr,GObject * object)139 photos_item_manager_add_object (PhotosBaseManager *mngr, GObject *object)
140 {
141   g_assert_not_reached ();
142 }
143 
144 
145 static gboolean
photos_item_manager_can_add_mtime_for_mode(PhotosItemManager * self,gint64 mtime,PhotosWindowMode mode)146 photos_item_manager_can_add_mtime_for_mode (PhotosItemManager *self, gint64 mtime, PhotosWindowMode mode)
147 {
148   g_autoptr (PhotosBaseItem) oldest_item = NULL;
149   PhotosBaseManager *item_mngr_chld;
150   gboolean ret_val = TRUE;
151   gint64 oldest_mtime;
152   guint n_items;
153 
154   if (!self->constrain_additions[mode])
155     goto out;
156 
157   item_mngr_chld = self->item_mngr_chldrn[mode];
158   n_items = g_list_model_get_n_items (G_LIST_MODEL (item_mngr_chld));
159   if (n_items == 0)
160     goto out;
161 
162   oldest_item = PHOTOS_BASE_ITEM (g_list_model_get_object (G_LIST_MODEL (item_mngr_chld), n_items - 1));
163   oldest_mtime = photos_base_item_get_mtime (oldest_item);
164 
165   if (mtime > oldest_mtime)
166     goto out;
167 
168   ret_val = FALSE;
169 
170  out:
171   return ret_val;
172 }
173 
174 
175 static gboolean
photos_item_manager_can_add_cursor_for_mode(PhotosItemManager * self,TrackerSparqlCursor * cursor,PhotosWindowMode mode)176 photos_item_manager_can_add_cursor_for_mode (PhotosItemManager *self,
177                                              TrackerSparqlCursor *cursor,
178                                              PhotosWindowMode mode)
179 {
180   gboolean ret_val = TRUE;
181   gint64 mtime;
182 
183   mtime = photos_utils_get_mtime_from_sparql_cursor (cursor);
184   ret_val = photos_item_manager_can_add_mtime_for_mode (self, mtime, mode);
185   return ret_val;
186 }
187 
188 
189 static gboolean
photos_item_manager_can_add_item_for_mode(PhotosItemManager * self,PhotosBaseItem * item,PhotosWindowMode mode)190 photos_item_manager_can_add_item_for_mode (PhotosItemManager *self, PhotosBaseItem *item, PhotosWindowMode mode)
191 {
192   gboolean ret_val = TRUE;
193   gint64 mtime;
194 
195   mtime = photos_base_item_get_mtime (item);
196   ret_val = photos_item_manager_can_add_mtime_for_mode (self, mtime, mode);
197   return ret_val;
198 }
199 
200 
201 static gboolean
photos_item_manager_cursor_is_collection(TrackerSparqlCursor * cursor)202 photos_item_manager_cursor_is_collection (TrackerSparqlCursor *cursor)
203 {
204   gboolean ret_val = FALSE;
205   const gchar *rdf_type;
206 
207   rdf_type = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_RDF_TYPE, NULL);
208   if (rdf_type == NULL)
209     goto out;
210 
211   ret_val = strstr (rdf_type, "nfo#DataContainer") != NULL;
212 
213  out:
214   return ret_val;
215 }
216 
217 
218 static gboolean
photos_item_manager_try_to_add_item_for_mode(PhotosItemManager * self,PhotosBaseItem * item,PhotosWindowMode mode)219 photos_item_manager_try_to_add_item_for_mode (PhotosItemManager *self,
220                                               PhotosBaseItem *item,
221                                               PhotosWindowMode mode)
222 {
223   gboolean ret_val = FALSE;
224 
225   g_return_val_if_fail (PHOTOS_IS_ITEM_MANAGER (self), FALSE);
226   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (item), FALSE);
227   g_return_val_if_fail (mode != PHOTOS_WINDOW_MODE_NONE, FALSE);
228   g_return_val_if_fail (mode != PHOTOS_WINDOW_MODE_EDIT, FALSE);
229   g_return_val_if_fail (mode != PHOTOS_WINDOW_MODE_PREVIEW, FALSE);
230 
231   if (!photos_item_manager_can_add_item_for_mode (self, item, mode))
232     goto out;
233 
234   photos_base_manager_add_object (self->item_mngr_chldrn[mode], G_OBJECT (item));
235   ret_val = TRUE;
236 
237  out:
238   return ret_val;
239 }
240 
241 
242 static void
photos_item_manager_info_updated(PhotosBaseItem * item,gpointer user_data)243 photos_item_manager_info_updated (PhotosBaseItem *item, gpointer user_data)
244 {
245   PhotosItemManager *self = PHOTOS_ITEM_MANAGER (user_data);
246   GType base_item_type;
247   PhotosBaseItem *updated_item;
248   gboolean is_collection;
249   gboolean is_favorite;
250   const gchar *id;
251 
252   g_return_if_fail (PHOTOS_IS_BASE_ITEM (item));
253 
254   id = photos_filterable_get_id (PHOTOS_FILTERABLE (item));
255   updated_item = PHOTOS_BASE_ITEM (photos_base_manager_get_object_by_id (PHOTOS_BASE_MANAGER (self), id));
256   g_return_if_fail (updated_item == item);
257 
258   base_item_type = G_OBJECT_TYPE (item);
259   if (base_item_type == PHOTOS_TYPE_DEVICE_ITEM)
260     goto out;
261 
262   is_collection = photos_base_item_is_collection (item);
263   is_favorite = photos_base_item_is_favorite (item);
264 
265   if (is_collection)
266     {
267       photos_item_manager_try_to_add_item_for_mode (self, item, PHOTOS_WINDOW_MODE_COLLECTIONS);
268     }
269   else
270     {
271       if (is_favorite)
272         photos_item_manager_try_to_add_item_for_mode (self, item, PHOTOS_WINDOW_MODE_FAVORITES);
273 
274       photos_item_manager_try_to_add_item_for_mode (self, item, PHOTOS_WINDOW_MODE_OVERVIEW);
275     }
276 
277   if (is_collection)
278     {
279       photos_base_manager_remove_object (self->item_mngr_chldrn[PHOTOS_WINDOW_MODE_COLLECTION_VIEW],
280                                          G_OBJECT (item));
281       photos_base_manager_remove_object (self->item_mngr_chldrn[PHOTOS_WINDOW_MODE_FAVORITES], G_OBJECT (item));
282       photos_base_manager_remove_object (self->item_mngr_chldrn[PHOTOS_WINDOW_MODE_OVERVIEW], G_OBJECT (item));
283     }
284   else
285     {
286       photos_base_manager_remove_object (self->item_mngr_chldrn[PHOTOS_WINDOW_MODE_COLLECTIONS], G_OBJECT (item));
287 
288       if (!is_favorite)
289         photos_base_manager_remove_object (self->item_mngr_chldrn[PHOTOS_WINDOW_MODE_FAVORITES], G_OBJECT (item));
290     }
291 
292  out:
293   return;
294 }
295 
296 
297 static void
photos_item_manager_add_cursor_for_mode(PhotosItemManager * self,GType base_item_type,TrackerSparqlCursor * cursor,PhotosWindowMode mode,gboolean force)298 photos_item_manager_add_cursor_for_mode (PhotosItemManager *self,
299                                          GType base_item_type,
300                                          TrackerSparqlCursor *cursor,
301                                          PhotosWindowMode mode,
302                                          gboolean force)
303 {
304   g_autoptr (PhotosBaseItem) item = NULL;
305   PhotosBaseManager *item_mngr_chld;
306   gboolean is_collection;
307   const gchar *id;
308 
309   g_return_if_fail (PHOTOS_IS_ITEM_MANAGER (self));
310   g_return_if_fail (base_item_type == G_TYPE_NONE
311                     || (base_item_type != PHOTOS_TYPE_BASE_ITEM
312                         && g_type_is_a (base_item_type, PHOTOS_TYPE_BASE_ITEM)));
313   g_return_if_fail (TRACKER_IS_SPARQL_CURSOR (cursor));
314   g_return_if_fail (mode != PHOTOS_WINDOW_MODE_NONE);
315   g_return_if_fail (mode != PHOTOS_WINDOW_MODE_EDIT);
316   g_return_if_fail (mode != PHOTOS_WINDOW_MODE_PREVIEW);
317 
318   is_collection = photos_item_manager_cursor_is_collection (cursor);
319   g_return_if_fail ((is_collection
320                      && (mode == PHOTOS_WINDOW_MODE_COLLECTIONS || mode == PHOTOS_WINDOW_MODE_SEARCH))
321                     || (!is_collection && mode != PHOTOS_WINDOW_MODE_COLLECTIONS));
322 
323   if (!force && !photos_item_manager_can_add_cursor_for_mode (self, cursor, mode))
324     goto out;
325 
326   item_mngr_chld = self->item_mngr_chldrn[mode];
327   id = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_URN, NULL);
328 
329   item = PHOTOS_BASE_ITEM (photos_base_manager_get_object_by_id (item_mngr_chld, id));
330   if (item != NULL)
331     {
332       g_object_ref (item);
333     }
334   else
335     {
336       gboolean already_present = FALSE;
337 
338       item = PHOTOS_BASE_ITEM (photos_base_manager_get_object_by_id (PHOTOS_BASE_MANAGER (self), id));
339       if (item != NULL)
340         {
341           g_object_ref (item);
342           already_present = TRUE;
343         }
344       else
345         {
346           item = photos_item_manager_create_item (self, base_item_type, cursor, TRUE);
347           if (photos_base_item_is_collection (item))
348             g_hash_table_insert (self->collections, g_strdup (id), g_object_ref (item));
349 
350           g_signal_connect_object (item, "info-updated", G_CALLBACK (photos_item_manager_info_updated), self, 0);
351         }
352 
353       photos_base_manager_add_object (item_mngr_chld, G_OBJECT (item));
354       photos_base_manager_add_object (self->item_mngr_chldrn[0], G_OBJECT (item));
355 
356       if (!already_present)
357         g_signal_emit_by_name (self, "object-added", G_OBJECT (item));
358     }
359 
360  out:
361   return;
362 }
363 
364 
365 static void
photos_item_manager_check_wait_for_changes(PhotosItemManager * self,const gchar * id,const gchar * uri)366 photos_item_manager_check_wait_for_changes (PhotosItemManager *self, const gchar *id, const gchar *uri)
367 {
368   GList *l;
369   GList *tasks;
370 
371   g_return_if_fail (id != NULL && id[0] != '\0');
372   g_return_if_fail (uri != NULL && uri[0] != '\0');
373 
374   photos_debug (PHOTOS_DEBUG_TRACKER, "Detected changes to %s", uri);
375 
376   tasks = (GList *) g_hash_table_lookup (self->wait_for_changes_table, uri);
377   for (l = tasks; l != NULL; l = l->next)
378     {
379       GTask *task = G_TASK (l->data);
380       g_task_return_pointer (task, g_strdup (id), g_free);
381     }
382 
383   g_hash_table_remove (self->wait_for_changes_table, uri);
384 }
385 
386 
387 static void
photos_item_manager_item_created_executed_import(GObject * source_object,GAsyncResult * res,gpointer user_data)388 photos_item_manager_item_created_executed_import (GObject *source_object, GAsyncResult *res, gpointer user_data)
389 {
390   g_autoptr (PhotosItemManager) self = PHOTOS_ITEM_MANAGER (user_data);
391   PhotosSingleItemJob *job_import = PHOTOS_SINGLE_ITEM_JOB (source_object);
392   TrackerSparqlCursor *cursor = NULL; /* TODO: use g_autoptr */
393 
394   {
395     g_autoptr (GError) error = NULL;
396 
397     cursor = photos_single_item_job_finish (job_import, res, &error);
398     if (error != NULL)
399       {
400         g_warning ("Unable to query single item: %s", error->message);
401         goto out;
402       }
403   }
404 
405   if (cursor == NULL)
406     goto out;
407 
408   photos_item_manager_add_item_for_mode (self, PHOTOS_TYPE_DEVICE_ITEM, PHOTOS_WINDOW_MODE_IMPORT, cursor);
409 
410  out:
411   g_clear_object (&cursor);
412 }
413 
414 
415 static void
photos_item_manager_item_created_executed_overview(GObject * source_object,GAsyncResult * res,gpointer user_data)416 photos_item_manager_item_created_executed_overview (GObject *source_object, GAsyncResult *res, gpointer user_data)
417 {
418   g_autoptr (PhotosItemManager) self = PHOTOS_ITEM_MANAGER (user_data);
419   PhotosSingleItemJob *job_overview = PHOTOS_SINGLE_ITEM_JOB (source_object);
420   TrackerSparqlCursor *cursor = NULL; /* TODO: use g_autoptr */
421 
422   {
423     g_autoptr (GError) error = NULL;
424 
425     cursor = photos_single_item_job_finish (job_overview, res, &error);
426     if (error != NULL)
427       {
428         g_warning ("Unable to query single item: %s", error->message);
429         goto out;
430       }
431   }
432 
433   if (cursor == NULL)
434     goto out;
435 
436   photos_item_manager_add_item (self, G_TYPE_NONE, cursor, FALSE);
437 
438  out:
439   g_clear_object (&cursor);
440 }
441 
442 
443 static void
photos_item_manager_item_created_executed_wait_for_changes(GObject * source_object,GAsyncResult * res,gpointer user_data)444 photos_item_manager_item_created_executed_wait_for_changes (GObject *source_object,
445                                                             GAsyncResult *res,
446                                                             gpointer user_data)
447 {
448   g_autoptr (PhotosItemManager) self = PHOTOS_ITEM_MANAGER (user_data);
449   PhotosSingleItemJob *job_wait_for_changes = PHOTOS_SINGLE_ITEM_JOB (source_object);
450   TrackerSparqlCursor *cursor = NULL; /* TODO: use g_autoptr */
451 
452   {
453     g_autoptr (GError) error = NULL;
454 
455     cursor = photos_single_item_job_finish (job_wait_for_changes, res, &error);
456     if (error != NULL)
457       {
458         g_warning ("Unable to query single item: %s", error->message);
459         goto out;
460       }
461   }
462 
463   if (cursor == NULL)
464     goto out;
465 
466   if (!photos_item_manager_cursor_is_collection (cursor))
467     {
468       const gchar *id;
469       const gchar *uri;
470 
471       id = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_URN, NULL);
472       uri = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_URI, NULL);
473       if (id != NULL && id[0] != '\0' && uri != NULL && uri[0] != '\0')
474         photos_item_manager_check_wait_for_changes (self, id, uri);
475     }
476 
477  out:
478   g_clear_object (&cursor);
479 }
480 
481 
482 static void
photos_item_manager_item_created(PhotosItemManager * self,const gchar * urn)483 photos_item_manager_item_created (PhotosItemManager *self, const gchar *urn)
484 {
485   GApplication *app;
486   PhotosItemManagerHiddenItem *old_hidden_item;
487   PhotosSearchContextState *state;
488   g_autoptr (PhotosSingleItemJob) job_import = NULL;
489   g_autoptr (PhotosSingleItemJob) job_overview = NULL;
490   guint wait_for_changes_size;
491 
492   old_hidden_item = (PhotosItemManagerHiddenItem *) g_hash_table_lookup (self->hidden_items, urn);
493   g_return_if_fail (old_hidden_item == NULL);
494 
495   app = g_application_get_default ();
496   state = photos_search_context_get_state (PHOTOS_SEARCH_CONTEXT (app));
497 
498   job_import = photos_single_item_job_new (urn);
499   photos_single_item_job_run (job_import,
500                               state,
501                               PHOTOS_QUERY_FLAGS_IMPORT,
502                               NULL,
503                               photos_item_manager_item_created_executed_import,
504                               g_object_ref (self));
505 
506   job_overview = photos_single_item_job_new (urn);
507   photos_single_item_job_run (job_overview,
508                               state,
509                               PHOTOS_QUERY_FLAGS_NONE,
510                               NULL,
511                               photos_item_manager_item_created_executed_overview,
512                               g_object_ref (self));
513 
514   wait_for_changes_size = g_hash_table_size (self->wait_for_changes_table);
515   if (wait_for_changes_size > 0)
516     {
517       g_autoptr (PhotosSingleItemJob) job_wait_for_changes = NULL;
518 
519       job_wait_for_changes = photos_single_item_job_new (urn);
520       photos_single_item_job_run (job_wait_for_changes,
521                                   state,
522                                   PHOTOS_QUERY_FLAGS_UNFILTERED,
523                                   NULL,
524                                   photos_item_manager_item_created_executed_wait_for_changes,
525                                   g_object_ref (self));
526     }
527 }
528 
529 
530 static void
photos_item_manager_notifier_events_foreach(gpointer data,gpointer user_data)531 photos_item_manager_notifier_events_foreach (gpointer data, gpointer user_data)
532 {
533   PhotosItemManager *self = PHOTOS_ITEM_MANAGER (user_data);
534   TrackerNotifierEvent *event = (TrackerNotifierEvent *) data;
535   TrackerNotifierEventType event_type;
536   const gchar *event_urn;
537 
538   event_type = tracker_notifier_event_get_event_type (event);
539   event_urn = tracker_notifier_event_get_urn (event);
540   photos_debug (PHOTOS_DEBUG_TRACKER, "Received TrackerNotifierEvent (%d): URN %s", event_type, event_urn);
541 
542   g_return_if_fail (event_urn != NULL && event_urn[0] != '\0');
543 
544   if (event_type == TRACKER_NOTIFIER_EVENT_UPDATE)
545     {
546       GObject *object;
547 
548       object = photos_base_manager_get_object_by_id (PHOTOS_BASE_MANAGER (self), event_urn);
549       if (object != NULL)
550         {
551           photos_base_item_refresh (PHOTOS_BASE_ITEM (object));
552 
553           if (!photos_base_item_is_collection (PHOTOS_BASE_ITEM (object)))
554             {
555               const gchar *uri;
556 
557               uri = photos_base_item_get_uri (PHOTOS_BASE_ITEM (object));
558               photos_item_manager_check_wait_for_changes (self, event_urn, uri);
559             }
560         }
561     }
562   else if (event_type == TRACKER_NOTIFIER_EVENT_CREATE)
563     {
564       photos_item_manager_item_created (self, event_urn);
565     }
566   else if (event_type == TRACKER_NOTIFIER_EVENT_DELETE)
567     {
568       GObject *object;
569 
570       object = photos_base_manager_get_object_by_id (PHOTOS_BASE_MANAGER (self), event_urn);
571       if (object != NULL)
572         {
573           photos_base_item_destroy (PHOTOS_BASE_ITEM (object));
574           g_hash_table_remove (self->hidden_items, event_urn);
575           photos_base_manager_remove_object_by_id (PHOTOS_BASE_MANAGER (self), event_urn);
576         }
577     }
578 }
579 
580 
581 static void
photos_item_manager_notifier_events(PhotosItemManager * self,const gchar * service,const gchar * graph,GPtrArray * events)582 photos_item_manager_notifier_events (PhotosItemManager *self,
583                                      const gchar *service,
584                                      const gchar *graph,
585                                      GPtrArray *events)
586 {
587   if (g_strcmp0 (graph, PHOTOS_PICTURES_GRAPH) == 0)
588     g_ptr_array_foreach (events, photos_item_manager_notifier_events_foreach, self);
589 }
590 
591 
592 static void
photos_item_manager_clear_active_item_load(PhotosItemManager * self)593 photos_item_manager_clear_active_item_load (PhotosItemManager *self)
594 {
595   if (self->loader_cancellable != NULL)
596     {
597       g_cancellable_cancel (self->loader_cancellable);
598       g_clear_object (&self->loader_cancellable);
599     }
600 }
601 
602 
603 static gboolean
photos_item_manager_cursor_is_favorite(TrackerSparqlCursor * cursor)604 photos_item_manager_cursor_is_favorite (TrackerSparqlCursor *cursor)
605 {
606   gboolean favorite;
607   const gchar *rdf_type;
608 
609   favorite = tracker_sparql_cursor_get_boolean (cursor, PHOTOS_QUERY_COLUMNS_RESOURCE_FAVORITE);
610   rdf_type = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_RDF_TYPE, NULL);
611   if (strstr (rdf_type, "nfo#DataContainer") != NULL)
612     favorite = FALSE;
613 
614   return favorite;
615 }
616 
617 
618 static void
photos_item_manager_remove_timeout(PhotosItemManager * self)619 photos_item_manager_remove_timeout (PhotosItemManager *self)
620 {
621   if (self->wait_for_changes_id != 0)
622     {
623       g_source_remove (self->wait_for_changes_id);
624       self->wait_for_changes_id = 0;
625     }
626 }
627 
628 
629 static void
photos_item_manager_wait_for_changes_timeout_cursor_next(GObject * source_object,GAsyncResult * res,gpointer user_data)630 photos_item_manager_wait_for_changes_timeout_cursor_next (GObject *source_object, GAsyncResult *res, gpointer user_data)
631 {
632   PhotosItemManager *self = PHOTOS_ITEM_MANAGER (user_data);
633   TrackerSparqlCursor *cursor = TRACKER_SPARQL_CURSOR (source_object);
634   gboolean success;
635   const gchar *id;
636   const gchar *uri;
637   guint wait_for_changes_size;
638 
639   {
640     g_autoptr (GError) error = NULL;
641 
642     /* Note that tracker_sparql_cursor_next_finish can return FALSE even
643      * without an error.
644      */
645     success = tracker_sparql_cursor_next_finish (cursor, res, &error);
646     if (error != NULL)
647       {
648         g_warning ("Unable to fetch URN for URI: %s", error->message);
649         goto out;
650       }
651   }
652 
653   if (!success)
654     goto out;
655 
656   id = tracker_sparql_cursor_get_string (cursor, 0, NULL);
657   uri = tracker_sparql_cursor_get_string (cursor, 1, NULL);
658   if (id != NULL && id[0] != '\0' && uri != NULL && uri[0] != '\0')
659     photos_item_manager_check_wait_for_changes (self, id, uri);
660 
661   photos_item_manager_remove_timeout (self);
662 
663   wait_for_changes_size = g_hash_table_size (self->wait_for_changes_table);
664   if (wait_for_changes_size > 0)
665     {
666       self->wait_for_changes_id = g_timeout_add_seconds (WAIT_FOR_CHANGES_TIMEOUT,
667                                                          photos_item_manager_wait_for_changes_timeout,
668                                                          self);
669     }
670 
671  out:
672   g_object_unref (self);
673 }
674 
675 
676 static void
photos_item_manager_wait_for_changes_timeout_query_executed(GObject * source_object,GAsyncResult * res,gpointer user_data)677 photos_item_manager_wait_for_changes_timeout_query_executed (GObject *source_object,
678                                                              GAsyncResult *res,
679                                                              gpointer user_data)
680 {
681   PhotosItemManager *self = PHOTOS_ITEM_MANAGER (user_data);
682   TrackerSparqlConnection *connection = TRACKER_SPARQL_CONNECTION (source_object);
683   TrackerSparqlCursor *cursor = NULL; /* TODO: use g_autoptr */
684 
685   {
686     g_autoptr (GError) error = NULL;
687 
688     cursor = tracker_sparql_connection_query_finish (connection, res, &error);
689     if (error != NULL)
690       {
691         g_warning ("Unable to fetch URN for URI: %s", error->message);
692         goto out;
693       }
694   }
695 
696   if (cursor == NULL)
697     goto out;
698 
699   tracker_sparql_cursor_next_async (cursor,
700                                     NULL,
701                                     photos_item_manager_wait_for_changes_timeout_cursor_next,
702                                     g_object_ref (self));
703 
704  out:
705   g_clear_object (&cursor);
706 }
707 
708 
709 static gboolean
photos_item_manager_wait_for_changes_timeout(gpointer user_data)710 photos_item_manager_wait_for_changes_timeout (gpointer user_data)
711 {
712   PhotosItemManager *self = PHOTOS_ITEM_MANAGER (user_data);
713   GApplication *app;
714   GHashTableIter iter;
715   GList *tasks;
716   const gchar *miner_files_name;
717   const gchar *uri;
718 
719   if (G_UNLIKELY (self->queue == NULL))
720     goto out;
721 
722   app = g_application_get_default ();
723   miner_files_name = photos_application_get_miner_files_name (PHOTOS_APPLICATION (app));
724 
725   g_hash_table_iter_init (&iter, self->wait_for_changes_table);
726   while (g_hash_table_iter_next (&iter, (gpointer *) &uri, (gpointer *) &tasks))
727     {
728       g_autoptr (PhotosQuery) query = NULL;
729       g_autofree gchar *sparql = NULL;
730 
731       sparql = g_strdup_printf ("SELECT ?urn ?file WHERE {"
732                                 "  SERVICE <dbus:%s> {"
733                                 "    GRAPH tracker:Pictures {"
734                                 "      SELECT ?urn nie:isStoredAs (?urn) AS ?file WHERE {"
735                                 "        ?urn nie:isStoredAs '%s' "
736                                 "    }"
737                                 "  }"
738                                 "}",
739                                 miner_files_name,
740                                 uri);
741 
742       query = photos_query_new (NULL, sparql);
743       photos_tracker_queue_select (self->queue,
744                                    query,
745                                    NULL,
746                                    photos_item_manager_wait_for_changes_timeout_query_executed,
747                                    g_object_ref (self),
748                                    g_object_unref);
749     }
750 
751  out:
752   self->wait_for_changes_id = 0;
753   return G_SOURCE_REMOVE;
754 }
755 
756 
757 static GObject *
photos_item_manager_get_active_object(PhotosBaseManager * mngr)758 photos_item_manager_get_active_object (PhotosBaseManager *mngr)
759 {
760   PhotosItemManager *self = PHOTOS_ITEM_MANAGER (mngr);
761   return self->active_object;
762 }
763 
764 
765 static gpointer
photos_item_manager_get_item(GListModel * list,guint position)766 photos_item_manager_get_item (GListModel *list, guint position)
767 {
768   PhotosItemManager *self = PHOTOS_ITEM_MANAGER (list);
769   gpointer item;
770 
771   item = g_list_model_get_item (G_LIST_MODEL (self->item_mngr_chldrn[0]), position);
772   return item;
773 }
774 
775 
776 static GType
photos_item_manager_get_item_type(GListModel * list)777 photos_item_manager_get_item_type (GListModel *list)
778 {
779   return PHOTOS_TYPE_BASE_ITEM;
780 }
781 
782 
783 static guint
photos_item_manager_get_n_items(GListModel * list)784 photos_item_manager_get_n_items (GListModel *list)
785 {
786   PhotosItemManager *self = PHOTOS_ITEM_MANAGER (list);
787   guint n_items;
788 
789   n_items = g_list_model_get_n_items (G_LIST_MODEL (self->item_mngr_chldrn[0]));
790   return n_items;
791 }
792 
793 
794 static GObject *
photos_item_manager_get_object_by_id(PhotosBaseManager * mngr,const gchar * id)795 photos_item_manager_get_object_by_id (PhotosBaseManager *mngr, const gchar *id)
796 {
797   PhotosItemManager *self = PHOTOS_ITEM_MANAGER (mngr);
798   GObject *ret_val;
799 
800   ret_val = photos_base_manager_get_object_by_id (self->item_mngr_chldrn[0], id);
801   return ret_val;
802 }
803 
804 
805 static gchar *
photos_item_manager_get_where(PhotosBaseManager * mngr,gint flags)806 photos_item_manager_get_where (PhotosBaseManager *mngr, gint flags)
807 {
808   PhotosItemManager *self = PHOTOS_ITEM_MANAGER (mngr);
809 
810   if (self->active_collection == NULL || (flags & PHOTOS_QUERY_FLAGS_SEARCH) != 0)
811     return g_strdup ("");
812 
813   return photos_base_item_get_where (self->active_collection);
814 }
815 
816 
817 static void
photos_item_manager_item_load(GObject * source_object,GAsyncResult * res,gpointer user_data)818 photos_item_manager_item_load (GObject *source_object, GAsyncResult *res, gpointer user_data)
819 {
820   g_autoptr (PhotosItemManager) self = PHOTOS_ITEM_MANAGER (user_data);
821   g_autoptr (GeglNode) node = NULL;
822   PhotosBaseItem *item = PHOTOS_BASE_ITEM (source_object);
823 
824   g_clear_object (&self->loader_cancellable);
825 
826   {
827     g_autoptr (GError) error = NULL;
828 
829     node = photos_base_item_load_finish (item, res, &error);
830     if (error != NULL)
831       {
832         g_autofree gchar *message = NULL;
833 
834         self->load_state = PHOTOS_LOAD_STATE_ERROR;
835 
836         if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
837           {
838             const gchar *domain_str;
839             const gchar *name;
840 
841             domain_str = g_quark_to_string (error->domain);
842             g_warning ("Unable to load item: (%s, %d) %s", domain_str, error->code, error->message);
843 
844             name = photos_base_item_get_name_with_fallback (item);
845             message = g_strdup_printf (_("Oops! Unable to load “%s”"), name);
846           }
847 
848         g_signal_emit (self, signals[LOAD_ERROR], 0, message, error);
849         goto out;
850       }
851   }
852 
853   self->load_state = PHOTOS_LOAD_STATE_FINISHED;
854   g_signal_emit (self, signals[LOAD_FINISHED], 0, item, node);
855 
856  out:
857   return;
858 }
859 
860 
861 static void
photos_item_manager_items_changed(PhotosItemManager * self,guint position,guint removed,guint added)862 photos_item_manager_items_changed (PhotosItemManager *self, guint position, guint removed, guint added)
863 {
864   g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added);
865 }
866 
867 
868 static void
photos_item_manager_remove_object_by_id(PhotosBaseManager * mngr,const gchar * id)869 photos_item_manager_remove_object_by_id (PhotosBaseManager *mngr, const gchar *id)
870 {
871   PhotosItemManager *self = PHOTOS_ITEM_MANAGER (mngr);
872   g_autoptr (PhotosBaseItem) item = NULL;
873   guint i;
874 
875   g_hash_table_remove (self->collections, id);
876 
877   item = PHOTOS_BASE_ITEM (photos_base_manager_get_object_by_id (PHOTOS_BASE_MANAGER (self), id));
878   if (item == NULL)
879     return;
880 
881   g_signal_handlers_disconnect_by_func (item, photos_item_manager_info_updated, self);
882   g_object_ref (item);
883 
884   for (i = 0; self->item_mngr_chldrn[i] != NULL; i++)
885     photos_base_manager_remove_object_by_id (self->item_mngr_chldrn[i], id);
886 
887   g_signal_emit_by_name (self, "object-removed", G_OBJECT (item));
888 }
889 
890 
891 static void
photos_item_manager_update_fullscreen(PhotosItemManager * self)892 photos_item_manager_update_fullscreen (PhotosItemManager *self)
893 {
894   /* Should be called after priv->mode has been updated. */
895 
896   if (!photos_mode_controller_get_can_fullscreen (self) && self->fullscreen)
897     photos_mode_controller_set_fullscreen (self, FALSE);
898 
899   g_signal_emit (self, signals[CAN_FULLSCREEN_CHANGED], 0);
900 }
901 
902 
903 static gboolean
photos_item_manager_set_window_mode_internal(PhotosItemManager * self,PhotosWindowMode mode,PhotosWindowMode * out_old_mode)904 photos_item_manager_set_window_mode_internal (PhotosItemManager *self,
905                                               PhotosWindowMode mode,
906                                               PhotosWindowMode *out_old_mode)
907 {
908   PhotosWindowMode old_mode;
909   gboolean ret_val = FALSE;
910 
911   old_mode = self->mode;
912 
913   if (old_mode == mode)
914     goto out;
915 
916   g_queue_push_head (self->history, GINT_TO_POINTER (old_mode));
917   self->mode = mode;
918 
919   if (out_old_mode != NULL)
920     *out_old_mode = old_mode;
921 
922   ret_val = TRUE;
923 
924  out:
925   return ret_val;
926 }
927 
928 
929 static gboolean
photos_item_manager_set_active_object(PhotosBaseManager * manager,GObject * object)930 photos_item_manager_set_active_object (PhotosBaseManager *manager, GObject *object)
931 {
932   PhotosItemManager *self = PHOTOS_ITEM_MANAGER (manager);
933   PhotosWindowMode old_mode;
934   gboolean active_collection_changed = FALSE;
935   gboolean is_collection = FALSE;
936   gboolean ret_val = FALSE;
937   gboolean start_loading = FALSE;
938   gboolean window_mode_changed = FALSE;
939 
940   g_return_val_if_fail (object != NULL, FALSE);
941   g_return_val_if_fail (PHOTOS_IS_BASE_ITEM (object), FALSE);
942   g_return_val_if_fail (self->mode != PHOTOS_WINDOW_MODE_EDIT, FALSE);
943   g_return_val_if_fail (self->mode != PHOTOS_WINDOW_MODE_IMPORT, FALSE);
944 
945   is_collection = photos_base_item_is_collection (PHOTOS_BASE_ITEM (object));
946   if (is_collection)
947     g_return_val_if_fail (self->active_collection == NULL, FALSE);
948 
949   if (object == self->active_object)
950     goto out;
951 
952   photos_item_manager_clear_active_item_load (self);
953 
954   if (is_collection)
955     {
956       window_mode_changed = photos_item_manager_set_window_mode_internal (self,
957                                                                           PHOTOS_WINDOW_MODE_COLLECTION_VIEW,
958                                                                           &old_mode);
959       g_assert_true (window_mode_changed);
960 
961       g_assert_null (self->active_collection);
962       self->active_collection = g_object_ref (PHOTOS_BASE_ITEM (object));
963       self->load_state = PHOTOS_LOAD_STATE_NONE;
964       active_collection_changed = TRUE;
965     }
966   else
967     {
968       window_mode_changed = photos_item_manager_set_window_mode_internal (self,
969                                                                           PHOTOS_WINDOW_MODE_PREVIEW,
970                                                                           &old_mode);
971       photos_item_manager_update_fullscreen (self);
972       self->load_state = PHOTOS_LOAD_STATE_STARTED;
973       start_loading = TRUE;
974     }
975 
976   g_set_object (&self->active_object, object);
977   g_signal_emit_by_name (self, "active-changed", self->active_object);
978   /* We have already eliminated the possibility of failure. */
979   ret_val = TRUE;
980 
981   if (active_collection_changed)
982     {
983       g_signal_emit (self, signals[ACTIVE_COLLECTION_CHANGED], 0, self->active_collection);
984       g_assert (self->active_object == (GObject *) self->active_collection);
985     }
986 
987   if (start_loading)
988     {
989       GtkRecentManager *recent;
990       const gchar *uri;
991 
992       recent = gtk_recent_manager_get_default ();
993       uri = photos_base_item_get_uri (PHOTOS_BASE_ITEM (object));
994       gtk_recent_manager_add_item (recent, uri);
995 
996       self->loader_cancellable = g_cancellable_new ();
997       photos_base_item_load_async (PHOTOS_BASE_ITEM (object),
998                                    self->loader_cancellable,
999                                    photos_item_manager_item_load,
1000                                    g_object_ref (self));
1001 
1002       g_signal_emit (self, signals[LOAD_STARTED], 0, PHOTOS_BASE_ITEM (object));
1003 
1004       g_assert (self->active_object != (GObject *) self->active_collection);
1005     }
1006 
1007   if (window_mode_changed)
1008     g_signal_emit (self, signals[WINDOW_MODE_CHANGED], 0, self->mode, old_mode);
1009 
1010  out:
1011   return ret_val;
1012 }
1013 
1014 
1015 static gint
photos_item_manager_sort_func(gconstpointer a,gconstpointer b,gpointer user_data)1016 photos_item_manager_sort_func (gconstpointer a, gconstpointer b, gpointer user_data)
1017 {
1018   PhotosBaseItem *item_a = PHOTOS_BASE_ITEM ((gpointer) a);
1019   PhotosBaseItem *item_b = PHOTOS_BASE_ITEM ((gpointer) b);
1020   gint ret_val;
1021   gint64 mtime_a;
1022   gint64 mtime_b;
1023 
1024   mtime_a = photos_base_item_get_mtime (item_a);
1025   mtime_b = photos_base_item_get_mtime (item_b);
1026 
1027   if (mtime_a > mtime_b)
1028     ret_val = -1;
1029   else if (mtime_a == mtime_b)
1030     ret_val = 0;
1031   else
1032     ret_val = 1;
1033 
1034   return ret_val;
1035 }
1036 
1037 
1038 static void
photos_item_manager_dispose(GObject * object)1039 photos_item_manager_dispose (GObject *object)
1040 {
1041   PhotosItemManager *self = PHOTOS_ITEM_MANAGER (object);
1042 
1043   photos_item_manager_remove_timeout (self);
1044 
1045   if (self->item_mngr_chldrn != NULL)
1046     {
1047       guint i;
1048 
1049       for (i = 0; self->item_mngr_chldrn[i] != NULL; i++)
1050         g_object_unref (self->item_mngr_chldrn[i]);
1051 
1052       g_free (self->item_mngr_chldrn);
1053       self->item_mngr_chldrn = NULL;
1054     }
1055 
1056   g_clear_pointer (&self->collections, g_hash_table_unref);
1057   g_clear_pointer (&self->hidden_items, g_hash_table_unref);
1058   g_clear_pointer (&self->wait_for_changes_table, g_hash_table_unref);
1059   g_clear_object (&self->active_object);
1060   g_clear_object (&self->loader_cancellable);
1061   g_clear_object (&self->active_collection);
1062   g_clear_object (&self->queue);
1063   g_clear_object (&self->notifier);
1064 
1065   G_OBJECT_CLASS (photos_item_manager_parent_class)->dispose (object);
1066 }
1067 
1068 
1069 static void
photos_item_manager_finalize(GObject * object)1070 photos_item_manager_finalize (GObject *object)
1071 {
1072   PhotosItemManager *self = PHOTOS_ITEM_MANAGER (object);
1073 
1074   g_queue_free (self->history);
1075   g_free (self->constrain_additions);
1076 
1077   G_OBJECT_CLASS (photos_item_manager_parent_class)->finalize (object);
1078 
1079   DZL_COUNTER_DEC (instances);
1080 }
1081 
1082 
1083 static void
photos_item_manager_init(PhotosItemManager * self)1084 photos_item_manager_init (PhotosItemManager *self)
1085 {
1086   GEnumClass *window_mode_class; /* TODO: use g_autoptr */
1087   guint i;
1088 
1089   DZL_COUNTER_INC (instances);
1090 
1091   self->collections = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
1092   self->hidden_items = g_hash_table_new_full (g_str_hash,
1093                                               g_str_equal,
1094                                               g_free,
1095                                               (GDestroyNotify) photos_item_manager_hidden_item_free);
1096   self->wait_for_changes_table = g_hash_table_new_full (g_str_hash,
1097                                                         g_str_equal,
1098                                                         g_free,
1099                                                         (GDestroyNotify) photos_utils_object_list_free_full);
1100   self->extension_point = g_io_extension_point_lookup (PHOTOS_BASE_ITEM_EXTENSION_POINT_NAME);
1101   self->history = g_queue_new ();
1102 
1103   window_mode_class = G_ENUM_CLASS (g_type_class_ref (PHOTOS_TYPE_WINDOW_MODE));
1104 
1105   self->item_mngr_chldrn = (PhotosBaseManager **) g_malloc0_n (window_mode_class->n_values + 1,
1106                                                                sizeof (PhotosBaseManager *));
1107   for (i = 0; i < window_mode_class->n_values; i++)
1108     self->item_mngr_chldrn[i] = photos_base_manager_new (photos_item_manager_sort_func, NULL);
1109 
1110   g_signal_connect_swapped (self->item_mngr_chldrn[0],
1111                             "items-changed",
1112                             G_CALLBACK (photos_item_manager_items_changed),
1113                             self);
1114 
1115   self->mode = PHOTOS_WINDOW_MODE_NONE;
1116 
1117   {
1118     g_autoptr (GError) error = NULL;
1119 
1120     self->queue = photos_tracker_queue_dup_singleton (NULL, &error);
1121     if (G_UNLIKELY (error != NULL))
1122       g_warning ("Unable to create PhotosTrackerQueue: %s", error->message);
1123   }
1124 
1125   self->fullscreen = FALSE;
1126   self->constrain_additions = (gboolean *) g_malloc0_n (window_mode_class->n_values, sizeof (gboolean));
1127 
1128   g_type_class_unref (window_mode_class);
1129 }
1130 
1131 
1132 static void
photos_item_manager_class_init(PhotosItemManagerClass * class)1133 photos_item_manager_class_init (PhotosItemManagerClass *class)
1134 {
1135   GObjectClass *object_class = G_OBJECT_CLASS (class);
1136   PhotosBaseManagerClass *base_manager_class = PHOTOS_BASE_MANAGER_CLASS (class);
1137 
1138   object_class->dispose = photos_item_manager_dispose;
1139   object_class->finalize = photos_item_manager_finalize;
1140   base_manager_class->add_object = photos_item_manager_add_object;
1141   base_manager_class->get_active_object = photos_item_manager_get_active_object;
1142   base_manager_class->get_where = photos_item_manager_get_where;
1143   base_manager_class->get_object_by_id = photos_item_manager_get_object_by_id;
1144   base_manager_class->set_active_object = photos_item_manager_set_active_object;
1145   base_manager_class->remove_object_by_id = photos_item_manager_remove_object_by_id;
1146 
1147   signals[ACTIVE_COLLECTION_CHANGED] = g_signal_new ("active-collection-changed",
1148                                                      G_TYPE_FROM_CLASS (class),
1149                                                      G_SIGNAL_RUN_LAST,
1150                                                      0,
1151                                                      NULL, /*accumulator */
1152                                                      NULL, /*accu_data */
1153                                                      g_cclosure_marshal_VOID__OBJECT,
1154                                                      G_TYPE_NONE,
1155                                                      1,
1156                                                      PHOTOS_TYPE_BASE_ITEM);
1157 
1158   signals[CAN_FULLSCREEN_CHANGED] = g_signal_new ("can-fullscreen-changed",
1159                                                   G_TYPE_FROM_CLASS (class),
1160                                                   G_SIGNAL_RUN_LAST,
1161                                                   0,
1162                                                   NULL, /*accumulator */
1163                                                   NULL, /*accu_data */
1164                                                   g_cclosure_marshal_VOID__VOID,
1165                                                   G_TYPE_NONE,
1166                                                   0);
1167 
1168   signals[FULLSCREEN_CHANGED] = g_signal_new ("fullscreen-changed",
1169                                               G_TYPE_FROM_CLASS (class),
1170                                               G_SIGNAL_RUN_LAST,
1171                                               0,
1172                                               NULL, /*accumulator */
1173                                               NULL, /* accu_data */
1174                                               g_cclosure_marshal_VOID__BOOLEAN,
1175                                               G_TYPE_NONE,
1176                                               1,
1177                                               G_TYPE_BOOLEAN);
1178 
1179   signals[LOAD_ERROR] = g_signal_new ("load-error",
1180                                       G_TYPE_FROM_CLASS (class),
1181                                       G_SIGNAL_RUN_LAST,
1182                                       0,
1183                                       NULL, /*accumulator */
1184                                       NULL, /*accu_data */
1185                                       _photos_marshal_VOID__STRING_ENUM,
1186                                       G_TYPE_NONE,
1187                                       2,
1188                                       G_TYPE_STRING,
1189                                       G_TYPE_ERROR);
1190 
1191   signals[LOAD_FINISHED] = g_signal_new ("load-finished",
1192                                          G_TYPE_FROM_CLASS (class),
1193                                          G_SIGNAL_RUN_LAST,
1194                                          0,
1195                                          NULL, /*accumulator */
1196                                          NULL, /*accu_data */
1197                                          g_cclosure_marshal_generic,
1198                                          G_TYPE_NONE,
1199                                          2,
1200                                          PHOTOS_TYPE_BASE_ITEM,
1201                                          GEGL_TYPE_NODE);
1202 
1203   signals[LOAD_STARTED] = g_signal_new ("load-started",
1204                                         G_TYPE_FROM_CLASS (class),
1205                                         G_SIGNAL_RUN_LAST,
1206                                         0,
1207                                         NULL, /*accumulator */
1208                                         NULL, /*accu_data */
1209                                         g_cclosure_marshal_VOID__OBJECT,
1210                                         G_TYPE_NONE,
1211                                         1,
1212                                         PHOTOS_TYPE_BASE_ITEM);
1213 
1214   signals[WINDOW_MODE_CHANGED] = g_signal_new ("window-mode-changed",
1215                                                G_TYPE_FROM_CLASS (class),
1216                                                G_SIGNAL_RUN_LAST,
1217                                                0,
1218                                                NULL, /*accumulator */
1219                                                NULL, /*accu_data */
1220                                                _photos_marshal_VOID__ENUM_ENUM,
1221                                                G_TYPE_NONE,
1222                                                2,
1223                                                PHOTOS_TYPE_WINDOW_MODE,
1224                                                PHOTOS_TYPE_WINDOW_MODE);
1225 }
1226 
1227 
1228 static void
photos_item_manager_list_model_iface_init(GListModelInterface * iface)1229 photos_item_manager_list_model_iface_init (GListModelInterface *iface)
1230 {
1231   iface->get_item = photos_item_manager_get_item;
1232   iface->get_item_type = photos_item_manager_get_item_type;
1233   iface->get_n_items = photos_item_manager_get_n_items;
1234 }
1235 
1236 
1237 PhotosBaseManager *
photos_item_manager_new(void)1238 photos_item_manager_new (void)
1239 {
1240   return g_object_new (PHOTOS_TYPE_ITEM_MANAGER, NULL);
1241 }
1242 
1243 
1244 void
photos_item_manager_add_item(PhotosItemManager * self,GType base_item_type,TrackerSparqlCursor * cursor,gboolean force)1245 photos_item_manager_add_item (PhotosItemManager *self,
1246                               GType base_item_type,
1247                               TrackerSparqlCursor *cursor,
1248                               gboolean force)
1249 {
1250   PhotosItemManagerHiddenItem *old_hidden_item;
1251   const gchar *id;
1252 
1253   g_return_if_fail (PHOTOS_IS_ITEM_MANAGER (self));
1254   g_return_if_fail (base_item_type == G_TYPE_NONE
1255                     || (base_item_type != PHOTOS_TYPE_BASE_ITEM
1256                         && g_type_is_a (base_item_type, PHOTOS_TYPE_BASE_ITEM)));
1257   g_return_if_fail (TRACKER_IS_SPARQL_CURSOR (cursor));
1258 
1259   id = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_URN, NULL);
1260   g_return_if_fail (id != NULL && id[0] != '\0');
1261 
1262   old_hidden_item = (PhotosItemManagerHiddenItem *) g_hash_table_lookup (self->hidden_items, id);
1263   if (old_hidden_item != NULL)
1264     goto out;
1265 
1266   if (photos_item_manager_cursor_is_collection (cursor))
1267     {
1268       photos_item_manager_add_cursor_for_mode (self,
1269                                                base_item_type,
1270                                                cursor,
1271                                                PHOTOS_WINDOW_MODE_COLLECTIONS,
1272                                                force);
1273     }
1274   else
1275     {
1276       if (photos_item_manager_cursor_is_favorite (cursor))
1277         {
1278           photos_item_manager_add_cursor_for_mode (self,
1279                                                    base_item_type,
1280                                                    cursor,
1281                                                    PHOTOS_WINDOW_MODE_FAVORITES,
1282                                                    force);
1283         }
1284 
1285       photos_item_manager_add_cursor_for_mode (self,
1286                                                base_item_type,
1287                                                cursor,
1288                                                PHOTOS_WINDOW_MODE_OVERVIEW,
1289                                                force);
1290     }
1291 
1292  out:
1293   return;
1294 }
1295 
1296 
1297 void
photos_item_manager_add_item_for_mode(PhotosItemManager * self,GType base_item_type,PhotosWindowMode mode,TrackerSparqlCursor * cursor)1298 photos_item_manager_add_item_for_mode (PhotosItemManager *self,
1299                                        GType base_item_type,
1300                                        PhotosWindowMode mode,
1301                                        TrackerSparqlCursor *cursor)
1302 {
1303   PhotosItemManagerHiddenItem *old_hidden_item;
1304   const gchar *id;
1305 
1306   g_return_if_fail (base_item_type == G_TYPE_NONE
1307                     || (base_item_type != PHOTOS_TYPE_BASE_ITEM
1308                         && g_type_is_a (base_item_type, PHOTOS_TYPE_BASE_ITEM)));
1309 
1310   id = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_URN, NULL);
1311   g_return_if_fail (id != NULL && id[0] != '\0');
1312 
1313   old_hidden_item = (PhotosItemManagerHiddenItem *) g_hash_table_lookup (self->hidden_items, id);
1314   if (old_hidden_item != NULL)
1315     goto out;
1316 
1317   photos_item_manager_add_cursor_for_mode (self, base_item_type, cursor, mode, FALSE);
1318 
1319  out:
1320   return;
1321 }
1322 
1323 
1324 void
photos_item_manager_clear(PhotosItemManager * self,PhotosWindowMode mode)1325 photos_item_manager_clear (PhotosItemManager *self, PhotosWindowMode mode)
1326 {
1327   PhotosBaseManager *item_mngr_chld;
1328   guint i;
1329   guint n_items;
1330 
1331   g_return_if_fail (PHOTOS_IS_ITEM_MANAGER (self));
1332   g_return_if_fail (mode != PHOTOS_WINDOW_MODE_NONE);
1333   g_return_if_fail (mode != PHOTOS_WINDOW_MODE_EDIT);
1334   g_return_if_fail (mode != PHOTOS_WINDOW_MODE_PREVIEW);
1335 
1336   item_mngr_chld = self->item_mngr_chldrn[mode];
1337   n_items = g_list_model_get_n_items (G_LIST_MODEL (item_mngr_chld));
1338   for (i = 0; i < n_items; i++)
1339     {
1340       g_autoptr (PhotosBaseItem) item = NULL;
1341       PhotosBaseItem *item1 = NULL;
1342       const gchar *id;
1343       guint j;
1344 
1345       item = PHOTOS_BASE_ITEM (g_list_model_get_object (G_LIST_MODEL (item_mngr_chld), i));
1346       id = photos_filterable_get_id (PHOTOS_FILTERABLE (item));
1347 
1348       for (j = 1; self->item_mngr_chldrn[j] != NULL; j++)
1349         {
1350           if (item_mngr_chld == self->item_mngr_chldrn[j])
1351             continue;
1352 
1353           item1 = PHOTOS_BASE_ITEM (photos_base_manager_get_object_by_id (self->item_mngr_chldrn[j], id));
1354           if (item1 != NULL)
1355             break;
1356         }
1357 
1358       if (item1 == NULL)
1359         {
1360           item1 = PHOTOS_BASE_ITEM (photos_base_manager_get_object_by_id (self->item_mngr_chldrn[0], id));
1361           g_assert_true (item == item1);
1362 
1363           g_signal_handlers_disconnect_by_func (item, photos_item_manager_info_updated, self);
1364           photos_base_manager_remove_object_by_id (self->item_mngr_chldrn[0], id);
1365         }
1366     }
1367 
1368   photos_base_manager_clear (item_mngr_chld);
1369 }
1370 
1371 
1372 PhotosBaseItem *
photos_item_manager_create_item(PhotosItemManager * self,GType base_item_type,TrackerSparqlCursor * cursor,gboolean create_thumbnails)1373 photos_item_manager_create_item (PhotosItemManager *self,
1374                                  GType base_item_type,
1375                                  TrackerSparqlCursor *cursor,
1376                                  gboolean create_thumbnails)
1377 {
1378   PhotosBaseItem *item;
1379   PhotosBaseItem *ret_val = NULL;
1380   const gchar *id;
1381 
1382   g_return_val_if_fail (PHOTOS_IS_ITEM_MANAGER (self), NULL);
1383   g_return_val_if_fail (base_item_type == G_TYPE_NONE
1384                         || (base_item_type != PHOTOS_TYPE_BASE_ITEM
1385                             && g_type_is_a (base_item_type, PHOTOS_TYPE_BASE_ITEM)), NULL);
1386   g_return_val_if_fail (TRACKER_IS_SPARQL_CURSOR (cursor), NULL);
1387 
1388   id = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_URN, NULL);
1389   item = PHOTOS_BASE_ITEM (photos_base_manager_get_object_by_id (PHOTOS_BASE_MANAGER (self), id));
1390   if (item != NULL)
1391     {
1392       ret_val = g_object_ref (item);
1393     }
1394   else
1395     {
1396       GType type;
1397 
1398       if (base_item_type == G_TYPE_NONE)
1399         {
1400           GIOExtension *extension;
1401           g_auto (GStrv) split_identifier = NULL;
1402           const gchar *extension_name = "local";
1403           g_autofree gchar *identifier = NULL;
1404 
1405           identifier = g_strdup (tracker_sparql_cursor_get_string (cursor,
1406                                                                    PHOTOS_QUERY_COLUMNS_IDENTIFIER,
1407                                                                    NULL));
1408           if (identifier != NULL)
1409             {
1410               split_identifier = g_strsplit (identifier, ":", 4);
1411 
1412               if (photos_item_manager_cursor_is_collection (cursor))
1413                 {
1414                   /* Its a collection. */
1415                   extension_name = split_identifier[2];
1416                 }
1417               else
1418                 {
1419                   /* Its a normal photo item. */
1420                   if (g_strv_length (split_identifier) > 1)
1421                     extension_name = split_identifier[0];
1422                 }
1423             }
1424 
1425           extension = g_io_extension_point_get_extension_by_name (self->extension_point, extension_name);
1426           if (G_UNLIKELY (extension == NULL))
1427             {
1428               g_warning ("Unable to find extension %s for identifier: %s", extension_name, identifier);
1429               goto out;
1430             }
1431 
1432           type = g_io_extension_get_type (extension);
1433         }
1434       else
1435         {
1436           type = base_item_type;
1437         }
1438 
1439       g_return_val_if_fail (type != G_TYPE_NONE, NULL);
1440       g_return_val_if_fail (type != PHOTOS_TYPE_BASE_ITEM && g_type_is_a (type, PHOTOS_TYPE_BASE_ITEM), NULL);
1441 
1442       ret_val = PHOTOS_BASE_ITEM (g_object_new (type,
1443                                                 "cursor", cursor,
1444                                                 "failed-thumbnailing", !create_thumbnails,
1445                                                 NULL));
1446     }
1447 
1448  out:
1449   return ret_val;
1450 }
1451 
1452 
1453 PhotosBaseItem *
photos_item_manager_get_active_collection(PhotosItemManager * self)1454 photos_item_manager_get_active_collection (PhotosItemManager *self)
1455 {
1456   g_return_val_if_fail (PHOTOS_IS_ITEM_MANAGER (self), NULL);
1457   return self->active_collection;
1458 }
1459 
1460 
1461 GHashTable *
photos_item_manager_get_collections(PhotosItemManager * self)1462 photos_item_manager_get_collections (PhotosItemManager *self)
1463 {
1464   g_return_val_if_fail (PHOTOS_IS_ITEM_MANAGER (self), NULL);
1465   return self->collections;
1466 }
1467 
1468 
1469 PhotosBaseManager *
photos_item_manager_get_for_mode(PhotosItemManager * self,PhotosWindowMode mode)1470 photos_item_manager_get_for_mode (PhotosItemManager *self, PhotosWindowMode mode)
1471 {
1472   g_return_val_if_fail (PHOTOS_IS_ITEM_MANAGER (self), NULL);
1473   g_return_val_if_fail (mode != PHOTOS_WINDOW_MODE_NONE, NULL);
1474   g_return_val_if_fail (mode != PHOTOS_WINDOW_MODE_EDIT, NULL);
1475   g_return_val_if_fail (mode != PHOTOS_WINDOW_MODE_PREVIEW, NULL);
1476 
1477   return self->item_mngr_chldrn[mode];
1478 }
1479 
1480 
1481 PhotosLoadState
photos_item_manager_get_load_state(PhotosItemManager * self)1482 photos_item_manager_get_load_state (PhotosItemManager *self)
1483 {
1484   g_return_val_if_fail (PHOTOS_IS_ITEM_MANAGER (self), PHOTOS_LOAD_STATE_NONE);
1485   return self->load_state;
1486 }
1487 
1488 
1489 void
photos_item_manager_hide_item(PhotosItemManager * self,PhotosBaseItem * item)1490 photos_item_manager_hide_item (PhotosItemManager *self, PhotosBaseItem *item)
1491 {
1492   PhotosItemManagerHiddenItem *hidden_item;
1493   PhotosItemManagerHiddenItem *old_hidden_item;
1494   const gchar *id;
1495   guint i;
1496 
1497   g_return_if_fail (PHOTOS_IS_ITEM_MANAGER (self));
1498   g_return_if_fail (PHOTOS_IS_BASE_ITEM (item));
1499 
1500   id = photos_filterable_get_id (PHOTOS_FILTERABLE (item));
1501   g_return_if_fail (id != NULL && id[0] != '\0');
1502 
1503   old_hidden_item = (PhotosItemManagerHiddenItem *) g_hash_table_lookup (self->hidden_items, id);
1504   g_return_if_fail (old_hidden_item == NULL);
1505 
1506   hidden_item = photos_item_manager_hidden_item_new (item);
1507   for (i = 0; self->item_mngr_chldrn[i] != NULL; i++)
1508     {
1509       PhotosBaseItem *item1;
1510 
1511       g_assert_cmpuint (i, <, hidden_item->n_modes);
1512 
1513       item1 = PHOTOS_BASE_ITEM (photos_base_manager_get_object_by_id (self->item_mngr_chldrn[i], id));
1514       if (item1 != NULL)
1515         {
1516           g_assert_true (item == item1);
1517           hidden_item->modes[i] = TRUE;
1518         }
1519     }
1520 
1521   g_hash_table_insert (self->hidden_items, g_strdup (id), hidden_item);
1522   photos_base_manager_remove_object_by_id (PHOTOS_BASE_MANAGER (self), id);
1523 }
1524 
1525 
1526 void
photos_item_manager_set_constraints_for_mode(PhotosItemManager * self,gboolean constrain,PhotosWindowMode mode)1527 photos_item_manager_set_constraints_for_mode (PhotosItemManager *self, gboolean constrain, PhotosWindowMode mode)
1528 {
1529   g_return_if_fail (PHOTOS_IS_ITEM_MANAGER (self));
1530   g_return_if_fail (mode != PHOTOS_WINDOW_MODE_NONE);
1531   g_return_if_fail (mode != PHOTOS_WINDOW_MODE_EDIT);
1532   g_return_if_fail (mode != PHOTOS_WINDOW_MODE_PREVIEW);
1533 
1534   self->constrain_additions[mode] = constrain;
1535 }
1536 
1537 
1538 void
photos_item_manager_set_notifier(PhotosItemManager * self,TrackerNotifier * notifier)1539 photos_item_manager_set_notifier (PhotosItemManager *self, TrackerNotifier *notifier)
1540 {
1541   g_return_if_fail (PHOTOS_IS_ITEM_MANAGER (self));
1542   g_return_if_fail (notifier == NULL || TRACKER_IS_NOTIFIER (notifier));
1543 
1544   if (self->notifier != NULL)
1545     {
1546       g_signal_handlers_disconnect_by_func (self->notifier, photos_item_manager_notifier_events, self);
1547       g_clear_object (&self->notifier);
1548     }
1549 
1550   if (notifier != NULL)
1551     {
1552       self->notifier = g_object_ref (notifier);
1553       g_signal_connect_swapped (self->notifier, "events", G_CALLBACK (photos_item_manager_notifier_events), self);
1554     }
1555 }
1556 
1557 
1558 void
photos_item_manager_unhide_item(PhotosItemManager * self,PhotosBaseItem * item)1559 photos_item_manager_unhide_item (PhotosItemManager *self, PhotosBaseItem *item)
1560 {
1561   PhotosItemManagerHiddenItem *hidden_item;
1562   gboolean added_somewhere = FALSE;
1563   const gchar *id;
1564   guint i;
1565 
1566   g_return_if_fail (PHOTOS_IS_ITEM_MANAGER (self));
1567   g_return_if_fail (PHOTOS_IS_BASE_ITEM (item));
1568 
1569   id = photos_filterable_get_id (PHOTOS_FILTERABLE (item));
1570   g_return_if_fail (id != NULL && id[0] != '\0');
1571 
1572   hidden_item = (PhotosItemManagerHiddenItem *) g_hash_table_lookup (self->hidden_items, id);
1573   g_return_if_fail (hidden_item->item == item);
1574 
1575   for (i = 1; self->item_mngr_chldrn[i] != NULL; i++)
1576     {
1577       g_assert_cmpuint (i, <, hidden_item->n_modes);
1578 
1579       if (hidden_item->modes[i])
1580         added_somewhere = added_somewhere || photos_item_manager_try_to_add_item_for_mode (self, item, i);
1581     }
1582 
1583   g_hash_table_remove (self->hidden_items, id);
1584 
1585   if (added_somewhere)
1586     {
1587       photos_base_manager_add_object (self->item_mngr_chldrn[0], G_OBJECT (item));
1588       g_signal_connect_object (item, "info-updated", G_CALLBACK (photos_item_manager_info_updated), self, 0);
1589       g_signal_emit_by_name (self, "object-added", G_OBJECT (item));
1590     }
1591 }
1592 
1593 
1594 void
photos_item_manager_wait_for_file_async(PhotosItemManager * self,GFile * file,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1595 photos_item_manager_wait_for_file_async (PhotosItemManager *self,
1596                                          GFile *file,
1597                                          GCancellable *cancellable,
1598                                          GAsyncReadyCallback callback,
1599                                          gpointer user_data)
1600 {
1601   GList *tasks;
1602   g_autoptr (GTask) task = NULL;
1603   g_autofree gchar *uri = NULL;
1604 
1605   g_return_if_fail (PHOTOS_IS_ITEM_MANAGER (self));
1606   g_return_if_fail (G_IS_FILE (file));
1607   g_return_if_fail (g_file_is_native (file));
1608   g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
1609 
1610   task = g_task_new (self, cancellable, callback, user_data);
1611   g_task_set_source_tag (task, photos_item_manager_wait_for_file_async);
1612 
1613   uri = g_file_get_uri (file);
1614   tasks = (GList *) g_hash_table_lookup (self->wait_for_changes_table, uri);
1615   tasks = g_list_copy_deep (tasks, (GCopyFunc) g_object_ref, NULL);
1616   tasks = g_list_prepend (tasks, g_object_ref (task));
1617   g_hash_table_insert (self->wait_for_changes_table, g_strdup (uri), tasks);
1618 
1619   photos_item_manager_remove_timeout (self);
1620   self->wait_for_changes_id = g_timeout_add_seconds (WAIT_FOR_CHANGES_TIMEOUT,
1621                                                      photos_item_manager_wait_for_changes_timeout,
1622                                                      self);
1623 
1624   photos_debug (PHOTOS_DEBUG_TRACKER, "Waiting for %s", uri);
1625 }
1626 
1627 
1628 gchar *
photos_item_manager_wait_for_file_finish(PhotosItemManager * self,GAsyncResult * res,GError ** error)1629 photos_item_manager_wait_for_file_finish (PhotosItemManager *self, GAsyncResult *res, GError **error)
1630 {
1631   GTask *task;
1632 
1633   g_return_val_if_fail (PHOTOS_IS_ITEM_MANAGER (self), NULL);
1634 
1635   g_return_val_if_fail (g_task_is_valid (res, self), NULL);
1636   task = G_TASK (res);
1637 
1638   g_return_val_if_fail (g_task_get_source_tag (task) == photos_item_manager_wait_for_file_async, NULL);
1639   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1640 
1641   return g_task_propagate_pointer (task, error);
1642 }
1643 
1644 
1645 void
photos_item_manager_wait_for_changes_async(PhotosItemManager * self,PhotosBaseItem * item,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1646 photos_item_manager_wait_for_changes_async (PhotosItemManager *self,
1647                                             PhotosBaseItem *item,
1648                                             GCancellable *cancellable,
1649                                             GAsyncReadyCallback callback,
1650                                             gpointer user_data)
1651 {
1652   GList *tasks;
1653   g_autoptr (GTask) task = NULL;
1654   const gchar *uri;
1655 
1656   g_return_if_fail (PHOTOS_IS_ITEM_MANAGER (self));
1657   g_return_if_fail (PHOTOS_IS_BASE_ITEM (item));
1658   g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
1659 
1660   task = g_task_new (self, cancellable, callback, user_data);
1661   g_task_set_source_tag (task, photos_item_manager_wait_for_changes_async);
1662 
1663   if (!PHOTOS_IS_LOCAL_ITEM (item) || photos_base_item_is_collection (item))
1664     {
1665       const gchar *id;
1666 
1667       id = photos_filterable_get_id (PHOTOS_FILTERABLE (item));
1668       g_task_return_pointer (task, g_strdup (id), g_free);
1669       goto out;
1670     }
1671 
1672   uri = photos_base_item_get_uri (item);
1673   tasks = (GList *) g_hash_table_lookup (self->wait_for_changes_table, uri);
1674   tasks = g_list_copy_deep (tasks, (GCopyFunc) g_object_ref, NULL);
1675   tasks = g_list_prepend (tasks, g_object_ref (task));
1676   g_hash_table_insert (self->wait_for_changes_table, g_strdup (uri), tasks);
1677 
1678   photos_item_manager_remove_timeout (self);
1679   self->wait_for_changes_id = g_timeout_add_seconds (WAIT_FOR_CHANGES_TIMEOUT,
1680                                                      photos_item_manager_wait_for_changes_timeout,
1681                                                      self);
1682 
1683   photos_debug (PHOTOS_DEBUG_TRACKER, "Waiting for %s", uri);
1684 
1685  out:
1686   return;
1687 }
1688 
1689 
1690 gchar *
photos_item_manager_wait_for_changes_finish(PhotosItemManager * self,GAsyncResult * res,GError ** error)1691 photos_item_manager_wait_for_changes_finish (PhotosItemManager *self, GAsyncResult *res, GError **error)
1692 {
1693   GTask *task = G_TASK (res);
1694 
1695   g_return_val_if_fail (PHOTOS_IS_ITEM_MANAGER (self), NULL);
1696   g_return_val_if_fail (g_task_is_valid (res, self), NULL);
1697   g_return_val_if_fail (g_task_get_source_tag (task) == photos_item_manager_wait_for_changes_async, NULL);
1698   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1699 
1700   return g_task_propagate_pointer (task, error);
1701 }
1702 
1703 
1704 gboolean
photos_mode_controller_get_can_fullscreen(PhotosModeController * self)1705 photos_mode_controller_get_can_fullscreen (PhotosModeController *self)
1706 {
1707   g_return_val_if_fail (PHOTOS_IS_MODE_CONTROLLER (self), FALSE);
1708   return self->mode == PHOTOS_WINDOW_MODE_PREVIEW;
1709 }
1710 
1711 
1712 gboolean
photos_mode_controller_get_fullscreen(PhotosModeController * self)1713 photos_mode_controller_get_fullscreen (PhotosModeController *self)
1714 {
1715   g_return_val_if_fail (PHOTOS_IS_MODE_CONTROLLER (self), FALSE);
1716   return self->fullscreen;
1717 }
1718 
1719 
1720 PhotosWindowMode
photos_mode_controller_get_window_mode(PhotosModeController * self)1721 photos_mode_controller_get_window_mode (PhotosModeController *self)
1722 {
1723   g_return_val_if_fail (PHOTOS_IS_MODE_CONTROLLER (self), PHOTOS_WINDOW_MODE_NONE);
1724   return self->mode;
1725 }
1726 
1727 
1728 void
photos_mode_controller_go_back(PhotosModeController * self)1729 photos_mode_controller_go_back (PhotosModeController *self)
1730 {
1731   GAction *selection_mode_action;
1732   GApplication *app;
1733   PhotosWindowMode old_mode;
1734   PhotosWindowMode tmp;
1735   gboolean active_changed = FALSE;
1736   gboolean active_collection_changed = FALSE;
1737   gboolean unset_selection_mode = FALSE;
1738 
1739   g_return_if_fail (PHOTOS_IS_MODE_CONTROLLER (self));
1740   g_return_if_fail (!g_queue_is_empty (self->history));
1741 
1742   app = g_application_get_default ();
1743   selection_mode_action = g_action_map_lookup_action (G_ACTION_MAP (app), "selection-mode");
1744 
1745   old_mode = (PhotosWindowMode) GPOINTER_TO_INT (g_queue_peek_head (self->history));
1746   g_return_if_fail (old_mode != PHOTOS_WINDOW_MODE_NONE);
1747 
1748   switch (self->mode)
1749     {
1750     case PHOTOS_WINDOW_MODE_EDIT:
1751       g_return_if_fail (self->load_state == PHOTOS_LOAD_STATE_FINISHED);
1752       g_return_if_fail (old_mode == PHOTOS_WINDOW_MODE_PREVIEW);
1753       break;
1754 
1755     case PHOTOS_WINDOW_MODE_COLLECTION_VIEW:
1756       g_return_if_fail (self->load_state == PHOTOS_LOAD_STATE_NONE);
1757       g_return_if_fail (PHOTOS_IS_BASE_ITEM (self->active_collection));
1758       g_return_if_fail (self->active_object == (GObject *) self->active_collection);
1759       g_return_if_fail (old_mode == PHOTOS_WINDOW_MODE_COLLECTIONS || old_mode == PHOTOS_WINDOW_MODE_SEARCH);
1760       break;
1761 
1762     case PHOTOS_WINDOW_MODE_COLLECTIONS:
1763     case PHOTOS_WINDOW_MODE_FAVORITES:
1764     case PHOTOS_WINDOW_MODE_OVERVIEW:
1765     case PHOTOS_WINDOW_MODE_SEARCH:
1766       g_return_if_fail (old_mode != PHOTOS_WINDOW_MODE_PREVIEW);
1767       break;
1768 
1769     case PHOTOS_WINDOW_MODE_IMPORT:
1770       {
1771         g_autoptr (GVariant) state = NULL;
1772 
1773         g_return_if_fail (old_mode == PHOTOS_WINDOW_MODE_COLLECTIONS
1774                           || old_mode == PHOTOS_WINDOW_MODE_FAVORITES
1775                           || old_mode == PHOTOS_WINDOW_MODE_OVERVIEW);
1776 
1777         state = g_action_get_state (selection_mode_action);
1778         g_return_if_fail (state != NULL);
1779         g_return_if_fail (g_variant_get_boolean (state));
1780         break;
1781       }
1782 
1783     case PHOTOS_WINDOW_MODE_PREVIEW:
1784       g_return_if_fail (PHOTOS_IS_BASE_ITEM (self->active_object));
1785       g_return_if_fail (self->active_object != (GObject *) self->active_collection);
1786       g_return_if_fail (old_mode != PHOTOS_WINDOW_MODE_PREVIEW);
1787       g_return_if_fail ((old_mode == PHOTOS_WINDOW_MODE_COLLECTION_VIEW && PHOTOS_IS_BASE_ITEM (self->active_collection))
1788                         || (old_mode != PHOTOS_WINDOW_MODE_COLLECTION_VIEW && self->active_collection == NULL));
1789       break;
1790 
1791     case PHOTOS_WINDOW_MODE_NONE:
1792     default:
1793       g_assert_not_reached ();
1794       break;
1795     }
1796 
1797   g_queue_pop_head (self->history);
1798 
1799   /* Swap the old and current modes */
1800   tmp = old_mode;
1801   old_mode = self->mode;
1802   self->mode = tmp;
1803 
1804   photos_item_manager_update_fullscreen (self);
1805   photos_item_manager_clear_active_item_load (self);
1806 
1807   switch (old_mode)
1808     {
1809     case PHOTOS_WINDOW_MODE_COLLECTION_VIEW:
1810       g_clear_object (&self->active_collection);
1811       g_clear_object (&self->active_object);
1812       active_changed = TRUE;
1813       active_collection_changed = TRUE;
1814       break;
1815 
1816     case PHOTOS_WINDOW_MODE_EDIT:
1817       break;
1818 
1819     case PHOTOS_WINDOW_MODE_IMPORT:
1820       unset_selection_mode = TRUE;
1821       break;
1822 
1823     case PHOTOS_WINDOW_MODE_PREVIEW:
1824       self->load_state = PHOTOS_LOAD_STATE_NONE;
1825       g_set_object (&self->active_object, G_OBJECT (self->active_collection));
1826       active_changed = TRUE;
1827       break;
1828 
1829     case PHOTOS_WINDOW_MODE_COLLECTIONS:
1830     case PHOTOS_WINDOW_MODE_FAVORITES:
1831     case PHOTOS_WINDOW_MODE_OVERVIEW:
1832     case PHOTOS_WINDOW_MODE_SEARCH:
1833       if (self->active_collection != NULL)
1834         {
1835           g_clear_object (&self->active_collection);
1836           active_collection_changed = TRUE;
1837         }
1838 
1839       g_clear_object (&self->active_object);
1840       active_changed = TRUE;
1841 
1842       self->load_state = PHOTOS_LOAD_STATE_NONE;
1843       break;
1844 
1845     case PHOTOS_WINDOW_MODE_NONE:
1846     default:
1847       g_assert_not_reached ();
1848       break;
1849     }
1850 
1851   if (unset_selection_mode)
1852     {
1853       GVariant *state;
1854 
1855       state = g_variant_new_boolean (FALSE);
1856       g_action_change_state (selection_mode_action, state);
1857     }
1858 
1859   if (active_changed)
1860     g_signal_emit_by_name (self, "active-changed", self->active_object);
1861 
1862   if (active_collection_changed)
1863     g_signal_emit (self, signals[ACTIVE_COLLECTION_CHANGED], 0, self->active_collection);
1864 
1865   g_signal_emit (self, signals[WINDOW_MODE_CHANGED], 0, self->mode, old_mode);
1866 }
1867 
1868 
1869 void
photos_mode_controller_toggle_fullscreen(PhotosModeController * self)1870 photos_mode_controller_toggle_fullscreen (PhotosModeController *self)
1871 {
1872   g_return_if_fail (PHOTOS_IS_MODE_CONTROLLER (self));
1873   photos_mode_controller_set_fullscreen (self, !self->fullscreen);
1874 }
1875 
1876 
1877 void
photos_mode_controller_set_fullscreen(PhotosModeController * self,gboolean fullscreen)1878 photos_mode_controller_set_fullscreen (PhotosModeController *self, gboolean fullscreen)
1879 {
1880   g_return_if_fail (PHOTOS_IS_MODE_CONTROLLER (self));
1881 
1882   if (self->fullscreen == fullscreen)
1883     return;
1884 
1885   self->fullscreen = fullscreen;
1886   g_signal_emit (self, signals[FULLSCREEN_CHANGED], 0, self->fullscreen);
1887 }
1888 
1889 
1890 void
photos_mode_controller_set_window_mode(PhotosModeController * self,PhotosWindowMode mode)1891 photos_mode_controller_set_window_mode (PhotosModeController *self, PhotosWindowMode mode)
1892 {
1893   GAction *selection_mode_action;
1894   GApplication *app;
1895   PhotosWindowMode old_mode;
1896   gboolean active_changed = FALSE;
1897   gboolean active_collection_changed = FALSE;
1898   gboolean set_selection_mode = FALSE;
1899 
1900   g_return_if_fail (PHOTOS_IS_MODE_CONTROLLER (self));
1901   g_return_if_fail (mode != PHOTOS_WINDOW_MODE_NONE);
1902   g_return_if_fail (mode != PHOTOS_WINDOW_MODE_COLLECTION_VIEW);
1903   g_return_if_fail (mode != PHOTOS_WINDOW_MODE_PREVIEW);
1904 
1905   app = g_application_get_default ();
1906   selection_mode_action = g_action_map_lookup_action (G_ACTION_MAP (app), "selection-mode");
1907 
1908   if (mode == PHOTOS_WINDOW_MODE_EDIT)
1909     {
1910       g_return_if_fail (self->load_state == PHOTOS_LOAD_STATE_FINISHED);
1911       g_return_if_fail (self->mode == PHOTOS_WINDOW_MODE_PREVIEW);
1912     }
1913   else if (mode == PHOTOS_WINDOW_MODE_IMPORT)
1914     {
1915       g_autoptr (GVariant) state = NULL;
1916 
1917       g_return_if_fail (self->mode == PHOTOS_WINDOW_MODE_COLLECTIONS
1918                         || self->mode == PHOTOS_WINDOW_MODE_FAVORITES
1919                         || self->mode == PHOTOS_WINDOW_MODE_OVERVIEW);
1920 
1921       state = g_action_get_state (selection_mode_action);
1922       g_return_if_fail (state != NULL);
1923       g_return_if_fail (!g_variant_get_boolean (state));
1924     }
1925   else
1926     {
1927       g_return_if_fail (self->mode != PHOTOS_WINDOW_MODE_EDIT);
1928       g_return_if_fail (self->mode != PHOTOS_WINDOW_MODE_PREVIEW);
1929     }
1930 
1931   if (!photos_item_manager_set_window_mode_internal (self, mode, &old_mode))
1932     return;
1933 
1934   photos_item_manager_update_fullscreen (self);
1935   photos_item_manager_clear_active_item_load (self);
1936 
1937   if (mode != PHOTOS_WINDOW_MODE_EDIT)
1938     {
1939       self->load_state = PHOTOS_LOAD_STATE_NONE;
1940 
1941       if (self->active_collection != NULL)
1942         {
1943           g_clear_object (&self->active_collection);
1944           active_collection_changed = TRUE;
1945         }
1946 
1947       g_clear_object (&self->active_object);
1948       active_changed = TRUE;
1949     }
1950 
1951   if (mode == PHOTOS_WINDOW_MODE_IMPORT)
1952     set_selection_mode = TRUE;
1953 
1954   if (set_selection_mode)
1955     {
1956       GVariant *state;
1957 
1958       state = g_variant_new_boolean (TRUE);
1959       g_action_change_state (selection_mode_action, state);
1960     }
1961 
1962   if (active_changed)
1963     g_signal_emit_by_name (self, "active-changed", self->active_object);
1964 
1965   if (active_collection_changed)
1966     g_signal_emit (self, signals[ACTIVE_COLLECTION_CHANGED], 0, self->active_collection);
1967 
1968   g_signal_emit (self, signals[WINDOW_MODE_CHANGED], 0, mode, old_mode);
1969 }
1970