1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
2
3 /*
4 caja-directory-background.c: Helper for the background of a widget
5 that is viewing a particular location.
6
7 Copyright (C) 2000 Eazel, Inc.
8 Copyright (C) 2012 Jasmine Hassan <jasmine.aura@gmail.com>
9 Copyright (C) 2012-2021 The MATE developers
10
11 This program is free software; you can redistribute it and/or
12 modify it under the terms of the GNU General Public License as
13 published by the Free Software Foundation; either version 2 of the
14 License, or (at your option) any later version.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 General Public License for more details.
20
21 You should have received a copy of the GNU General Public
22 License along with this program; if not, write to the
23 Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
24 Boston, MA 02110-1301, USA.
25
26 Authors: Darin Adler <darin@bentspoon.com>
27 Jasmine Hassan <jasmine.aura@gmail.com>
28 */
29
30 #include <config.h>
31 #include <gtk/gtk.h>
32 #include <string.h>
33
34 #include <eel/eel-gdk-extensions.h>
35 #include <eel/eel-gtk-extensions.h>
36 #include <eel/eel-background.h>
37
38 #include "caja-directory-background.h"
39 #include "caja-dnd.h"
40 #include "caja-global-preferences.h"
41 #include "caja-metadata.h"
42 #include "caja-file-attributes.h"
43
44 static void caja_background_changed_cb (EelBackground *background,
45 GdkDragAction action,
46 CajaFile *file);
47
48 static void
caja_background_get_default_settings(char ** color,char ** image)49 caja_background_get_default_settings (char **color,
50 char **image)
51 {
52 gboolean background_set;
53
54 background_set = g_settings_get_boolean (caja_preferences, CAJA_PREFERENCES_BACKGROUND_SET);
55
56 if (background_set && color)
57 *color = g_settings_get_string (caja_preferences, CAJA_PREFERENCES_BACKGROUND_COLOR);
58
59 if (background_set && image)
60 *image = g_settings_get_string (caja_preferences, CAJA_PREFERENCES_BACKGROUND_URI);
61 }
62
63 static void
caja_background_load_from_file_metadata(CajaFile * file,EelBackground * background)64 caja_background_load_from_file_metadata (CajaFile *file,
65 EelBackground *background)
66 {
67 char *color, *image;
68
69 g_assert (EEL_IS_BACKGROUND (background));
70 g_assert (CAJA_IS_FILE (file));
71 g_assert (g_object_get_data (G_OBJECT (background), "eel_background_file") == file);
72
73 color = caja_file_get_metadata (file, CAJA_METADATA_KEY_LOCATION_BACKGROUND_COLOR, NULL);
74 image = caja_file_get_metadata (file, CAJA_METADATA_KEY_LOCATION_BACKGROUND_IMAGE, NULL);
75
76 /* if there's none, read the default from the theme */
77 if (color == NULL && image == NULL)
78 caja_background_get_default_settings (&color, &image);
79
80 /* Block the other handler while we are responding to changes
81 * in the metadata so it doesn't try to change the metadata.
82 */
83 g_signal_handlers_block_by_func (background, G_CALLBACK (caja_background_changed_cb), file);
84
85 eel_background_set_color (background, color);
86 /* non-tiled only available for desktop, at least for now */
87 eel_bg_set_placement (background, MATE_BG_PLACEMENT_TILED);
88 eel_background_set_image_uri (background, image);
89
90 /* Unblock the handler. */
91 g_signal_handlers_unblock_by_func (background, G_CALLBACK (caja_background_changed_cb), file);
92
93 g_free (color);
94 g_free (image);
95 }
96
97 /* handle the file changed signal */
98 static void
caja_background_settings_notify_cb(CajaFile * file,EelBackground * background)99 caja_background_settings_notify_cb (CajaFile *file,
100 EelBackground *background)
101 {
102 caja_background_load_from_file_metadata (file, background);
103 }
104
105 /* handle the theme changing */
106 static void
caja_background_theme_notify_cb(GSettings * settings,const gchar * key,gpointer user_data)107 caja_background_theme_notify_cb (GSettings *settings,
108 const gchar *key,
109 gpointer user_data)
110 {
111 CajaFile *file;
112 EelBackground *background = EEL_BACKGROUND (user_data);
113
114 file = g_object_get_data (G_OBJECT (background), "eel_background_file");
115
116 if (file)
117 caja_background_settings_notify_cb (file, background);
118 }
119
120 /* handle the background changed signal */
121 static void
caja_background_changed_cb(EelBackground * background,GdkDragAction action,CajaFile * file)122 caja_background_changed_cb (EelBackground *background,
123 GdkDragAction action,
124 CajaFile *file)
125 {
126 g_assert (EEL_IS_BACKGROUND (background));
127 g_assert (CAJA_IS_FILE (file));
128 g_assert (g_object_get_data (G_OBJECT (background), "eel_background_file") == file);
129
130 char *color = eel_background_get_color (background);
131 char *image = eel_background_get_image_uri (background);
132
133 /* Block the other handler while we are writing metadata so it doesn't
134 * try to change the background.
135 */
136 g_signal_handlers_block_by_func (file, G_CALLBACK (caja_background_settings_notify_cb),
137 background);
138
139 if (action != (GdkDragAction) CAJA_DND_ACTION_SET_AS_FOLDER_BACKGROUND &&
140 action != (GdkDragAction) CAJA_DND_ACTION_SET_AS_GLOBAL_BACKGROUND)
141 {
142 action = (GdkDragAction) GPOINTER_TO_INT (g_object_get_data (G_OBJECT (background),
143 "default_drag_action"));
144 }
145
146 if (action == (GdkDragAction) CAJA_DND_ACTION_SET_AS_GLOBAL_BACKGROUND)
147 {
148 caja_file_set_metadata (file, CAJA_METADATA_KEY_LOCATION_BACKGROUND_COLOR, NULL, NULL);
149 caja_file_set_metadata (file, CAJA_METADATA_KEY_LOCATION_BACKGROUND_IMAGE, NULL, NULL);
150
151 g_signal_handlers_block_by_func (caja_preferences,
152 G_CALLBACK (caja_background_theme_notify_cb),
153 background);
154
155 g_settings_set_string (caja_preferences,
156 CAJA_PREFERENCES_BACKGROUND_COLOR, color ? color : "");
157 g_settings_set_string (caja_preferences,
158 CAJA_PREFERENCES_BACKGROUND_URI, image ? image : "");
159
160 g_settings_set_boolean (caja_preferences, CAJA_PREFERENCES_BACKGROUND_SET, TRUE);
161
162 g_signal_handlers_unblock_by_func (caja_preferences,
163 G_CALLBACK (caja_background_theme_notify_cb),
164 background);
165 } else {
166 caja_file_set_metadata (file, CAJA_METADATA_KEY_LOCATION_BACKGROUND_COLOR, NULL, color);
167 caja_file_set_metadata (file, CAJA_METADATA_KEY_LOCATION_BACKGROUND_IMAGE, NULL, image);
168 }
169
170 /* Unblock the handler. */
171 g_signal_handlers_unblock_by_func (file, G_CALLBACK (caja_background_settings_notify_cb),
172 background);
173
174 g_free (color);
175 g_free (image);
176 }
177
178 /* handle the background reset signal by setting values from the current theme */
179 static void
caja_background_reset_cb(EelBackground * background,CajaFile * file)180 caja_background_reset_cb (EelBackground *background,
181 CajaFile *file)
182 {
183 char *color, *image;
184
185 /* Block the other handler while we are writing metadata so it doesn't
186 * try to change the background.
187 */
188 g_signal_handlers_block_by_func (file, G_CALLBACK (caja_background_settings_notify_cb),
189 background);
190
191 color = caja_file_get_metadata (file, CAJA_METADATA_KEY_LOCATION_BACKGROUND_COLOR, NULL);
192 image = caja_file_get_metadata (file, CAJA_METADATA_KEY_LOCATION_BACKGROUND_IMAGE, NULL);
193 if (!color && !image)
194 {
195 g_signal_handlers_block_by_func (caja_preferences,
196 G_CALLBACK (caja_background_theme_notify_cb),
197 background);
198 g_settings_set_boolean (caja_preferences, CAJA_PREFERENCES_BACKGROUND_SET, FALSE);
199 g_signal_handlers_unblock_by_func (caja_preferences,
200 G_CALLBACK (caja_background_theme_notify_cb),
201 background);
202 }
203 else
204 {
205 /* reset the metadata */
206 caja_file_set_metadata (file, CAJA_METADATA_KEY_LOCATION_BACKGROUND_COLOR, NULL, NULL);
207 caja_file_set_metadata (file, CAJA_METADATA_KEY_LOCATION_BACKGROUND_IMAGE, NULL, NULL);
208 }
209 g_free (color);
210 g_free (image);
211
212 /* Unblock the handler. */
213 g_signal_handlers_unblock_by_func (file, G_CALLBACK (caja_background_settings_notify_cb),
214 background);
215
216 caja_background_settings_notify_cb (file, background);
217 }
218
219 /* handle the background destroyed signal */
220 static void
caja_background_weak_notify(gpointer data,GObject * background)221 caja_background_weak_notify (gpointer data,
222 GObject *background)
223 {
224 CajaFile *file = CAJA_FILE (data);
225
226 g_signal_handlers_disconnect_by_func (file, G_CALLBACK (caja_background_settings_notify_cb),
227 background);
228 caja_file_monitor_remove (file, background);
229 g_signal_handlers_disconnect_by_func (caja_preferences, caja_background_theme_notify_cb,
230 background);
231 }
232
233 /* key routine that hooks up a background and location */
234 void
caja_connect_background_to_file_metadata(GtkWidget * widget,CajaFile * file,GdkDragAction default_drag_action)235 caja_connect_background_to_file_metadata (GtkWidget *widget,
236 CajaFile *file,
237 GdkDragAction default_drag_action)
238 {
239 EelBackground *background;
240 gpointer old_file;
241
242 /* Get at the background object we'll be connecting. */
243 background = eel_get_widget_background (widget);
244
245 /* Check if it is already connected. */
246 old_file = g_object_get_data (G_OBJECT (background), "eel_background_file");
247 if (old_file == file)
248 return;
249
250 /* Disconnect old signal handlers. */
251 if (old_file != NULL)
252 {
253 g_assert (CAJA_IS_FILE (old_file));
254
255 g_signal_handlers_disconnect_by_func (background,
256 G_CALLBACK (caja_background_changed_cb), old_file);
257 g_signal_handlers_disconnect_by_func (background,
258 G_CALLBACK (caja_background_reset_cb), old_file);
259
260 g_object_weak_unref (G_OBJECT (background), caja_background_weak_notify, old_file);
261
262 g_signal_handlers_disconnect_by_func (old_file,
263 G_CALLBACK (caja_background_settings_notify_cb),
264 background);
265
266 caja_file_monitor_remove (old_file, background);
267
268 g_signal_handlers_disconnect_by_func (caja_preferences, caja_background_theme_notify_cb,
269 background);
270 }
271
272 /* Attach the new directory. */
273 caja_file_ref (file);
274 g_object_set_data_full (G_OBJECT (background), "eel_background_file",
275 file, (GDestroyNotify) caja_file_unref);
276
277 g_object_set_data (G_OBJECT (background), "default_drag_action",
278 GINT_TO_POINTER (default_drag_action));
279
280 /* Connect new signal handlers. */
281 if (file != NULL)
282 {
283 g_signal_connect_object (background, "settings_changed",
284 G_CALLBACK (caja_background_changed_cb), file, 0);
285
286 g_signal_connect_object (background, "reset",
287 G_CALLBACK (caja_background_reset_cb), file, 0);
288
289 g_signal_connect_object (file, "changed",
290 G_CALLBACK (caja_background_settings_notify_cb), background, 0);
291
292 g_object_weak_ref (G_OBJECT (background), caja_background_weak_notify, file);
293
294 /* arrange to receive file metadata */
295 caja_file_monitor_add (file, background, CAJA_FILE_ATTRIBUTE_INFO);
296
297 /* arrange for notification when the theme changes */
298 g_signal_connect (caja_preferences, "changed::" CAJA_PREFERENCES_BACKGROUND_SET,
299 G_CALLBACK(caja_background_theme_notify_cb), background);
300 g_signal_connect (caja_preferences, "changed::" CAJA_PREFERENCES_BACKGROUND_COLOR,
301 G_CALLBACK(caja_background_theme_notify_cb), background);
302 g_signal_connect (caja_preferences, "changed::" CAJA_PREFERENCES_BACKGROUND_URI,
303 G_CALLBACK(caja_background_theme_notify_cb), background);
304 }
305
306 /* Update the background based on the file metadata. */
307 caja_background_load_from_file_metadata (file, background);
308 }
309
310 /**
311 * DESKTOP BACKGROUND HANDLING
312 */
313
314 /* handle the desktop background "settings_changed" signal */
315 static void
desktop_background_changed_cb(EelBackground * background,GdkDragAction action,gpointer user_data)316 desktop_background_changed_cb (EelBackground *background,
317 GdkDragAction action,
318 gpointer user_data)
319 {
320 eel_bg_save_to_gsettings (background,
321 mate_background_preferences);
322 }
323
324 /* delayed initializor of desktop background after GSettings changes */
325 static gboolean
desktop_background_prefs_change_event_idle_cb(EelBackground * background)326 desktop_background_prefs_change_event_idle_cb (EelBackground *background)
327 {
328 gchar *desktop_color = NULL;
329
330 eel_bg_load_from_gsettings (background,
331 mate_background_preferences);
332
333 desktop_color = eel_bg_get_desktop_color (background);
334 eel_background_set_color (background, desktop_color);
335
336 g_free(desktop_color);
337 g_object_unref (background);
338
339 return FALSE; /* remove from the list of event sources */
340 }
341
342 /* handle the desktop background "reset" signal: reset to schema's defaults */
343 static void
desktop_background_reset_cb(EelBackground * background,gpointer user_data)344 desktop_background_reset_cb (EelBackground *background,
345 gpointer user_data)
346 {
347 /* Reset to defaults, and save */
348 eel_bg_load_from_system_gsettings (background,
349 mate_background_preferences,
350 TRUE);
351 /* Reload from saved settings */
352 g_idle_add ((GSourceFunc) desktop_background_prefs_change_event_idle_cb,
353 g_object_ref (background));
354 }
355
356 /* handle the desktop GSettings "change-event" (batch changes) signal */
357 static gboolean
desktop_background_prefs_change_event_cb(GSettings * settings,gpointer keys,gint n_keys,gpointer user_data)358 desktop_background_prefs_change_event_cb (GSettings *settings,
359 gpointer keys,
360 gint n_keys,
361 gpointer user_data)
362 {
363 EelBackground *background = user_data;
364
365 /* Defer signal processing to avoid making the dconf backend deadlock, and
366 * hold a ref to avoid accessing fields of an object that was destroyed.
367 */
368 g_idle_add ((GSourceFunc) desktop_background_prefs_change_event_idle_cb,
369 g_object_ref (background));
370
371 return FALSE; /* let the event propagate further */
372 }
373
374 static void
desktop_background_weak_notify(gpointer data,GObject * object)375 desktop_background_weak_notify (gpointer data,
376 GObject *object)
377 {
378 g_signal_handlers_disconnect_by_func (mate_background_preferences,
379 G_CALLBACK (desktop_background_prefs_change_event_cb),
380 object);
381 }
382
383 void
caja_connect_desktop_background_to_settings(CajaIconContainer * icon_container)384 caja_connect_desktop_background_to_settings (CajaIconContainer *icon_container)
385 {
386 EelBackground *background;
387
388 background = eel_get_widget_background (GTK_WIDGET (icon_container));
389
390 eel_background_set_desktop (background, TRUE);
391
392 g_signal_connect_object (background, "settings_changed",
393 G_CALLBACK (desktop_background_changed_cb), NULL, 0);
394
395 g_signal_connect_object (background, "reset",
396 G_CALLBACK (desktop_background_reset_cb), NULL, 0);
397
398 eel_bg_load_from_gsettings (background,
399 mate_background_preferences);
400
401 /* Connect to "change-event" signal to receive *groups of changes* before
402 * they are split out into multiple emissions of the "changed" signal.
403 */
404 g_signal_connect (mate_background_preferences, "change-event",
405 G_CALLBACK (desktop_background_prefs_change_event_cb),
406 background);
407
408 g_object_weak_ref (G_OBJECT (background),
409 desktop_background_weak_notify, NULL);
410 }
411