GCC Code Coverage Report


Directory: ./
File: panels/background/cc-background-chooser.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 157 0.0%
Functions: 0 17 0.0%
Branches: 0 55 0.0%

Line Branch Exec Source
1 /* cc-background-chooser.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 "cc-background-chooser"
23
24 #include <glib/gi18n.h>
25 #include <libgnome-desktop/gnome-desktop-thumbnail.h>
26
27 #include "bg-recent-source.h"
28 #include "bg-wallpapers-source.h"
29 #include "cc-background-chooser.h"
30 #include "cc-background-paintable.h"
31 #include "cc-background-item.h"
32
33 #define THUMBNAIL_WIDTH 144
34 #define THUMBNAIL_HEIGHT (THUMBNAIL_WIDTH * 3 / 4)
35
36 struct _CcBackgroundChooser
37 {
38 GtkBox parent;
39
40 GtkFlowBox *flowbox;
41 GtkWidget *recent_box;
42 GtkFlowBox *recent_flowbox;
43
44 gboolean recent_selected;
45
46 BgWallpapersSource *wallpapers_source;
47 BgRecentSource *recent_source;
48
49 CcBackgroundItem *active_item;
50
51 GnomeDesktopThumbnailFactory *thumbnail_factory;
52 };
53
54 G_DEFINE_TYPE (CcBackgroundChooser, cc_background_chooser, GTK_TYPE_BOX)
55
56 enum
57 {
58 BACKGROUND_CHOSEN,
59 N_SIGNALS,
60 };
61
62 static guint signals [N_SIGNALS];
63
64 static void
65 emit_background_chosen (CcBackgroundChooser *self)
66 {
67 g_autoptr(GList) list = NULL;
68 CcBackgroundItem *item;
69 GtkFlowBox *flowbox;
70
71 flowbox = self->recent_selected ? self->recent_flowbox : self->flowbox;
72 list = gtk_flow_box_get_selected_children (flowbox);
73 g_assert (g_list_length (list) == 1);
74
75 item = g_object_get_data (list->data, "item");
76
77 g_signal_emit (self, signals[BACKGROUND_CHOSEN], 0, item);
78 }
79
80 static void
81 on_delete_background_clicked_cb (GtkButton *button,
82 BgRecentSource *source)
83 {
84 GtkWidget *parent;
85 CcBackgroundItem *item;
86
87 parent = gtk_widget_get_parent (gtk_widget_get_parent (GTK_WIDGET (button)));
88 g_assert (GTK_IS_FLOW_BOX_CHILD (parent));
89
90 item = g_object_get_data (G_OBJECT (parent), "item");
91
92 bg_recent_source_remove_item (source, item);
93 }
94
95 static void
96 direction_changed_cb (GtkWidget *widget,
97 GtkTextDirection *previous_direction,
98 GdkPaintable *paintable)
99 {
100 g_object_set (paintable,
101 "text-direction", gtk_widget_get_direction (widget),
102 NULL);
103 }
104
105 static GtkWidget*
106 create_widget_func (gpointer model_item,
107 gpointer user_data)
108 {
109 g_autoptr(CcBackgroundPaintable) paintable = NULL;
110 CcBackgroundChooser *self;
111 CcBackgroundItem *item;
112 GtkWidget *overlay;
113 GtkWidget *child;
114 GtkWidget *picture;
115 GtkWidget *icon;
116 GtkWidget *check;
117 GtkWidget *button = NULL;
118 BgSource *source;
119
120 source = BG_SOURCE (user_data);
121 item = CC_BACKGROUND_ITEM (model_item);
122
123 self = g_object_get_data (G_OBJECT (source), "background-chooser");
124
125 paintable = cc_background_paintable_new (self->thumbnail_factory,
126 item,
127 CC_BACKGROUND_PAINT_LIGHT_DARK,
128 THUMBNAIL_WIDTH,
129 THUMBNAIL_HEIGHT);
130
131 picture = gtk_picture_new_for_paintable (GDK_PAINTABLE (paintable));
132 gtk_picture_set_can_shrink (GTK_PICTURE (picture), FALSE);
133
134 g_object_bind_property (picture, "scale-factor",
135 paintable, "scale-factor", G_BINDING_SYNC_CREATE);
136 g_signal_connect_object (picture, "direction-changed",
137 G_CALLBACK (direction_changed_cb), paintable, 0);
138
139 icon = gtk_image_new_from_icon_name ("slideshow-symbolic");
140 gtk_widget_set_halign (icon, GTK_ALIGN_START);
141 gtk_widget_set_valign (icon, GTK_ALIGN_END);
142 gtk_widget_set_visible (icon, cc_background_item_changes_with_time (item));
143 gtk_widget_add_css_class (icon, "slideshow-icon");
144
145 check = gtk_image_new_from_icon_name ("background-selected-symbolic");
146 gtk_widget_set_halign (check, GTK_ALIGN_END);
147 gtk_widget_set_valign (check, GTK_ALIGN_END);
148 gtk_widget_add_css_class (check, "selected-check");
149
150 if (BG_IS_RECENT_SOURCE (source))
151 {
152 button = gtk_button_new_from_icon_name ("cross-small-symbolic");
153 gtk_widget_set_halign (button, GTK_ALIGN_END);
154 gtk_widget_set_valign (button, GTK_ALIGN_START);
155
156 gtk_widget_add_css_class (button, "osd");
157 gtk_widget_add_css_class (button, "circular");
158 gtk_widget_add_css_class (button, "remove-button");
159
160 gtk_widget_set_tooltip_text (GTK_WIDGET (button), _("Remove Background"));
161
162 g_signal_connect (button,
163 "clicked",
164 G_CALLBACK (on_delete_background_clicked_cb),
165 source);
166 }
167
168 overlay = gtk_overlay_new ();
169 gtk_widget_set_overflow (overlay, GTK_OVERFLOW_HIDDEN);
170 gtk_widget_add_css_class (overlay, "background-thumbnail");
171 gtk_overlay_set_child (GTK_OVERLAY (overlay), picture);
172 gtk_overlay_add_overlay (GTK_OVERLAY (overlay), icon);
173 gtk_overlay_add_overlay (GTK_OVERLAY (overlay), check);
174 if (button)
175 gtk_overlay_add_overlay (GTK_OVERLAY (overlay), button);
176 gtk_accessible_update_property (GTK_ACCESSIBLE (overlay),
177 GTK_ACCESSIBLE_PROPERTY_LABEL,
178 cc_background_item_get_name (item),
179 -1);
180
181
182 child = gtk_flow_box_child_new ();
183 gtk_widget_set_halign (child, GTK_ALIGN_CENTER);
184 gtk_widget_set_valign (child, GTK_ALIGN_CENTER);
185 gtk_flow_box_child_set_child (GTK_FLOW_BOX_CHILD (child), overlay);
186
187 g_object_set_data_full (G_OBJECT (child), "item", g_object_ref (item), g_object_unref);
188
189 if (self->active_item && cc_background_item_compare (item, self->active_item))
190 gtk_widget_add_css_class (GTK_WIDGET (child), "active-item");
191
192 return child;
193 }
194
195 static void
196 update_recent_visibility (CcBackgroundChooser *self)
197 {
198 GListStore *store;
199 gboolean has_items;
200
201 store = bg_source_get_liststore (BG_SOURCE (self->recent_source));
202 has_items = g_list_model_get_n_items (G_LIST_MODEL (store)) != 0;
203
204 gtk_widget_set_visible (self->recent_box, has_items);
205 }
206
207 static void
208 setup_flowbox (CcBackgroundChooser *self)
209 {
210 GListStore *store;
211
212 store = bg_source_get_liststore (BG_SOURCE (self->wallpapers_source));
213
214 gtk_flow_box_bind_model (self->flowbox,
215 G_LIST_MODEL (store),
216 create_widget_func,
217 self->wallpapers_source,
218 NULL);
219
220 store = bg_source_get_liststore (BG_SOURCE (self->recent_source));
221
222 gtk_flow_box_bind_model (self->recent_flowbox,
223 G_LIST_MODEL (store),
224 create_widget_func,
225 self->recent_source,
226 NULL);
227
228 update_recent_visibility (self);
229 g_signal_connect_object (store,
230 "items-changed",
231 G_CALLBACK (update_recent_visibility),
232 self,
233 G_CONNECT_SWAPPED);
234 }
235
236 static void
237 on_item_activated_cb (CcBackgroundChooser *self,
238 GtkFlowBoxChild *child,
239 GtkFlowBox *flowbox)
240 {
241 self->recent_selected = flowbox == self->recent_flowbox;
242 if (self->recent_selected)
243 gtk_flow_box_unselect_all (self->flowbox);
244 else
245 gtk_flow_box_unselect_all (self->recent_flowbox);
246 emit_background_chosen (self);
247 }
248
249 static void
250 file_dialog_open_cb (GObject *source_object,
251 GAsyncResult *res,
252 gpointer user_data)
253 {
254 CcBackgroundChooser *self = CC_BACKGROUND_CHOOSER (user_data);
255 GtkFileDialog *file_dialog = GTK_FILE_DIALOG (source_object);
256 g_autoptr(GListModel) files = NULL;
257 g_autoptr(GError) error = NULL;
258 guint i;
259
260 files = gtk_file_dialog_open_multiple_finish (file_dialog, res, &error);
261
262 if (error != NULL)
263 {
264 g_warning ("Failed to pick backgrounds: %s", error->message);
265 return;
266 }
267
268 for (i = 0; i < g_list_model_get_n_items (files); i++)
269 {
270 g_autoptr(GFile) file = g_list_model_get_item (files, i);
271 g_autofree gchar *filename = g_file_get_path (file);
272
273 bg_recent_source_add_file (self->recent_source, filename);
274 }
275 }
276
277 /* GObject overrides */
278
279 static void
280 cc_background_chooser_finalize (GObject *object)
281 {
282 CcBackgroundChooser *self = (CcBackgroundChooser *)object;
283
284 g_clear_object (&self->recent_source);
285 g_clear_object (&self->wallpapers_source);
286 g_clear_object (&self->thumbnail_factory);
287
288 G_OBJECT_CLASS (cc_background_chooser_parent_class)->finalize (object);
289 }
290
291 static void
292 cc_background_chooser_class_init (CcBackgroundChooserClass *klass)
293 {
294 GObjectClass *object_class = G_OBJECT_CLASS (klass);
295 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
296
297 object_class->finalize = cc_background_chooser_finalize;
298
299 signals[BACKGROUND_CHOSEN] = g_signal_new ("background-chosen",
300 CC_TYPE_BACKGROUND_CHOOSER,
301 G_SIGNAL_RUN_FIRST,
302 0, NULL, NULL, NULL,
303 G_TYPE_NONE,
304 1,
305 CC_TYPE_BACKGROUND_ITEM);
306
307 gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/background/cc-background-chooser.ui");
308
309 gtk_widget_class_bind_template_child (widget_class, CcBackgroundChooser, flowbox);
310 gtk_widget_class_bind_template_child (widget_class, CcBackgroundChooser, recent_box);
311 gtk_widget_class_bind_template_child (widget_class, CcBackgroundChooser, recent_flowbox);
312
313 gtk_widget_class_bind_template_callback (widget_class, on_item_activated_cb);
314 }
315
316 static void
317 cc_background_chooser_init (CcBackgroundChooser *self)
318 {
319 gtk_widget_init_template (GTK_WIDGET (self));
320
321 self->recent_source = bg_recent_source_new ();
322 self->wallpapers_source = bg_wallpapers_source_new ();
323
324 self->thumbnail_factory = gnome_desktop_thumbnail_factory_new (GNOME_DESKTOP_THUMBNAIL_SIZE_LARGE);
325 g_object_set_data (G_OBJECT (self->recent_source), "background-chooser", self);
326 g_object_set_data (G_OBJECT (self->wallpapers_source), "background-chooser", self);
327
328 setup_flowbox (self);
329 }
330
331 void
332 cc_background_chooser_select_file (CcBackgroundChooser *self)
333 {
334 g_autoptr(GFile) pictures_folder = NULL;
335 GtkFileFilter *filter;
336 GtkFileDialog *file_dialog;
337 GtkWindow *toplevel;
338 GListStore *filters;
339
340 g_return_if_fail (CC_IS_BACKGROUND_CHOOSER (self));
341
342 toplevel = (GtkWindow*) gtk_widget_get_native (GTK_WIDGET (self));
343
344 file_dialog = gtk_file_dialog_new ();
345 gtk_file_dialog_set_title (file_dialog, _("Select a picture"));
346 gtk_file_dialog_set_modal (file_dialog, TRUE);
347
348 filter = gtk_file_filter_new ();
349 gtk_file_filter_add_pixbuf_formats (filter);
350
351 filters = g_list_store_new (GTK_TYPE_FILE_FILTER);
352 g_list_store_append (filters, filter);
353 gtk_file_dialog_set_filters (file_dialog, G_LIST_MODEL (filters));
354
355 pictures_folder = g_file_new_for_path (g_get_user_special_dir (G_USER_DIRECTORY_PICTURES));
356 gtk_file_dialog_set_initial_folder (file_dialog, pictures_folder);
357
358 gtk_file_dialog_open_multiple (file_dialog,
359 toplevel,
360 NULL,
361 file_dialog_open_cb,
362 self);
363 }
364
365 static void flow_box_set_active_item (GtkFlowBox *flowbox, CcBackgroundItem *active_item)
366 {
367 GtkFlowBoxChild *child = NULL;
368 CcBackgroundItem *item;
369 int idx = 0;
370
371 while ((child = gtk_flow_box_get_child_at_index (flowbox, idx++)))
372 {
373 item = g_object_get_data (G_OBJECT (child), "item");
374
375 if (cc_background_item_compare (item, active_item))
376 gtk_widget_add_css_class (GTK_WIDGET (child), "active-item");
377 else
378 gtk_widget_remove_css_class (GTK_WIDGET (child), "active-item");
379 }
380 }
381
382 void
383 cc_background_chooser_set_active_item (CcBackgroundChooser *self, CcBackgroundItem *active_item)
384 {
385 g_return_if_fail (CC_IS_BACKGROUND_CHOOSER (self));
386 g_return_if_fail (CC_IS_BACKGROUND_ITEM (active_item));
387
388 self->active_item = active_item;
389
390 flow_box_set_active_item (self->flowbox, active_item);
391 flow_box_set_active_item (self->recent_flowbox, active_item);
392 }
393