GCC Code Coverage Report


Directory: ./
File: panels/background/bg-recent-source.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 179 0.0%
Functions: 0 20 0.0%
Branches: 0 124 0.0%

Line Branch Exec Source
1 /* bg-recent-source.c
2 *
3 * Copyright 2019 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
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 * SPDX-License-Identifier: GPL-3.0-or-later
19 */
20
21 #undef G_LOG_DOMAIN
22 #define G_LOG_DOMAIN "bg-recent-source"
23
24 #include "bg-recent-source.h"
25 #include "cc-background-item.h"
26
27 #define ATTRIBUTES G_FILE_ATTRIBUTE_STANDARD_NAME "," \
28 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE "," \
29 G_FILE_ATTRIBUTE_TIME_MODIFIED
30
31 struct _BgRecentSource
32 {
33 BgSource parent;
34
35 GFile *backgrounds_folder;
36 GFileMonitor *monitor;
37
38 GCancellable *cancellable;
39 GHashTable *items;
40 };
41
42 G_DEFINE_TYPE (BgRecentSource, bg_recent_source, BG_TYPE_SOURCE)
43
44 static int
45 sort_func (gconstpointer a,
46 gconstpointer b,
47 gpointer user_data)
48 {
49 CcBackgroundItem *item_a;
50 CcBackgroundItem *item_b;
51 guint64 modified_a;
52 guint64 modified_b;
53 int retval;
54
55 item_a = (CcBackgroundItem *) a;
56 item_b = (CcBackgroundItem *) b;
57 modified_a = cc_background_item_get_modified (item_a);
58 modified_b = cc_background_item_get_modified (item_b);
59
60 retval = modified_b - modified_a;
61
62 return retval;
63 }
64
65 static void
66 add_file_from_info (BgRecentSource *self,
67 GFile *file,
68 GFileInfo *info)
69 {
70 g_autoptr(CcBackgroundItem) item = NULL;
71 g_autofree gchar *source_uri = NULL;
72 g_autofree gchar *uri = NULL;
73 GListStore *store;
74 const gchar *content_type;
75 guint64 mtime;
76
77 content_type = g_file_info_get_content_type (info);
78 mtime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
79
80 if (!content_type || !g_content_type_is_a (content_type, "image/*"))
81 return;
82
83 uri = g_file_get_uri (file);
84 item = cc_background_item_new (uri);
85 g_object_set (G_OBJECT (item),
86 "shading", G_DESKTOP_BACKGROUND_SHADING_SOLID,
87 "placement", G_DESKTOP_BACKGROUND_STYLE_ZOOM,
88 "modified", mtime,
89 "needs-download", FALSE,
90 "source-url", source_uri,
91 NULL);
92
93 store = bg_source_get_liststore (BG_SOURCE (self));
94 g_list_store_insert_sorted (store, item, sort_func, self);
95
96 g_hash_table_insert (self->items, g_strdup (uri), g_object_ref (item));
97 }
98
99 static void
100 remove_item (BgRecentSource *self,
101 CcBackgroundItem *item)
102 {
103 GListStore *store;
104 const gchar *uri;
105 guint i;
106
107 g_return_if_fail (BG_IS_RECENT_SOURCE (self));
108 g_return_if_fail (CC_IS_BACKGROUND_ITEM (item));
109
110 uri = cc_background_item_get_uri (item);
111 store = bg_source_get_liststore (BG_SOURCE (self));
112
113 g_debug ("Removing wallpaper %s", uri);
114
115 for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (store)); i++)
116 {
117 g_autoptr(CcBackgroundItem) tmp = NULL;
118
119 tmp = g_list_model_get_item (G_LIST_MODEL (store), i);
120
121 if (tmp == item)
122 {
123 g_list_store_remove (store, i);
124 break;
125 }
126 }
127
128 g_hash_table_remove (self->items, cc_background_item_get_uri (item));
129 }
130
131 static void
132 query_info_finished_cb (GObject *source,
133 GAsyncResult *result,
134 gpointer user_data)
135 {
136 BgRecentSource *self;
137 g_autoptr(GFileInfo) file_info = NULL;
138 g_autoptr(GError) error = NULL;
139 GFile *file = NULL;
140
141 file = G_FILE (source);
142 file_info = g_file_query_info_finish (file, result, &error);
143 if (error)
144 {
145 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
146 g_warning ("Could not get pictures file information: %s", error->message);
147 return;
148 }
149
150 self = BG_RECENT_SOURCE (user_data);
151
152 g_debug ("Adding wallpaper %s (%d)",
153 g_file_info_get_name (file_info),
154 G_IS_FILE (self->backgrounds_folder));
155
156 add_file_from_info (self, file, file_info);
157 }
158
159 static void
160 on_file_changed_cb (BgRecentSource *self,
161 GFile *file,
162 GFile *other_file,
163 GFileMonitorEvent event_type)
164 {
165 g_autofree gchar *uri = NULL;
166
167 switch (event_type)
168 {
169 case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
170 g_file_query_info_async (file,
171 ATTRIBUTES,
172 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
173 G_PRIORITY_DEFAULT,
174 self->cancellable,
175 query_info_finished_cb,
176 self);
177 break;
178
179 case G_FILE_MONITOR_EVENT_DELETED:
180 uri = g_file_get_uri (file);
181 remove_item (self, g_hash_table_lookup (self->items, uri));
182 break;
183
184 default:
185 return;
186 }
187 }
188
189 static int
190 file_sort_func (gconstpointer a,
191 gconstpointer b)
192 {
193 GFileInfo *file_a = G_FILE_INFO (a);
194 GFileInfo *file_b = G_FILE_INFO (b);
195 guint64 modified_a, modified_b;
196
197 modified_a = g_file_info_get_attribute_uint64 (file_a, G_FILE_ATTRIBUTE_TIME_MODIFIED);
198 modified_b = g_file_info_get_attribute_uint64 (file_b, G_FILE_ATTRIBUTE_TIME_MODIFIED);
199
200 return modified_b - modified_a;
201 }
202
203 static void
204 file_info_async_ready_cb (GObject *source,
205 GAsyncResult *result,
206 gpointer user_data)
207 {
208 BgRecentSource *self;
209 g_autolist(GFileInfo) file_infos = NULL;
210 g_autoptr(GError) error = NULL;
211 GFile *parent = NULL;
212 GList *l;
213
214 file_infos = g_file_enumerator_next_files_finish (G_FILE_ENUMERATOR (source),
215 result,
216 &error);
217 if (error)
218 {
219 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
220 g_warning ("Could not get pictures file information: %s", error->message);
221 return;
222 }
223
224 self = BG_RECENT_SOURCE (user_data);
225 parent = g_file_enumerator_get_container (G_FILE_ENUMERATOR (source));
226
227 file_infos = g_list_sort (file_infos, file_sort_func);
228
229 for (l = file_infos; l; l = l->next)
230 {
231 g_autoptr(GFile) file = NULL;
232 GFileInfo *info;
233
234 info = l->data;
235 file = g_file_get_child (parent, g_file_info_get_name (info));
236
237 g_debug ("Found recent wallpaper %s", g_file_info_get_name (info));
238
239 add_file_from_info (self, file, info);
240 }
241
242 g_file_enumerator_close (G_FILE_ENUMERATOR (source), self->cancellable, &error);
243
244 if (error)
245 g_warning ("Error closing file enumerator: %s", error->message);
246 }
247
248 static void
249 enumerate_children_finished_cb (GObject *source,
250 GAsyncResult *result,
251 gpointer user_data)
252 {
253 BgRecentSource *self;
254 g_autoptr(GFileEnumerator) enumerator = NULL;
255 g_autoptr(GError) error = NULL;
256
257 enumerator = g_file_enumerate_children_finish (G_FILE (source), result, &error);
258
259 if (error)
260 {
261 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
262 g_warning ("Could not fill pictures source: %s", error->message);
263 return;
264 }
265
266 self = BG_RECENT_SOURCE (user_data);
267 g_file_enumerator_next_files_async (enumerator,
268 G_MAXINT,
269 G_PRIORITY_DEFAULT,
270 self->cancellable,
271 file_info_async_ready_cb,
272 self);
273 }
274
275 static void
276 load_backgrounds (BgRecentSource *self)
277 {
278 g_autofree gchar *backgrounds_path = NULL;
279 g_autoptr(GError) error = NULL;
280
281 if (!g_file_make_directory_with_parents (self->backgrounds_folder, self->cancellable, &error) &&
282 !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
283 {
284 g_critical ("Failed to create local background directory: %s", error->message);
285 return;
286 }
287
288 backgrounds_path = g_file_get_path (self->backgrounds_folder);
289 g_debug ("Enumerating wallpapers under %s", backgrounds_path);
290
291 g_file_enumerate_children_async (self->backgrounds_folder,
292 ATTRIBUTES,
293 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
294 G_PRIORITY_DEFAULT,
295 self->cancellable,
296 enumerate_children_finished_cb,
297 self);
298
299 self->monitor = g_file_monitor_directory (self->backgrounds_folder,
300 G_FILE_MONITOR_WATCH_MOVES,
301 self->cancellable,
302 &error);
303
304 if (!self->monitor)
305 {
306 g_critical ("Failed to monitor background directory: %s", error->message);
307 return;
308 }
309
310 g_signal_connect_object (self->monitor, "changed", G_CALLBACK (on_file_changed_cb), self, G_CONNECT_SWAPPED);
311 }
312
313 /* Callbacks */
314
315 static void
316 on_file_copied_cb (GObject *source,
317 GAsyncResult *result,
318 gpointer user_data)
319 {
320 g_autoptr(BgRecentSource) self = BG_RECENT_SOURCE (user_data);
321 g_autofree gchar *original_file = NULL;
322 g_autoptr(GError) error = NULL;
323
324 g_file_copy_finish (G_FILE (source), result, &error);
325
326 if (error)
327 {
328 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
329 g_critical ("Failed to copy file: %s", error->message);
330 return;
331 }
332
333 original_file = g_file_get_path (G_FILE (source));
334 g_debug ("Successfully copied wallpaper: %s", original_file);
335 }
336
337 static void
338 on_file_deleted_cb (GObject *source,
339 GAsyncResult *result,
340 gpointer user_data)
341 {
342 g_autoptr(BgRecentSource) self = BG_RECENT_SOURCE (user_data);
343 g_autofree gchar *original_file = NULL;
344 g_autoptr(GError) error = NULL;
345
346 g_file_delete_finish (G_FILE (source), result, &error);
347
348 if (error)
349 {
350 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
351 g_critical ("Failed to delete wallpaper: %s", error->message);
352 return;
353 }
354
355 original_file = g_file_get_path (G_FILE (source));
356 g_debug ("Successfully deleted wallpaper: %s", original_file);
357 }
358
359 /* GObject overrides */
360
361 static void
362 bg_recent_source_finalize (GObject *object)
363 {
364 BgRecentSource *self = (BgRecentSource *)object;
365
366 g_cancellable_cancel (self->cancellable);
367 g_clear_object (&self->cancellable);
368 g_clear_object (&self->monitor);
369
370 G_OBJECT_CLASS (bg_recent_source_parent_class)->finalize (object);
371 }
372
373 static void
374 bg_recent_source_class_init (BgRecentSourceClass *klass)
375 {
376 GObjectClass *object_class = G_OBJECT_CLASS (klass);
377
378 object_class->finalize = bg_recent_source_finalize;
379 }
380
381 static void
382 bg_recent_source_init (BgRecentSource *self)
383 {
384 g_autofree gchar *backgrounds_path = NULL;
385
386 backgrounds_path = g_build_filename (g_get_user_data_dir (), "backgrounds", NULL);
387
388 self->items = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
389 self->cancellable = g_cancellable_new ();
390 self->backgrounds_folder = g_file_new_for_path (backgrounds_path);
391
392 load_backgrounds (self);
393 }
394
395 BgRecentSource*
396 bg_recent_source_new (void)
397 {
398 return g_object_new (BG_TYPE_RECENT_SOURCE, NULL);
399 }
400
401 void
402 bg_recent_source_add_file (BgRecentSource *self,
403 const gchar *path)
404 {
405 g_autoptr(GDateTime) now = NULL;
406 g_autofree gchar *destination_name = NULL;
407 g_autofree gchar *formatted_now = NULL;
408 g_autofree gchar *basename = NULL;
409 g_autoptr(GFile) destination = NULL;
410 g_autoptr(GFile) file = NULL;
411
412 g_return_if_fail (BG_IS_RECENT_SOURCE (self));
413 g_return_if_fail (path && *path);
414
415 g_debug ("Importing wallpaper %s", path);
416
417 now = g_date_time_new_now_local ();
418 formatted_now = g_date_time_format (now, "%Y-%m-%d-%H-%M-%S");
419
420 file = g_file_new_for_path (path);
421
422 basename = g_file_get_basename (file);
423 destination_name = g_strdup_printf ("%s-%s", formatted_now, basename);
424 destination = g_file_get_child (self->backgrounds_folder, destination_name);
425
426 g_file_copy_async (file,
427 destination,
428 G_FILE_COPY_NONE,
429 G_PRIORITY_DEFAULT,
430 self->cancellable,
431 NULL, NULL,
432 on_file_copied_cb,
433 g_object_ref (self));
434 }
435
436 void
437 bg_recent_source_remove_item (BgRecentSource *self,
438 CcBackgroundItem *item)
439 {
440 g_autoptr(GFile) file = NULL;
441 const gchar *uri;
442
443 g_return_if_fail (BG_IS_RECENT_SOURCE (self));
444 g_return_if_fail (CC_IS_BACKGROUND_ITEM (item));
445
446 uri = cc_background_item_get_uri (item);
447 file = g_file_new_for_uri (uri);
448
449 g_file_delete_async (file,
450 G_PRIORITY_DEFAULT,
451 self->cancellable,
452 on_file_deleted_cb,
453 g_object_ref (self));
454 }
455