1 /*
2 * Copyright (C) 2007, 2010 The GNOME Foundation
3 * Written by Thomas Wood <thos@gnome.org>
4 * Jens Granseuer <jensgr@gmx.net>
5 * All Rights Reserved
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22 #include "appearance.h"
23 #include "theme-thumbnail.h"
24 #include "mate-theme-apply.h"
25 #include "theme-installer.h"
26 #include "theme-save.h"
27 #include "theme-util.h"
28 #include "gtkrc-utils.h"
29 #include "appearance-themes.h"
30
31 #include <glib/gi18n.h>
32 #include <libwindow-settings/mate-wm-manager.h>
33 #include <string.h>
34 #include <libmate-desktop/mate-desktop-thumbnail.h>
35 #include <libmate-desktop/mate-gsettings.h>
36
37 #define CUSTOM_THEME_NAME "__custom__"
38
39 enum {
40 RESPONSE_APPLY_BG,
41 RESPONSE_REVERT_FONT,
42 RESPONSE_APPLY_FONT,
43 RESPONSE_INSTALL_ENGINE
44 };
45
46 enum {
47 TARGET_URI_LIST,
48 TARGET_NS_URL
49 };
50
51 static const GtkTargetEntry drop_types[] =
52 {
53 {"text/uri-list", 0, TARGET_URI_LIST},
54 {"_NETSCAPE_URL", 0, TARGET_NS_URL}
55 };
56
57 static void theme_message_area_update(AppearanceData* data);
58
theme_get_mtime(const char * name)59 static time_t theme_get_mtime(const char* name)
60 {
61 MateThemeMetaInfo* theme;
62 time_t mtime = -1;
63
64 theme = mate_theme_meta_info_find(name);
65 if (theme != NULL)
66 {
67 GFile* file;
68 GFileInfo* file_info;
69
70 file = g_file_new_for_path(theme->path);
71 file_info = g_file_query_info(file, G_FILE_ATTRIBUTE_TIME_MODIFIED, G_FILE_QUERY_INFO_NONE, NULL, NULL);
72 g_object_unref(file);
73
74 if (file_info != NULL)
75 {
76 mtime = g_file_info_get_attribute_uint64(file_info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
77 g_object_unref(file_info);
78 }
79 }
80
81 return mtime;
82 }
83
theme_thumbnail_update(GdkPixbuf * pixbuf,gchar * theme_name,AppearanceData * data,gboolean cache)84 static void theme_thumbnail_update(GdkPixbuf* pixbuf, gchar* theme_name, AppearanceData* data, gboolean cache)
85 {
86 GtkTreeIter iter;
87 GtkTreeModel* model = GTK_TREE_MODEL(data->theme_store);
88
89 /* find item in model and update thumbnail */
90 if (!pixbuf)
91 return;
92
93 if (theme_find_in_model(model, theme_name, &iter))
94 {
95 time_t mtime;
96
97 gtk_list_store_set(data->theme_store, &iter, COL_THUMBNAIL, pixbuf, -1);
98
99 /* cache thumbnail */
100 if (cache && (mtime = theme_get_mtime(theme_name)) != -1)
101 {
102 gchar* path;
103
104 /* try to share thumbs with caja, use themes:/// */
105 path = g_strconcat("themes:///", theme_name, NULL);
106
107 mate_desktop_thumbnail_factory_save_thumbnail(data->thumb_factory, pixbuf, path, mtime);
108
109 g_free(path);
110 }
111 }
112 }
113
theme_get_thumbnail_from_cache(MateThemeMetaInfo * info,AppearanceData * data)114 static GdkPixbuf* theme_get_thumbnail_from_cache(MateThemeMetaInfo* info, AppearanceData* data)
115 {
116 GdkPixbuf* thumb = NULL;
117 gchar* path, *thumb_filename;
118 time_t mtime;
119
120 if (info == data->theme_custom)
121 return NULL;
122
123 mtime = theme_get_mtime(info->name);
124
125 if (mtime == -1)
126 return NULL;
127
128 /* try to share thumbs with caja, use themes:/// */
129 path = g_strconcat ("themes:///", info->name, NULL);
130 thumb_filename = mate_desktop_thumbnail_factory_lookup(data->thumb_factory, path, mtime);
131 g_free(path);
132
133 if (thumb_filename != NULL)
134 {
135 thumb = gdk_pixbuf_new_from_file(thumb_filename, NULL);
136 g_free(thumb_filename);
137 }
138
139 return thumb;
140 }
141
142 static void
theme_thumbnail_done_cb(GdkPixbuf * pixbuf,gchar * theme_name,AppearanceData * data)143 theme_thumbnail_done_cb (GdkPixbuf *pixbuf, gchar *theme_name, AppearanceData *data)
144 {
145 theme_thumbnail_update (pixbuf, theme_name, data, TRUE);
146 }
147
theme_thumbnail_generate(MateThemeMetaInfo * info,AppearanceData * data)148 static void theme_thumbnail_generate(MateThemeMetaInfo* info, AppearanceData* data)
149 {
150 GdkPixbuf* thumb = theme_get_thumbnail_from_cache(info, data);
151
152 if (thumb != NULL)
153 {
154 theme_thumbnail_update(thumb, info->name, data, FALSE);
155 g_object_unref(thumb);
156 }
157 else
158 {
159 generate_meta_theme_thumbnail_async(info, (ThemeThumbnailFunc) theme_thumbnail_done_cb, data, NULL);
160 }
161 }
162
theme_changed_on_disk_cb(MateThemeCommonInfo * theme,MateThemeChangeType change_type,MateThemeElement element_type,AppearanceData * data)163 static void theme_changed_on_disk_cb(MateThemeCommonInfo* theme, MateThemeChangeType change_type, MateThemeElement element_type, AppearanceData* data)
164 {
165 if (theme->type == MATE_THEME_TYPE_METATHEME)
166 {
167 MateThemeMetaInfo* meta = (MateThemeMetaInfo*) theme;
168
169 if (change_type == MATE_THEME_CHANGE_CREATED)
170 {
171 gtk_list_store_insert_with_values (data->theme_store, NULL, 0, COL_LABEL, meta->readable_name, COL_NAME, meta->name, COL_THUMBNAIL, data->theme_icon, -1);
172 theme_thumbnail_generate(meta, data);
173 }
174 else if (change_type == MATE_THEME_CHANGE_DELETED)
175 {
176 GtkTreeIter iter;
177
178 if (theme_find_in_model(GTK_TREE_MODEL(data->theme_store), meta->name, &iter))
179 {
180 gtk_list_store_remove(data->theme_store, &iter);
181 }
182 }
183 else if (change_type == MATE_THEME_CHANGE_CHANGED)
184 {
185 theme_thumbnail_generate(meta, data);
186 }
187 }
188 }
189
190 /* Find out if the lockdown key has been set. */
is_locked_down(void)191 static gboolean is_locked_down(void)
192 {
193 gboolean is_locked;
194 GSettings *settings;
195 settings = g_settings_new (LOCKDOWN_SCHEMA);
196 is_locked = g_settings_get_boolean (settings, DISABLE_THEMES_SETTINGS_KEY);
197 g_object_unref (settings);
198 return is_locked;
199 }
200
201 static MateThemeMetaInfo *
theme_load_from_gsettings(AppearanceData * data)202 theme_load_from_gsettings (AppearanceData *data)
203 {
204 MateThemeMetaInfo *theme;
205 gchar *scheme;
206
207 theme = mate_theme_meta_info_new ();
208
209 theme->gtk_theme_name = g_settings_get_string (data->interface_settings, GTK_THEME_KEY);
210 if (theme->gtk_theme_name == NULL)
211 theme->gtk_theme_name = g_strdup ("Menta");
212
213 scheme = g_settings_get_string (data->interface_settings, COLOR_SCHEME_KEY);
214 if (scheme == NULL || !strcmp (scheme, "")) {
215 g_free (scheme);
216 scheme = gtkrc_get_color_scheme_for_theme (theme->gtk_theme_name);
217 }
218 theme->gtk_color_scheme = scheme;
219
220 theme->marco_theme_name = g_settings_get_string (data->marco_settings, MARCO_THEME_KEY);
221 if (theme->marco_theme_name == NULL)
222 theme->marco_theme_name = g_strdup ("Menta");
223
224 theme->icon_theme_name = g_settings_get_string (data->interface_settings, ICON_THEME_KEY);
225 if (theme->icon_theme_name == NULL)
226 theme->icon_theme_name = g_strdup ("menta");
227
228 if (mate_gsettings_schema_exists (NOTIFICATION_SCHEMA)) {
229 GSettings *notification_settings;
230 notification_settings = g_settings_new (NOTIFICATION_SCHEMA);
231 theme->notification_theme_name = g_settings_get_string (notification_settings, NOTIFICATION_THEME_KEY);
232 g_object_unref (notification_settings);
233 }
234 else
235 theme->notification_theme_name = NULL;
236
237 theme->cursor_theme_name = g_settings_get_string (data->mouse_settings, CURSOR_THEME_KEY);
238 theme->cursor_size = g_settings_get_int (data->mouse_settings, CURSOR_SIZE_KEY);
239
240 if (theme->cursor_theme_name == NULL)
241 theme->cursor_theme_name = g_strdup ("default");
242
243 theme->application_font = g_settings_get_string (data->interface_settings, GTK_FONT_KEY);
244
245 return theme;
246 }
247
248 static gchar *
theme_get_selected_name(GtkIconView * icon_view,AppearanceData * data)249 theme_get_selected_name (GtkIconView *icon_view, AppearanceData *data)
250 {
251 gchar *name = NULL;
252 GList *selected = gtk_icon_view_get_selected_items (icon_view);
253
254 if (selected) {
255 GtkTreePath *path = selected->data;
256 GtkTreeModel *model = gtk_icon_view_get_model (icon_view);
257 GtkTreeIter iter;
258
259 if (gtk_tree_model_get_iter (model, &iter, path))
260 gtk_tree_model_get (model, &iter, COL_NAME, &name, -1);
261
262 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
263 g_list_free (selected);
264 }
265
266 return name;
267 }
268
269 static const MateThemeMetaInfo *
theme_get_selected(GtkIconView * icon_view,AppearanceData * data)270 theme_get_selected (GtkIconView *icon_view, AppearanceData *data)
271 {
272 MateThemeMetaInfo *theme = NULL;
273 gchar *name = theme_get_selected_name (icon_view, data);
274
275 if (name != NULL) {
276 if (!strcmp (name, data->theme_custom->name)) {
277 theme = data->theme_custom;
278 } else {
279 theme = mate_theme_meta_info_find (name);
280 }
281
282 g_free (name);
283 }
284
285 return theme;
286 }
287
288 static void
theme_select_iter(GtkIconView * icon_view,GtkTreeIter * iter)289 theme_select_iter (GtkIconView *icon_view, GtkTreeIter *iter)
290 {
291 GtkTreePath *path;
292
293 path = gtk_tree_model_get_path (gtk_icon_view_get_model (icon_view), iter);
294 gtk_icon_view_select_path (icon_view, path);
295 gtk_icon_view_scroll_to_path (icon_view, path, FALSE, 0.5, 0.0);
296 gtk_tree_path_free (path);
297 }
298
299 static void
theme_select_name(GtkIconView * icon_view,const gchar * theme)300 theme_select_name (GtkIconView *icon_view, const gchar *theme)
301 {
302 GtkTreeIter iter;
303 GtkTreeModel *model = gtk_icon_view_get_model (icon_view);
304
305 if (theme_find_in_model (model, theme, &iter))
306 theme_select_iter (icon_view, &iter);
307 }
308
309 static gboolean
theme_is_equal(const MateThemeMetaInfo * a,const MateThemeMetaInfo * b)310 theme_is_equal (const MateThemeMetaInfo *a, const MateThemeMetaInfo *b)
311 {
312 gboolean a_set, b_set;
313
314 if (!(a->gtk_theme_name && b->gtk_theme_name) ||
315 strcmp (a->gtk_theme_name, b->gtk_theme_name))
316 return FALSE;
317
318 if (!(a->icon_theme_name && b->icon_theme_name) ||
319 strcmp (a->icon_theme_name, b->icon_theme_name))
320 return FALSE;
321
322 if (!(a->marco_theme_name && b->marco_theme_name) ||
323 strcmp (a->marco_theme_name, b->marco_theme_name))
324 return FALSE;
325
326 if (!(a->cursor_theme_name && b->cursor_theme_name) ||
327 strcmp (a->cursor_theme_name, b->cursor_theme_name))
328 return FALSE;
329
330 if (a->cursor_size != b->cursor_size)
331 return FALSE;
332
333 a_set = a->gtk_color_scheme && strcmp (a->gtk_color_scheme, "");
334 b_set = b->gtk_color_scheme && strcmp (b->gtk_color_scheme, "");
335 if ((a_set != b_set) ||
336 (a_set && !mate_theme_color_scheme_equal (a->gtk_color_scheme, b->gtk_color_scheme)))
337 return FALSE;
338
339 return TRUE;
340 }
341
342 static void
theme_set_custom_from_theme(const MateThemeMetaInfo * info,AppearanceData * data)343 theme_set_custom_from_theme (const MateThemeMetaInfo *info, AppearanceData *data)
344 {
345 MateThemeMetaInfo *custom = data->theme_custom;
346 GtkIconView *icon_view = GTK_ICON_VIEW (appearance_capplet_get_widget (data, "theme_list"));
347 GtkTreeModel *model;
348 GtkTreeIter iter;
349 GtkTreePath *path;
350
351 if (info == custom)
352 return;
353
354 /* if info is not NULL, we'll copy those theme settings over */
355 if (info != NULL) {
356 g_free (custom->gtk_theme_name);
357 g_free (custom->icon_theme_name);
358 g_free (custom->marco_theme_name);
359 g_free (custom->gtk_color_scheme);
360 g_free (custom->cursor_theme_name);
361 g_free (custom->application_font);
362 custom->gtk_color_scheme = NULL;
363 custom->application_font = NULL;
364
365 /* these settings are guaranteed to be non-NULL */
366 custom->gtk_theme_name = g_strdup (info->gtk_theme_name);
367 custom->icon_theme_name = g_strdup (info->icon_theme_name);
368 custom->marco_theme_name = g_strdup (info->marco_theme_name);
369 custom->cursor_theme_name = g_strdup (info->cursor_theme_name);
370 custom->cursor_size = info->cursor_size;
371
372 /* these can be NULL */
373 if (info->gtk_color_scheme)
374 custom->gtk_color_scheme = g_strdup (info->gtk_color_scheme);
375 else
376 custom->gtk_color_scheme = g_strdup ("");
377
378 if (info->application_font)
379 custom->application_font = g_strdup (info->application_font);
380 else
381 custom->application_font = g_strdup (GTK_FONT_DEFAULT_VALUE);
382 }
383
384 /* select the custom theme */
385 model = gtk_icon_view_get_model (icon_view);
386 if (!theme_find_in_model (model, custom->name, &iter)) {
387 GtkTreeIter child;
388
389 gtk_list_store_insert_with_values (data->theme_store, &child, 0,
390 COL_LABEL, custom->readable_name,
391 COL_NAME, custom->name,
392 COL_THUMBNAIL, data->theme_icon,
393 -1);
394 gtk_tree_model_sort_convert_child_iter_to_iter (
395 GTK_TREE_MODEL_SORT (model), &iter, &child);
396 }
397
398 path = gtk_tree_model_get_path (model, &iter);
399 gtk_icon_view_select_path (icon_view, path);
400 gtk_icon_view_scroll_to_path (icon_view, path, FALSE, 0.5, 0.0);
401 gtk_tree_path_free (path);
402
403 /* update the theme thumbnail */
404 theme_thumbnail_generate (custom, data);
405 }
406
407 /** GUI Callbacks **/
408
custom_font_cb(GtkWidget * button,AppearanceData * data)409 static void custom_font_cb(GtkWidget* button, AppearanceData* data)
410 {
411 g_free(data->revert_application_font);
412 g_free(data->revert_documents_font);
413 g_free(data->revert_desktop_font);
414 g_free(data->revert_windowtitle_font);
415 g_free(data->revert_monospace_font);
416 data->revert_application_font = NULL;
417 data->revert_documents_font = NULL;
418 data->revert_desktop_font = NULL;
419 data->revert_windowtitle_font = NULL;
420 data->revert_monospace_font = NULL;
421 }
422
423 static void
theme_message_area_response_cb(GtkWidget * w,gint response_id,AppearanceData * data)424 theme_message_area_response_cb (GtkWidget *w,
425 gint response_id,
426 AppearanceData *data)
427 {
428 const MateThemeMetaInfo *theme;
429 gchar *tmpfont;
430 gchar *engine_path;
431
432 theme = theme_get_selected (GTK_ICON_VIEW (appearance_capplet_get_widget (data, "theme_list")), data);
433 if (!theme)
434 return;
435
436 switch (response_id)
437 {
438 case RESPONSE_APPLY_BG:
439 g_settings_set_string (data->wp_settings, WP_FILE_KEY, theme->background_image);
440 break;
441
442 case RESPONSE_REVERT_FONT:
443 if (data->revert_application_font != NULL) {
444 g_settings_set_string (data->interface_settings, GTK_FONT_KEY, data->revert_application_font);
445 g_free (data->revert_application_font);
446 data->revert_application_font = NULL;
447 }
448
449 if (data->revert_documents_font != NULL) {
450 g_settings_set_string (data->interface_settings, DOCUMENT_FONT_KEY, data->revert_documents_font);
451 g_free (data->revert_documents_font);
452 data->revert_documents_font = NULL;
453 }
454
455 if (data->caja_settings && data->revert_desktop_font != NULL) {
456 g_settings_set_string (data->caja_settings, DESKTOP_FONT_KEY, data->revert_desktop_font);
457 g_free (data->revert_desktop_font);
458 data->revert_desktop_font = NULL;
459 }
460
461 if (data->revert_windowtitle_font != NULL) {
462 g_settings_set_string (data->marco_settings, WINDOW_TITLE_FONT_KEY, data->revert_windowtitle_font);
463 g_free (data->revert_windowtitle_font);
464 data->revert_windowtitle_font = NULL;
465 }
466
467 if (data->revert_monospace_font != NULL) {
468 g_settings_set_string (data->interface_settings, MONOSPACE_FONT_KEY, data->revert_monospace_font);
469 g_free (data->revert_monospace_font);
470 data->revert_monospace_font = NULL;
471 }
472 break;
473
474 case RESPONSE_APPLY_FONT:
475 if (theme->application_font) {
476 tmpfont = g_settings_get_string (data->interface_settings, GTK_FONT_KEY);
477 if (tmpfont != NULL) {
478 g_free (data->revert_application_font);
479
480 if (strcmp (theme->application_font, tmpfont) == 0) {
481 g_free (tmpfont);
482 data->revert_application_font = NULL;
483 } else
484 data->revert_application_font = tmpfont;
485 }
486 g_settings_set_string (data->interface_settings, GTK_FONT_KEY, theme->application_font);
487 }
488
489 if (theme->documents_font) {
490 tmpfont = g_settings_get_string (data->interface_settings, DOCUMENT_FONT_KEY);
491 if (tmpfont != NULL) {
492 g_free (data->revert_documents_font);
493
494 if (strcmp (theme->documents_font, tmpfont) == 0) {
495 g_free (tmpfont);
496 data->revert_documents_font = NULL;
497 } else
498 data->revert_documents_font = tmpfont;
499 }
500 g_settings_set_string (data->interface_settings, DOCUMENT_FONT_KEY, theme->documents_font);
501 }
502
503 if (data->caja_settings && theme->desktop_font) {
504 tmpfont = g_settings_get_string (data->caja_settings, DESKTOP_FONT_KEY);
505 if (tmpfont != NULL) {
506 g_free (data->revert_desktop_font);
507
508 if (strcmp (theme->desktop_font, tmpfont) == 0) {
509 g_free (tmpfont);
510 data->revert_desktop_font = NULL;
511 } else
512 data->revert_desktop_font = tmpfont;
513 }
514 g_settings_set_string (data->caja_settings, DESKTOP_FONT_KEY, theme->desktop_font);
515 }
516
517 if (theme->windowtitle_font) {
518 tmpfont = g_settings_get_string (data->marco_settings, WINDOW_TITLE_FONT_KEY);
519 if (tmpfont != NULL) {
520 g_free (data->revert_windowtitle_font);
521
522 if (strcmp (theme->windowtitle_font, tmpfont) == 0) {
523 g_free (tmpfont);
524 data->revert_windowtitle_font = NULL;
525 } else
526 data->revert_windowtitle_font = tmpfont;
527 }
528 g_settings_set_string (data->marco_settings, WINDOW_TITLE_FONT_KEY, theme->windowtitle_font);
529 }
530
531 if (theme->monospace_font) {
532 tmpfont = g_settings_get_string (data->interface_settings, MONOSPACE_FONT_KEY);
533 if (tmpfont != NULL) {
534 g_free (data->revert_monospace_font);
535
536 if (strcmp (theme->monospace_font, tmpfont) == 0) {
537 g_free (tmpfont);
538 data->revert_monospace_font = NULL;
539 } else
540 data->revert_monospace_font = tmpfont;
541 }
542 g_settings_set_string (data->interface_settings, MONOSPACE_FONT_KEY, theme->monospace_font);
543 }
544 break;
545
546 case RESPONSE_INSTALL_ENGINE:
547 engine_path = gtk_theme_info_missing_engine(theme->gtk_theme_name, FALSE);
548
549 if (engine_path != NULL) {
550 theme_install_file(GTK_WINDOW(gtk_widget_get_toplevel(data->install_button)), engine_path);
551 g_free (engine_path);
552 }
553
554 theme_message_area_update(data);
555 break;
556 }
557 }
558
559 static void
theme_message_area_update(AppearanceData * data)560 theme_message_area_update (AppearanceData *data)
561 {
562 const MateThemeMetaInfo *theme;
563 gboolean show_apply_background = FALSE;
564 gboolean show_apply_font = FALSE;
565 gboolean show_revert_font = FALSE;
566 gboolean show_error;
567 const gchar *message;
568 gchar *font;
569 GError *error = NULL;
570
571 theme = theme_get_selected (GTK_ICON_VIEW (appearance_capplet_get_widget (data, "theme_list")), data);
572
573 if (!theme) {
574 if (data->theme_message_area != NULL)
575 gtk_widget_hide (data->theme_message_area);
576 return;
577 }
578
579 show_error = !mate_theme_meta_info_validate (theme, &error);
580
581 if (!show_error) {
582 if (theme->background_image != NULL) {
583 gchar *background;
584
585 background = g_settings_get_string (data->wp_settings, WP_FILE_KEY);
586 show_apply_background =
587 (!background || strcmp (theme->background_image, background) != 0);
588 g_free (background);
589 }
590
591 if (theme->application_font) {
592 font = g_settings_get_string (data->interface_settings, GTK_FONT_KEY);
593 show_apply_font =
594 (!font || strcmp (theme->application_font, font) != 0);
595 g_free (font);
596 }
597
598 if (!show_apply_font && theme->documents_font) {
599 font = g_settings_get_string (data->interface_settings, DOCUMENT_FONT_KEY);
600 show_apply_font =
601 (!font || strcmp (theme->application_font, font) != 0);
602 g_free (font);
603 }
604
605 if (data->caja_settings && !show_apply_font && theme->desktop_font) {
606 font = g_settings_get_string (data->caja_settings, DESKTOP_FONT_KEY);
607 show_apply_font =
608 (!font || strcmp (theme->application_font, font) != 0);
609 g_free (font);
610 }
611
612 if (!show_apply_font && theme->windowtitle_font) {
613 font = g_settings_get_string (data->marco_settings, WINDOW_TITLE_FONT_KEY);
614 show_apply_font =
615 (!font || strcmp (theme->application_font, font) != 0);
616 g_free (font);
617 }
618
619 if (!show_apply_font && theme->monospace_font) {
620 font = g_settings_get_string (data->interface_settings, MONOSPACE_FONT_KEY);
621 show_apply_font =
622 (!font || strcmp (theme->application_font, font) != 0);
623 g_free (font);
624 }
625
626 show_revert_font = (data->revert_application_font != NULL ||
627 data->revert_documents_font != NULL || data->revert_desktop_font != NULL ||
628 data->revert_windowtitle_font != NULL || data->revert_monospace_font != NULL);
629 }
630
631 if (data->theme_message_area == NULL) {
632 GtkWidget *hbox;
633 GtkWidget *parent;
634 GtkWidget *content;
635
636 if (!show_apply_background && !show_revert_font && !show_apply_font && !show_error)
637 return;
638
639 data->theme_message_area = gtk_info_bar_new ();
640 gtk_widget_set_no_show_all (data->theme_message_area, TRUE);
641
642 g_signal_connect (data->theme_message_area, "response",
643 (GCallback) theme_message_area_response_cb, data);
644
645 data->apply_background_button = gtk_info_bar_add_button (
646 GTK_INFO_BAR (data->theme_message_area),
647 _("Apply Background"),
648 RESPONSE_APPLY_BG);
649 data->apply_font_button = gtk_info_bar_add_button (
650 GTK_INFO_BAR (data->theme_message_area),
651 _("Apply Font"),
652 RESPONSE_APPLY_FONT);
653 data->revert_font_button = gtk_info_bar_add_button (
654 GTK_INFO_BAR (data->theme_message_area),
655 _("Revert Font"),
656 RESPONSE_REVERT_FONT);
657 data->install_button = gtk_info_bar_add_button (
658 GTK_INFO_BAR (data->theme_message_area),
659 _("Install"),
660 RESPONSE_INSTALL_ENGINE);
661
662 data->theme_message_label = gtk_label_new (NULL);
663 gtk_widget_show (data->theme_message_label);
664 gtk_label_set_line_wrap (GTK_LABEL (data->theme_message_label), TRUE);
665 gtk_label_set_xalign (GTK_LABEL (data->theme_message_label), 0.0);
666
667 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 9);
668 gtk_widget_show (hbox);
669 data->theme_info_icon = gtk_image_new_from_icon_name ("dialog-information", GTK_ICON_SIZE_DIALOG);
670 data->theme_error_icon = gtk_image_new_from_icon_name ("dialog-warning", GTK_ICON_SIZE_DIALOG);
671 gtk_widget_set_halign (data->theme_info_icon, GTK_ALIGN_CENTER);
672 gtk_widget_set_valign (data->theme_info_icon, GTK_ALIGN_START);
673 gtk_widget_set_halign (data->theme_error_icon, GTK_ALIGN_CENTER);
674 gtk_widget_set_valign (data->theme_error_icon, GTK_ALIGN_START);
675 gtk_box_pack_start (GTK_BOX (hbox), data->theme_info_icon, FALSE, FALSE, 0);
676 gtk_box_pack_start (GTK_BOX (hbox), data->theme_error_icon, FALSE, FALSE, 0);
677 gtk_box_pack_start (GTK_BOX (hbox), data->theme_message_label, TRUE, TRUE, 0);
678 content = gtk_info_bar_get_content_area (GTK_INFO_BAR (data->theme_message_area));
679 gtk_container_add (GTK_CONTAINER (content), hbox);
680
681 parent = appearance_capplet_get_widget (data, "theme_list_vbox");
682 gtk_box_pack_start (GTK_BOX (parent), data->theme_message_area, FALSE, FALSE, 0);
683 }
684
685 if (show_error)
686 message = error->message;
687 else if (show_apply_background && show_apply_font && show_revert_font)
688 message = _("The current theme suggests a background and a font. Also, the last applied font suggestion can be reverted.");
689 else if (show_apply_background && show_revert_font)
690 message = _("The current theme suggests a background. Also, the last applied font suggestion can be reverted.");
691 else if (show_apply_background && show_apply_font)
692 message = _("The current theme suggests a background and a font.");
693 else if (show_apply_font && show_revert_font)
694 message = _("The current theme suggests a font. Also, the last applied font suggestion can be reverted.");
695 else if (show_apply_background)
696 message = _("The current theme suggests a background.");
697 else if (show_revert_font)
698 message = _("The last applied font suggestion can be reverted.");
699 else if (show_apply_font)
700 message = _("The current theme suggests a font.");
701 else
702 message = NULL;
703
704 if (show_apply_background)
705 gtk_widget_show (data->apply_background_button);
706 else
707 gtk_widget_hide (data->apply_background_button);
708
709 if (show_apply_font)
710 gtk_widget_show (data->apply_font_button);
711 else
712 gtk_widget_hide (data->apply_font_button);
713
714 if (show_revert_font)
715 gtk_widget_show (data->revert_font_button);
716 else
717 gtk_widget_hide (data->revert_font_button);
718
719 if (show_error
720 && g_error_matches (error, MATE_THEME_ERROR, MATE_THEME_ERROR_GTK_ENGINE_NOT_AVAILABLE)
721 && packagekit_available ())
722 gtk_widget_show (data->install_button);
723 else
724 gtk_widget_hide (data->install_button);
725
726 if (show_error || show_apply_background || show_apply_font || show_revert_font) {
727 gtk_widget_show (data->theme_message_area);
728 gtk_widget_queue_draw (data->theme_message_area);
729
730 if (show_error) {
731 gtk_widget_show (data->theme_error_icon);
732 gtk_widget_hide (data->theme_info_icon);
733 } else {
734 gtk_widget_show (data->theme_info_icon);
735 gtk_widget_hide (data->theme_error_icon);
736 }
737 } else {
738 gtk_widget_hide (data->theme_message_area);
739 }
740
741 gtk_label_set_text (GTK_LABEL (data->theme_message_label), message);
742 g_clear_error (&error);
743 }
744
745 static void
theme_selection_changed_cb(GtkWidget * icon_view,AppearanceData * data)746 theme_selection_changed_cb (GtkWidget *icon_view, AppearanceData *data)
747 {
748 GList *selection;
749 MateThemeMetaInfo *theme = NULL;
750 gboolean is_custom = FALSE;
751
752 selection = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (icon_view));
753
754 if (selection) {
755 GtkTreeModel *model;
756 GtkTreeIter iter;
757 gchar *name;
758
759 model = gtk_icon_view_get_model (GTK_ICON_VIEW (icon_view));
760 gtk_tree_model_get_iter (model, &iter, selection->data);
761 gtk_tree_model_get (model, &iter, COL_NAME, &name, -1);
762
763 is_custom = !strcmp (name, CUSTOM_THEME_NAME);
764
765 if (is_custom)
766 theme = data->theme_custom;
767 else
768 theme = mate_theme_meta_info_find (name);
769
770 if (theme) {
771 mate_meta_theme_set (theme);
772 theme_message_area_update (data);
773 }
774
775 g_free (name);
776 g_list_foreach (selection, (GFunc) gtk_tree_path_free, NULL);
777 g_list_free (selection);
778
779 gtk_widget_set_sensitive (appearance_capplet_get_widget (data, "theme_delete"),
780 theme_is_writable (theme));
781 gtk_widget_set_sensitive (appearance_capplet_get_widget (data, "theme_save"), is_custom);
782 }
783 }
784
785 static void
theme_custom_cb(GtkWidget * button,AppearanceData * data)786 theme_custom_cb (GtkWidget *button, AppearanceData *data)
787 {
788 GtkWidget *w;
789
790 w = appearance_capplet_get_widget (data, "theme_details");
791 gtk_window_set_transient_for (GTK_WINDOW (w), GET_WINDOW ("appearance_window"));
792 gtk_widget_show_all (w);
793 }
794
795 static void
theme_save_cb(GtkWidget * button,AppearanceData * data)796 theme_save_cb (GtkWidget *button, AppearanceData *data)
797 {
798 theme_save_dialog_run (data->theme_custom, data);
799 }
800
801 static void
theme_install_cb(GtkWidget * button,AppearanceData * data)802 theme_install_cb (GtkWidget *button, AppearanceData *data)
803 {
804 mate_theme_installer_run (GET_WINDOW ("appearance_window"));
805 }
806
807 static void
theme_delete_cb(GtkWidget * button,AppearanceData * data)808 theme_delete_cb (GtkWidget *button, AppearanceData *data)
809 {
810 GtkIconView *icon_view = GTK_ICON_VIEW (appearance_capplet_get_widget (data, "theme_list"));
811 GList *selected = gtk_icon_view_get_selected_items (icon_view);
812
813 if (selected) {
814 GtkTreePath *path = selected->data;
815 GtkTreeModel *model = gtk_icon_view_get_model (icon_view);
816 GtkTreeIter iter;
817 gchar *name = NULL;
818
819 if (gtk_tree_model_get_iter (model, &iter, path))
820 gtk_tree_model_get (model, &iter, COL_NAME, &name, -1);
821
822 if (name != NULL &&
823 strcmp (name, data->theme_custom->name) &&
824 theme_delete (name, THEME_TYPE_META)) {
825 /* remove theme from the model, too */
826 GtkTreeIter child;
827
828 if (gtk_tree_model_iter_next (model, &iter) ||
829 theme_model_iter_last (model, &iter))
830 theme_select_iter (icon_view, &iter);
831
832 gtk_tree_model_get_iter (model, &iter, path);
833 gtk_tree_model_sort_convert_iter_to_child_iter (
834 GTK_TREE_MODEL_SORT (model), &child, &iter);
835 gtk_list_store_remove (data->theme_store, &child);
836 }
837
838 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
839 g_list_free (selected);
840 g_free (name);
841 }
842 }
843
844 static void
theme_details_changed_cb(AppearanceData * data)845 theme_details_changed_cb (AppearanceData *data)
846 {
847 MateThemeMetaInfo *gsettings_theme;
848 const MateThemeMetaInfo *selected;
849 GtkIconView *icon_view;
850 gboolean done = FALSE;
851
852 /* load new state from gsettings */
853 gsettings_theme = theme_load_from_gsettings (data);
854
855 /* check if it's our currently selected theme */
856 icon_view = GTK_ICON_VIEW (appearance_capplet_get_widget (data, "theme_list"));
857 selected = theme_get_selected (icon_view, data);
858
859 if (!selected || !(done = theme_is_equal (selected, gsettings_theme))) {
860 /* look for a matching metatheme */
861 GList *theme_list, *l;
862
863 theme_list = mate_theme_meta_info_find_all ();
864
865 for (l = theme_list; l; l = l->next) {
866 MateThemeMetaInfo *info = l->data;
867
868 if (theme_is_equal (gsettings_theme, info)) {
869 theme_select_name (icon_view, info->name);
870 done = TRUE;
871 break;
872 }
873 }
874 g_list_free (theme_list);
875 }
876
877 if (!done)
878 /* didn't find a match, set or update custom */
879 theme_set_custom_from_theme (gsettings_theme, data);
880
881 mate_theme_meta_info_free (gsettings_theme);
882 }
883
884 static void
theme_setting_changed_cb(GObject * settings,GParamSpec * pspec,AppearanceData * data)885 theme_setting_changed_cb (GObject *settings,
886 GParamSpec *pspec,
887 AppearanceData *data)
888 {
889 theme_details_changed_cb (data);
890 }
891
892 static void
theme_gsettings_changed(GSettings * settings,gchar * key,AppearanceData * data)893 theme_gsettings_changed (GSettings *settings,
894 gchar *key,
895 AppearanceData *data)
896 {
897 theme_details_changed_cb (data);
898 }
899
900 static gint
theme_list_sort_func(MateThemeMetaInfo * a,MateThemeMetaInfo * b)901 theme_list_sort_func (MateThemeMetaInfo *a,
902 MateThemeMetaInfo *b)
903 {
904 return strcmp (a->readable_name, b->readable_name);
905 }
906
907 static gint
theme_store_sort_func(GtkTreeModel * model,GtkTreeIter * a,GtkTreeIter * b,gpointer user_data)908 theme_store_sort_func (GtkTreeModel *model,
909 GtkTreeIter *a,
910 GtkTreeIter *b,
911 gpointer user_data)
912 {
913 gchar *a_name, *a_label;
914 gint rc;
915
916 gtk_tree_model_get (model, a, COL_NAME, &a_name, COL_LABEL, &a_label, -1);
917
918 if (!strcmp (a_name, CUSTOM_THEME_NAME)) {
919 rc = -1;
920 } else {
921 gchar *b_name, *b_label;
922
923 gtk_tree_model_get (model, b, COL_NAME, &b_name, COL_LABEL, &b_label, -1);
924
925 if (!strcmp (b_name, CUSTOM_THEME_NAME)) {
926 rc = 1;
927 } else {
928 gchar *a_case, *b_case;
929
930 a_case = g_utf8_casefold (a_label, -1);
931 b_case = g_utf8_casefold (b_label, -1);
932 rc = g_utf8_collate (a_case, b_case);
933 g_free (a_case);
934 g_free (b_case);
935 }
936
937 g_free (b_name);
938 g_free (b_label);
939 }
940
941 g_free (a_name);
942 g_free (a_label);
943
944 return rc;
945 }
946
947 static void
theme_drag_data_received_cb(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint time,AppearanceData * data)948 theme_drag_data_received_cb (GtkWidget *widget,
949 GdkDragContext *context,
950 gint x, gint y,
951 GtkSelectionData *selection_data,
952 guint info, guint time,
953 AppearanceData *data)
954 {
955 gchar **uris;
956
957 if (!(info == TARGET_URI_LIST || info == TARGET_NS_URL))
958 return;
959
960 uris = g_uri_list_extract_uris ((gchar *) gtk_selection_data_get_data (selection_data));
961
962 if (uris != NULL && uris[0] != NULL) {
963 GFile *f = g_file_new_for_uri (uris[0]);
964
965 mate_theme_install (f, GET_WINDOW ("appearance_window"));
966 g_object_unref (f);
967 }
968
969 g_strfreev (uris);
970 }
971
background_or_font_changed(GSettings * settings,gchar * key,AppearanceData * data)972 static void background_or_font_changed(GSettings *settings, gchar *key, AppearanceData* data)
973 {
974 theme_message_area_update(data);
975 }
976
themes_init(AppearanceData * data)977 void themes_init(AppearanceData* data)
978 {
979 GtkWidget *w, *del_button;
980 GList *theme_list, *l;
981 GtkListStore *theme_store;
982 GtkTreeModel *sort_model;
983 MateThemeMetaInfo *meta_theme = NULL;
984 GtkIconView *icon_view;
985 GtkCellRenderer *renderer;
986 GtkSettings *settings;
987 char *url;
988
989 /* initialise some stuff */
990 mate_theme_init ();
991 mate_wm_manager_init ();
992
993 data->revert_application_font = NULL;
994 data->revert_documents_font = NULL;
995 data->revert_desktop_font = NULL;
996 data->revert_windowtitle_font = NULL;
997 data->revert_monospace_font = NULL;
998 data->theme_save_dialog = NULL;
999 data->theme_message_area = NULL;
1000 data->theme_info_icon = NULL;
1001 data->theme_error_icon = NULL;
1002 data->theme_icon = gdk_pixbuf_new_from_file (MATECC_PIXMAP_DIR "/theme-thumbnailing.png", NULL);
1003 data->theme_store = theme_store =
1004 gtk_list_store_new (NUM_COLS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
1005
1006 /* set up theme list */
1007 theme_list = mate_theme_meta_info_find_all ();
1008 mate_theme_info_register_theme_change ((ThemeChangedCallback) theme_changed_on_disk_cb, data);
1009
1010 data->theme_custom = theme_load_from_gsettings (data);
1011 data->theme_custom->name = g_strdup (CUSTOM_THEME_NAME);
1012 data->theme_custom->readable_name = g_strdup_printf ("<i>%s</i>", _("Custom"));
1013
1014 for (l = theme_list; l; l = l->next) {
1015 MateThemeMetaInfo *info = l->data;
1016
1017 gtk_list_store_insert_with_values (theme_store, NULL, 0,
1018 COL_LABEL, info->readable_name,
1019 COL_NAME, info->name,
1020 COL_THUMBNAIL, data->theme_icon,
1021 -1);
1022
1023 if (!meta_theme && theme_is_equal (data->theme_custom, info))
1024 meta_theme = info;
1025 }
1026
1027 if (!meta_theme) {
1028 /* add custom theme */
1029 meta_theme = data->theme_custom;
1030
1031 gtk_list_store_insert_with_values (theme_store, NULL, 0,
1032 COL_LABEL, meta_theme->readable_name,
1033 COL_NAME, meta_theme->name,
1034 COL_THUMBNAIL, data->theme_icon,
1035 -1);
1036
1037 theme_thumbnail_generate (meta_theme, data);
1038 }
1039
1040 theme_list = g_list_sort (theme_list, (GCompareFunc) theme_list_sort_func);
1041
1042 g_list_foreach (theme_list, (GFunc) theme_thumbnail_generate, data);
1043 g_list_free (theme_list);
1044
1045 icon_view = GTK_ICON_VIEW (appearance_capplet_get_widget (data, "theme_list"));
1046
1047 renderer = gtk_cell_renderer_pixbuf_new ();
1048 g_object_set (renderer, "xpad", 5, "ypad", 5,
1049 "xalign", 0.5, "yalign", 1.0, NULL);
1050 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (icon_view), renderer, FALSE);
1051 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (icon_view), renderer,
1052 "pixbuf", COL_THUMBNAIL, NULL);
1053
1054 renderer = gtk_cell_renderer_text_new ();
1055 g_object_set (renderer, "alignment", PANGO_ALIGN_CENTER,
1056 "wrap-mode", PANGO_WRAP_WORD_CHAR,
1057 "wrap-width", gtk_icon_view_get_item_width (icon_view),
1058 "width", gtk_icon_view_get_item_width (icon_view),
1059 "xalign", 0.5, "yalign", 0.0, NULL);
1060
1061 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (icon_view), renderer, FALSE);
1062 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (icon_view), renderer,
1063 "markup", COL_LABEL, NULL);
1064
1065 sort_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (theme_store));
1066 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sort_model), COL_LABEL, theme_store_sort_func, NULL, NULL);
1067 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model), COL_LABEL, GTK_SORT_ASCENDING);
1068 gtk_icon_view_set_model (icon_view, GTK_TREE_MODEL (sort_model));
1069
1070 g_signal_connect (icon_view, "selection-changed", (GCallback) theme_selection_changed_cb, data);
1071 g_signal_connect_after (icon_view, "realize", (GCallback) theme_select_name, meta_theme->name);
1072
1073 w = appearance_capplet_get_widget (data, "theme_install");
1074 g_signal_connect (w, "clicked", (GCallback) theme_install_cb, data);
1075
1076 w = appearance_capplet_get_widget (data, "theme_save");
1077 g_signal_connect (w, "clicked", (GCallback) theme_save_cb, data);
1078
1079 w = appearance_capplet_get_widget (data, "theme_custom");
1080 g_signal_connect (w, "clicked", (GCallback) theme_custom_cb, data);
1081
1082 del_button = appearance_capplet_get_widget (data, "theme_delete");
1083 g_signal_connect (del_button, "clicked", (GCallback) theme_delete_cb, data);
1084
1085 w = appearance_capplet_get_widget (data, "theme_vbox");
1086 gtk_drag_dest_set (w, GTK_DEST_DEFAULT_ALL,
1087 drop_types, G_N_ELEMENTS (drop_types),
1088 GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_MOVE);
1089 g_signal_connect (w, "drag-data-received", (GCallback) theme_drag_data_received_cb, data);
1090 if (is_locked_down ())
1091 gtk_widget_set_sensitive (w, FALSE);
1092
1093 w = appearance_capplet_get_widget (data, "more_themes_linkbutton");
1094 url = g_settings_get_string (data->settings, MORE_THEMES_URL_KEY);
1095 if (url != NULL && url[0] != '\0') {
1096 gtk_link_button_set_uri (GTK_LINK_BUTTON (w), url);
1097 gtk_widget_show (w);
1098 } else {
1099 gtk_widget_hide (w);
1100 }
1101 g_free (url);
1102
1103 /* listen to gsettings changes, too */
1104 g_signal_connect (data->marco_settings, "changed::" MARCO_THEME_KEY, G_CALLBACK (theme_gsettings_changed), data);
1105 g_signal_connect (data->mouse_settings, "changed::" CURSOR_THEME_KEY, G_CALLBACK (theme_gsettings_changed), data);
1106 g_signal_connect (data->mouse_settings, "changed::" CURSOR_SIZE_KEY, G_CALLBACK (theme_gsettings_changed), data);
1107 g_signal_connect (data->wp_settings, "changed::" WP_FILE_KEY, G_CALLBACK (background_or_font_changed), data);
1108 g_signal_connect (data->interface_settings, "changed::" GTK_FONT_KEY, G_CALLBACK (background_or_font_changed), data);
1109 g_signal_connect (data->interface_settings, "changed::" DOCUMENT_FONT_KEY, G_CALLBACK (background_or_font_changed), data);
1110
1111 if (data->caja_settings)
1112 g_signal_connect (data->caja_settings, "changed::" DESKTOP_FONT_KEY, G_CALLBACK (background_or_font_changed), data);
1113
1114 g_signal_connect (data->marco_settings, "changed::" WINDOW_TITLE_FONT_KEY, G_CALLBACK (background_or_font_changed), data);
1115 g_signal_connect (data->interface_settings, "changed::" MONOSPACE_FONT_KEY, G_CALLBACK (background_or_font_changed), data);
1116
1117 settings = gtk_settings_get_default ();
1118 g_signal_connect (settings, "notify::gtk-color-scheme", (GCallback) theme_setting_changed_cb, data);
1119 g_signal_connect (settings, "notify::gtk-theme-name", (GCallback) theme_setting_changed_cb, data);
1120 g_signal_connect (settings, "notify::gtk-icon-theme-name", (GCallback) theme_setting_changed_cb, data);
1121
1122 /* monitor individual font choice buttons, so
1123 "revert font" option (if any) can be cleared */
1124 w = appearance_capplet_get_widget (data, "application_font");
1125 g_signal_connect (w, "font_set", (GCallback) custom_font_cb, data);
1126 w = appearance_capplet_get_widget (data, "document_font");
1127 g_signal_connect (w, "font_set", (GCallback) custom_font_cb, data);
1128 w = appearance_capplet_get_widget (data, "desktop_font");
1129 g_signal_connect (w, "font_set", (GCallback) custom_font_cb, data);
1130 w = appearance_capplet_get_widget (data, "window_title_font");
1131 g_signal_connect (w, "font_set", (GCallback) custom_font_cb, data);
1132 w = appearance_capplet_get_widget (data, "monospace_font");
1133 g_signal_connect (w, "font_set", (GCallback) custom_font_cb, data);
1134 }
1135
1136 void
themes_shutdown(AppearanceData * data)1137 themes_shutdown (AppearanceData *data)
1138 {
1139 mate_theme_meta_info_free (data->theme_custom);
1140
1141 if (data->theme_icon)
1142 g_object_unref (data->theme_icon);
1143 if (data->theme_save_dialog)
1144 gtk_widget_destroy (data->theme_save_dialog);
1145 g_free (data->revert_application_font);
1146 g_free (data->revert_documents_font);
1147 g_free (data->revert_desktop_font);
1148 g_free (data->revert_windowtitle_font);
1149 g_free (data->revert_monospace_font);
1150 }
1151