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 <dazzle.h>
27
28 #include "photos-filterable.h"
29 #include "photos-query.h"
30 #include "photos-source.h"
31 #include "photos-utils.h"
32
33
34 struct _PhotosSource
35 {
36 GObject parent_instance;
37 GIcon *icon;
38 GIcon *symbolic_icon;
39 GMount *mount;
40 GoaObject *object;
41 gboolean builtin;
42 gchar *id;
43 gchar *name;
44 };
45
46 enum
47 {
48 PROP_0,
49 PROP_BUILTIN,
50 PROP_ID,
51 PROP_MOUNT,
52 PROP_NAME,
53 PROP_OBJECT
54 };
55
56 static void photos_source_filterable_iface_init (PhotosFilterableInterface *iface);
57
58
59 G_DEFINE_TYPE_WITH_CODE (PhotosSource, photos_source, G_TYPE_OBJECT,
60 G_IMPLEMENT_INTERFACE (PHOTOS_TYPE_FILTERABLE,
61 photos_source_filterable_iface_init));
62 DZL_DEFINE_COUNTER (instances, "PhotosSource", "Instances", "Number of PhotosSource instances")
63
64
65 static const gchar *TRACKER_SCHEMA = "org.freedesktop.Tracker3.Miner.Files";
66 static const gchar *TRACKER_KEY_RECURSIVE_DIRECTORIES = "index-recursive-directories";
67
68
69 static gchar *
photos_source_build_filter_local(void)70 photos_source_build_filter_local (void)
71 {
72 g_autoptr (GSettings) settings = NULL;
73 g_autoptr (GString) tracker_filter = NULL;
74 g_auto (GStrv) tracker_dirs = NULL;
75 g_autofree gchar *desktop_uri = NULL;
76 g_autofree gchar *download_uri = NULL;
77 g_autofree gchar *export_path = NULL;
78 g_autofree gchar *export_uri = NULL;
79 gchar *filter;
80 const gchar *path;
81 g_autofree gchar *pictures_uri = NULL;
82 guint i;
83
84 settings = g_settings_new (TRACKER_SCHEMA);
85 tracker_dirs = g_settings_get_strv (settings, TRACKER_KEY_RECURSIVE_DIRECTORIES);
86 tracker_filter = g_string_new ("");
87
88 for (i = 0; tracker_dirs[i] != NULL; i++)
89 {
90 g_autofree gchar *tracker_uri = NULL;
91
92 /* ignore special XDG placeholders, since we handle those internally */
93 if (tracker_dirs[i][0] == '&' || tracker_dirs[i][0] == '$')
94 continue;
95
96 tracker_uri = photos_utils_convert_path_to_uri (tracker_dirs[i]);
97 g_string_append_printf (tracker_filter, " || fn:contains (nie:isStoredAs (?urn), '%s')", tracker_uri);
98 }
99
100 path = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
101 desktop_uri = photos_utils_convert_path_to_uri (path);
102
103 path = g_get_user_special_dir (G_USER_DIRECTORY_DOWNLOAD);
104 download_uri = photos_utils_convert_path_to_uri (path);
105
106 path = g_get_user_special_dir (G_USER_DIRECTORY_PICTURES);
107 pictures_uri = photos_utils_convert_path_to_uri (path);
108
109 export_path = g_build_filename (path, PHOTOS_EXPORT_SUBPATH, NULL);
110 export_uri = photos_utils_convert_path_to_uri (export_path);
111
112 filter = g_strdup_printf ("(((fn:contains (nie:isStoredAs (?urn), '%s')"
113 " || fn:contains (nie:isStoredAs (?urn), '%s')"
114 " || fn:contains (nie:isStoredAs (?urn), '%s')"
115 " %s)"
116 " && !fn:contains (nie:isStoredAs (?urn), '%s'))"
117 " || fn:starts-with (nao:identifier (?urn), '%s')"
118 " || (?urn = nfo:image-category-screenshot))",
119 desktop_uri,
120 download_uri,
121 pictures_uri,
122 tracker_filter->str,
123 export_uri,
124 PHOTOS_QUERY_LOCAL_COLLECTIONS_IDENTIFIER);
125
126 return filter;
127 }
128
129
130 static gchar *
photos_source_build_filter_resource(PhotosSource * self)131 photos_source_build_filter_resource (PhotosSource *self)
132 {
133 g_autofree gchar *filter = NULL;
134 gchar *ret_val = NULL;
135
136 g_return_val_if_fail (!self->builtin, NULL);
137
138 if (self->object != NULL)
139 {
140 filter = g_strdup_printf ("(nie:dataSource (?urn) = '%s')", self->id);
141 }
142 else if (self->mount != NULL)
143 {
144 g_autoptr (GFile) root = NULL;
145 g_autofree gchar *uri = NULL;
146
147 root = g_mount_get_root (self->mount);
148 uri = g_file_get_uri (root);
149 filter = g_strdup_printf ("(fn:starts-with (nie:isStoredAs (?urn), '%s'))", uri);
150 }
151 else
152 {
153 g_return_val_if_reached (NULL);
154 }
155
156 ret_val = g_steal_pointer (&filter);
157 return ret_val;
158 }
159
160
161 static gboolean
photos_source_get_builtin(PhotosFilterable * iface)162 photos_source_get_builtin (PhotosFilterable *iface)
163 {
164 PhotosSource *self = PHOTOS_SOURCE (iface);
165 return self->builtin;
166 }
167
168
169 static gchar *
photos_source_get_filter(PhotosFilterable * iface)170 photos_source_get_filter (PhotosFilterable *iface)
171 {
172 PhotosSource *self = PHOTOS_SOURCE (iface);
173
174 g_assert_cmpstr (self->id, !=, PHOTOS_SOURCE_STOCK_ALL);
175
176 if (g_strcmp0 (self->id, PHOTOS_SOURCE_STOCK_LOCAL) == 0)
177 return photos_source_build_filter_local ();
178
179 return photos_source_build_filter_resource (self);
180 }
181
182
183 static const gchar *
photos_source_get_id(PhotosFilterable * filterable)184 photos_source_get_id (PhotosFilterable *filterable)
185 {
186 PhotosSource *self = PHOTOS_SOURCE (filterable);
187 return self->id;
188 }
189
190
191 static gboolean
photos_source_is_search_criterion(PhotosFilterable * filterable)192 photos_source_is_search_criterion (PhotosFilterable *filterable)
193 {
194 PhotosSource *self = PHOTOS_SOURCE (filterable);
195 gboolean ret_val;
196
197 ret_val = self->mount == NULL;
198 return ret_val;
199 }
200
201
202 static void
photos_source_dispose(GObject * object)203 photos_source_dispose (GObject *object)
204 {
205 PhotosSource *self = PHOTOS_SOURCE (object);
206
207 g_clear_object (&self->icon);
208 g_clear_object (&self->mount);
209 g_clear_object (&self->object);
210 g_clear_object (&self->symbolic_icon);
211
212 G_OBJECT_CLASS (photos_source_parent_class)->dispose (object);
213 }
214
215
216 static void
photos_source_finalize(GObject * object)217 photos_source_finalize (GObject *object)
218 {
219 PhotosSource *self = PHOTOS_SOURCE (object);
220
221 g_free (self->id);
222 g_free (self->name);
223
224 G_OBJECT_CLASS (photos_source_parent_class)->finalize (object);
225
226 DZL_COUNTER_DEC (instances);
227 }
228
229
230 static void
photos_source_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)231 photos_source_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
232 {
233 PhotosSource *self = PHOTOS_SOURCE (object);
234
235 switch (prop_id)
236 {
237 case PROP_BUILTIN:
238 g_value_set_boolean (value, self->builtin);
239 break;
240
241 case PROP_ID:
242 g_value_set_string (value, self->id);
243 break;
244
245 case PROP_MOUNT:
246 g_value_set_object (value, self->mount);
247 break;
248
249 case PROP_NAME:
250 g_value_set_string (value, self->name);
251 break;
252
253 case PROP_OBJECT:
254 g_value_set_object (value, (gpointer) self->object);
255 break;
256
257 default:
258 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
259 break;
260 }
261 }
262
263
264 static void
photos_source_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)265 photos_source_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
266 {
267 PhotosSource *self = PHOTOS_SOURCE (object);
268
269 switch (prop_id)
270 {
271 case PROP_BUILTIN:
272 self->builtin = g_value_get_boolean (value);
273 break;
274
275 case PROP_ID:
276 {
277 const gchar *id;
278
279 id = g_value_get_string (value);
280 g_return_if_fail (self->id == NULL || id == NULL);
281
282 if (self->id == NULL && id != NULL)
283 self->id = g_strdup (id);
284
285 break;
286 }
287
288 case PROP_MOUNT:
289 {
290 g_autoptr (GFile) root = NULL;
291 const gchar *type_name;
292 g_autofree gchar *uri = NULL;
293
294 self->mount = G_MOUNT (g_value_dup_object (value));
295 if (self->mount == NULL)
296 break;
297
298 type_name = G_OBJECT_TYPE_NAME (self->mount);
299 root = g_mount_get_root (self->mount);
300 uri = g_file_get_uri (root);
301 self->id = g_strdup_printf ("gd:g-mount:%s:%s", type_name, uri);
302
303 self->icon = g_mount_get_icon (self->mount);
304 self->symbolic_icon = g_mount_get_symbolic_icon (self->mount);
305 self->name = g_mount_get_name (self->mount);
306 break;
307 }
308
309 case PROP_NAME:
310 {
311 const gchar *name;
312
313 name = g_value_get_string (value);
314 g_return_if_fail (self->name == NULL || name == NULL);
315
316 if (self->name == NULL && name != NULL)
317 self->name = g_strdup (name);
318
319 break;
320 }
321
322 case PROP_OBJECT:
323 {
324 GoaAccount *account;
325 const gchar *provider_icon;
326 const gchar *provider_name;
327 const gchar *id;
328
329 self->object = GOA_OBJECT (g_value_dup_object (value));
330 if (self->object == NULL)
331 break;
332
333 g_return_if_fail (self->id == NULL);
334 g_return_if_fail (self->name == NULL);
335
336 account = goa_object_peek_account (self->object);
337
338 id = goa_account_get_id (account);
339 self->id = g_strdup_printf ("gd:goa-account:%s", id);
340
341 provider_icon = goa_account_get_provider_icon (account);
342
343 {
344 g_autoptr (GError) error = NULL;
345
346 self->icon = g_icon_new_for_string (provider_icon, &error);
347 if (error != NULL)
348 g_critical ("Unable to generate a GIcon for %s: %s", id, error->message);
349 }
350
351 provider_name = goa_account_get_provider_name (account);
352 self->name = g_strdup (provider_name);
353 break;
354 }
355
356 default:
357 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
358 break;
359 }
360 }
361
362
363 static void
photos_source_init(PhotosSource * self)364 photos_source_init (PhotosSource *self)
365 {
366 DZL_COUNTER_INC (instances);
367 }
368
369
370 static void
photos_source_class_init(PhotosSourceClass * class)371 photos_source_class_init (PhotosSourceClass *class)
372 {
373 GObjectClass *object_class = G_OBJECT_CLASS (class);
374
375 object_class->dispose = photos_source_dispose;
376 object_class->finalize = photos_source_finalize;
377 object_class->get_property = photos_source_get_property;
378 object_class->set_property = photos_source_set_property;
379
380 g_object_class_install_property (object_class,
381 PROP_BUILTIN,
382 g_param_spec_boolean ("builtin",
383 "",
384 "",
385 FALSE,
386 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
387
388 g_object_class_install_property (object_class,
389 PROP_ID,
390 g_param_spec_string ("id",
391 "",
392 "",
393 NULL,
394 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
395
396 g_object_class_install_property (object_class,
397 PROP_MOUNT,
398 g_param_spec_object ("mount",
399 "GMount instance",
400 "A mount point representing a removable device (eg., camera)",
401 G_TYPE_MOUNT,
402 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
403
404 g_object_class_install_property (object_class,
405 PROP_NAME,
406 g_param_spec_string ("name",
407 "",
408 "",
409 NULL,
410 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
411
412 g_object_class_install_property (object_class,
413 PROP_OBJECT,
414 g_param_spec_object ("object",
415 "GoaObject instance",
416 "A GOA configured account from which the source was created",
417 GOA_TYPE_OBJECT,
418 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
419 }
420
421
422 static void
photos_source_filterable_iface_init(PhotosFilterableInterface * iface)423 photos_source_filterable_iface_init (PhotosFilterableInterface *iface)
424 {
425 iface->get_builtin = photos_source_get_builtin;
426 iface->get_filter = photos_source_get_filter;
427 iface->get_id = photos_source_get_id;
428 iface->is_search_criterion = photos_source_is_search_criterion;
429 }
430
431
432 PhotosSource *
photos_source_new(const gchar * id,const gchar * name,gboolean builtin)433 photos_source_new (const gchar *id, const gchar *name, gboolean builtin)
434 {
435 return g_object_new (PHOTOS_TYPE_SOURCE, "id", id, "name", name, "builtin", builtin, NULL);
436 }
437
438
439 PhotosSource *
photos_source_new_from_goa_object(GoaObject * object)440 photos_source_new_from_goa_object (GoaObject *object)
441 {
442 g_return_val_if_fail (GOA_IS_OBJECT (object), NULL);
443 return g_object_new (PHOTOS_TYPE_SOURCE, "object", object, NULL);
444 }
445
446
447 PhotosSource *
photos_source_new_from_mount(GMount * mount)448 photos_source_new_from_mount (GMount *mount)
449 {
450 g_return_val_if_fail (G_IS_MOUNT (mount), NULL);
451 return g_object_new (PHOTOS_TYPE_SOURCE, "mount", mount, NULL);
452 }
453
454
455 const gchar *
photos_source_get_name(PhotosSource * self)456 photos_source_get_name (PhotosSource *self)
457 {
458 return self->name;
459 }
460
461
462 GoaObject *
photos_source_get_goa_object(PhotosSource * self)463 photos_source_get_goa_object (PhotosSource *self)
464 {
465 return self->object;
466 }
467
468
469 GIcon *
photos_source_get_icon(PhotosSource * self)470 photos_source_get_icon (PhotosSource *self)
471 {
472 return self->icon;
473 }
474
475
476 GMount *
photos_source_get_mount(PhotosSource * self)477 photos_source_get_mount (PhotosSource *self)
478 {
479 return self->mount;
480 }
481
482
483 GIcon *
photos_source_get_symbolic_icon(PhotosSource * self)484 photos_source_get_symbolic_icon (PhotosSource *self)
485 {
486 return self->symbolic_icon;
487 }
488