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