Merge branch 'master' of ssh://git.claws-mail.org/home/git/claws
[claws.git] / src / prefs_themes.c
1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2003-2015 Hiroyuki Yamamoto & the Claws Mail team
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 #ifdef HAVE_CONFIG_H
20 #  include <config.h>
21 #endif
22
23 #include "defs.h"
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/stat.h>
29 #ifdef HAVE_SVG
30 #include <math.h>
31 #endif
32
33 #include <glib.h>
34 #include <glib/gi18n.h>
35 #include <gtk/gtk.h>
36
37 #include "utils.h"
38 #include "codeconv.h"
39 #include "prefs_common.h"
40 #include "prefs_gtk.h"
41
42 #include "gtk/gtkutils.h"
43 #include "gtk/prefswindow.h"
44 #include "gtk/filesel.h"
45
46 #include "stock_pixmap.h"
47 #include "mainwindow.h"
48 #include "compose.h"
49 #include "alertpanel.h"
50 #include "addr_compl.h"
51 #include "file-utils.h"
52
53 #define IS_CURRENT_THEME(path)  (strcmp(prefs_common.pixmap_theme_path, path) == 0)
54 #define IS_INTERNAL_THEME(path) (strcmp(DEFAULT_PIXMAP_THEME, path) == 0)
55 #define IS_SYSTEM_THEME(path)   (prefs_themes_is_system_theme(path))
56
57 #define PREVIEW_ICONS 7
58
59 typedef struct _ThemesPage
60 {
61         PrefsPage page;
62
63         GtkWidget *window;              /* do not modify */
64
65         GtkWidget *op_menu;
66         GtkWidget *btn_install;
67         GtkWidget *btn_more;
68         GtkWidget *global;
69
70         GtkWidget *name;
71         GtkWidget *author;
72         GtkWidget *url;
73         GtkWidget *status;
74
75         GtkWidget *icons[PREVIEW_ICONS];
76
77         GtkWidget *btn_remove;
78
79         GdkPixbuf *pixbufs[PREVIEW_ICONS];
80
81 #ifdef HAVE_SVG
82         GtkWidget *checkbtn_enable_alpha;
83         GtkWidget *checkbtn_enable_scaling;
84         GtkWidget *checkbtn_scaling_auto;
85         GtkWidget *label_scaling_ppi;
86         GtkWidget *spinbtn_scaling_ppi;
87 #endif
88 } ThemesPage;
89
90 typedef struct _ThemeInfo
91 {
92         gchar *name;
93         gchar *author;
94         gchar *url;
95         gchar *status;
96 } ThemeInfo;
97
98 typedef struct _ThemeName
99 {
100         gchar *name;
101         GList *item;
102 } ThemeName;
103
104 typedef struct _ThemesData
105 {
106         GList      *themes;
107         GList      *names;
108         gchar      *displayed;
109         ThemesPage *page;
110 } ThemesData;
111
112 typedef void (*FileFunc) (const gchar *filename, gpointer data);
113
114 typedef struct _DirInfo {
115         gint bytes;
116         gint files;
117         gint pixms;
118         /* extensions info */
119         const char **supported;
120         gint *length;
121 } DirInfo;
122
123 typedef struct _CopyInfo {
124         gchar *dest;
125         gchar *status;
126 } CopyInfo;
127
128 static ThemesData *prefs_themes_data;
129
130 StockPixmap prefs_themes_icons[PREVIEW_ICONS] = { 
131         STOCK_PIXMAP_DIR_CLOSE,
132         STOCK_PIXMAP_MAIL_SEND,
133         STOCK_PIXMAP_MAIL_RECEIVE, 
134         STOCK_PIXMAP_MAIL_ATTACH,
135         STOCK_PIXMAP_BOOK, 
136         STOCK_PIXMAP_MIME_TEXT_PLAIN, 
137         STOCK_PIXMAP_REPLIED
138 };
139
140
141
142 static void prefs_themes_btn_remove_clicked_cb  (GtkWidget *widget, gpointer data);
143 static void prefs_themes_btn_install_clicked_cb (GtkWidget *widget, gpointer data);
144 static void prefs_themes_menu_item_activated_cb (GtkWidget *widget, gpointer data);
145 #ifdef HAVE_SVG
146 static gdouble prefs_themes_compute_ppi(GdkScreen *screen);
147 static gdouble prefs_themes_get_adjusted_ppi(void);
148 static void prefs_themes_checkbtn_enable_scaling_toggled_cb (GtkWidget *widget, gpointer data);
149 static void prefs_themes_checkbtn_scaling_auto_toggled_cb (GtkWidget *widget, gpointer data);
150 #endif
151
152 static void prefs_themes_update_buttons         (const ThemesData *tdata);
153 static void prefs_themes_display_global_stats   (const ThemesData *tdata);
154 static void prefs_themes_get_theme_info         (ThemesData *tdata);
155 static void prefs_themes_display_theme_info     (ThemesData *tdata, const ThemeInfo *info);
156 static void prefs_themes_get_themes_and_names   (ThemesData *tdata);
157 static int prefs_themes_cmp_name(gconstpointer a, gconstpointer b);
158 static void prefs_themes_free_names             (ThemesData *tdata);
159
160 static void prefs_themes_set_themes_menu        (GtkComboBox *combo, const ThemesData *tdata);
161
162 static gchar *prefs_themes_get_theme_stats      (const gchar *dirname);
163 static gboolean prefs_themes_is_system_theme    (const gchar *dirname);
164
165 static void prefs_themes_create_widget          (PrefsPage *page, GtkWindow *window, gpointer data);
166 static void prefs_themes_destroy_widget         (PrefsPage *page);
167 static void prefs_themes_save                   (PrefsPage *page);
168
169 static void prefs_themes_foreach_file           (const gchar *dirname, const FileFunc func, gpointer data);
170 static void prefs_themes_file_stats             (const gchar *filename, gpointer data);
171 static void prefs_themes_file_remove            (const gchar *filename, gpointer data);
172 static void prefs_themes_file_install           (const gchar *filename, gpointer data);
173
174
175
176 static void prefs_themes_file_stats(const gchar *filename, gpointer data)
177 {
178 #ifdef G_OS_WIN32
179         GFile *f;
180         GFileInfo *fi;
181         GError *error = NULL;
182 #else
183         GStatBuf s;
184 #endif
185         goffset size;
186         DirInfo *di = (DirInfo *)data;
187         gint len;
188         gint i;
189
190 #ifdef G_OS_WIN32
191         f = g_file_new_for_path(filename);
192         fi = g_file_query_info(f, "standard::size,standard::type",
193                         G_FILE_QUERY_INFO_NONE, NULL, &error);
194         if (error != NULL) {
195                 g_warning(error->message);
196                 g_error_free(error);
197                 g_object_unref(f);
198                 return;
199         }
200         if (g_file_info_get_file_type(fi) != G_FILE_TYPE_REGULAR) {
201                 g_object_unref(fi);
202                 g_object_unref(f);
203                 return;
204         }
205         size = g_file_info_get_size(fi);
206         g_object_unref(fi);
207         g_object_unref(f);
208 #else
209         if ((i = g_stat(filename, &s)) != 0) {
210                 debug_print("g_stat on '%s' failed: %d\n", filename, i);
211                 return;
212         }
213         if (!S_ISREG(s.st_mode)) {
214                 return;
215         }
216         size = s.st_size;
217 #endif
218
219         di->bytes += size;
220         di->files++;
221         len = strlen(filename);
222         for (i = 0; (di->supported)[i] != NULL; ++i) {
223                 gint curlen = (di->length)[i];
224                 if (len <= curlen)
225                         continue;
226                 const gchar *extension = filename + (len - curlen);
227                 if (!strcmp(extension, (di->supported)[i])) {
228                         di->pixms++;
229                         break;
230                 }
231         }
232 }
233         
234 static void prefs_themes_file_remove(const gchar *filename, gpointer data)
235 {
236         gchar **status = (gchar **)data;
237         gchar *base;
238
239         if ((*status) != NULL)
240                 return;
241
242         base = g_path_get_basename(filename);
243         if (TRUE == is_dir_exist(filename)) {
244                 if (strcmp(base, ".") != 0 && strcmp(base, "..") != 0)
245                         g_warning("prefs_themes_file_remove(): subdir in theme dir skipped: '%s'.",
246                                                 base);
247         }
248         else if (0 != claws_unlink(filename)) {
249                 (*status) = g_strdup(filename);
250         }
251         g_free(base);
252 }
253
254 static void prefs_themes_file_install(const gchar *filename, gpointer data)
255 {
256         CopyInfo *ci = (CopyInfo *)data;
257         gchar *base;
258
259         if (ci->status != NULL)
260                 return;
261
262         base = g_path_get_basename(filename);
263         if (TRUE == is_dir_exist(filename)) {
264                 if (strcmp(base, ".") != 0 && strcmp(base, "..") !=0 )
265                         g_warning("prefs_themes_file_install(): subdir in theme dir skipped: '%s'.",
266                                                 base);
267         }
268         else {
269                 gchar *fulldest;
270
271                 fulldest = g_strconcat(ci->dest, G_DIR_SEPARATOR_S, base, NULL);
272
273                 if (0 != copy_file(filename, fulldest, FALSE)) {
274                         ci->status = g_strdup(filename);
275                 }
276                 g_free(fulldest);
277         }
278         g_free(base);
279 }
280
281 static void prefs_themes_foreach_file(const gchar *dirname, const FileFunc func, gpointer data)
282 {
283         const gchar *entry;
284         gchar *fullentry;
285         GDir *dp;
286         GError *error = NULL;
287
288         cm_return_if_fail(dirname != NULL);
289         cm_return_if_fail(func != NULL);
290
291         if ((dp = g_dir_open(dirname, 0, &error)) == NULL) {
292                 debug_print("couldn't open dir '%s': %s (%d)\n", dirname,
293                                 error->message, error->code);
294                 g_error_free(error);
295                 return;
296         }
297
298         while ((entry = g_dir_read_name(dp)) != NULL) {
299
300                 fullentry = g_strconcat(dirname, G_DIR_SEPARATOR_S, entry, NULL);
301
302                 (*func)(fullentry, data);
303
304                 g_free(fullentry);
305         }
306         g_dir_close(dp);
307 }
308
309 static gboolean prefs_themes_is_system_theme(const gchar *dirname)
310 {
311         gint len;
312         gchar *system_theme_dir;
313         gboolean is_sys = FALSE;
314
315         cm_return_val_if_fail(dirname != NULL, FALSE);
316
317         system_theme_dir = stock_pixmap_get_system_theme_dir_for_theme(NULL);
318         len = strlen(system_theme_dir);
319         if (strlen(dirname) > len && 0 == strncmp(dirname, system_theme_dir, len))
320                 is_sys = TRUE;
321
322         g_free(system_theme_dir);
323
324         return is_sys;
325 }
326
327 static void prefs_themes_set_themes_menu(GtkComboBox *combo, const ThemesData *tdata)
328 {
329         GtkListStore *store;
330         GtkTreeIter iter;
331         GList     *themes = tdata->names;
332         gint       i = 0, active = 0;
333         GList     *sorted_list = NULL;
334
335         cm_return_if_fail(combo != NULL);
336
337         /* sort theme data list by data name */
338         while (themes != NULL) {
339                 ThemeName *tname = (ThemeName *)(themes->data);
340
341                 sorted_list = g_list_insert_sorted(sorted_list, (gpointer)(tname),
342                                                    (GCompareFunc)prefs_themes_cmp_name);
343
344                 themes = g_list_next(themes);
345         }
346
347         store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
348
349         /* feed gtk_menu w/ sorted themes names */
350         themes = sorted_list;
351         while (themes != NULL) {
352                 ThemeName *tname = (ThemeName *)(themes->data);
353                 gchar     *tpath = (gchar *)(tname->item->data);
354
355                 gtk_list_store_append(store, &iter);
356                 gtk_list_store_set(store, &iter,
357                                    0, tname->name,
358                                    1, tname->item->data, -1);
359
360                 if (tdata->displayed != NULL && !g_strcmp0(tdata->displayed,tpath))
361                         active = i;
362                 ++i;
363
364                 themes = g_list_next(themes);
365         }
366
367         g_list_free(sorted_list);
368
369         g_signal_connect(G_OBJECT(combo), "changed",
370                          G_CALLBACK(prefs_themes_menu_item_activated_cb),
371                          NULL);
372
373         gtk_combo_box_set_model(combo, GTK_TREE_MODEL(store));
374         gtk_combo_box_set_active(combo, active);
375 }
376
377 static int prefs_themes_cmp_name(gconstpointer a_p, gconstpointer b_p)
378 {
379         /* compare two ThemeData structures by their name attribute */
380         return g_strcmp0((gchar *)(((ThemeName*)a_p)->name),
381                                         (gchar *)(((ThemeName*)b_p)->name));
382 }
383
384 static void prefs_themes_get_themes_and_names(ThemesData *tdata)
385 {
386         GList *tpaths;
387
388         cm_return_if_fail(tdata != NULL);
389
390         stock_pixmap_themes_list_free(tdata->themes);
391         prefs_themes_free_names(tdata);
392
393         tdata->themes = stock_pixmap_themes_list_new();
394
395         tpaths = tdata->themes;
396         while (tpaths != NULL) {
397                 ThemeName *name = g_new0(ThemeName, 1);
398                 gchar *sname = g_path_get_basename((const gchar *)(tpaths->data));
399
400                 if (IS_INTERNAL_THEME(sname))
401                         name->name = g_strdup(_("Default internal theme"));
402                 else
403                         name->name = g_strdup(sname);
404                 name->item = tpaths;
405
406                 tdata->names = g_list_append(tdata->names, name);
407                 if (!g_strcmp0(tpaths->data, prefs_common.pixmap_theme_path)) {
408                         tdata->displayed = (gchar *)tpaths->data;
409                 }
410                 tpaths = g_list_next(tpaths);
411                 g_free(sname);  
412         }
413 }
414
415 void prefs_themes_init(void)
416 {
417         ThemesData   *tdata;
418         ThemesPage   *page;
419         GList        *tpaths;
420         static gchar *path[3];
421
422         path[0] = _("Display");
423         path[1] = _("Themes");
424         path[2] = NULL;
425
426         debug_print("Creating preferences for themes...\n");
427
428         tdata = g_new0(ThemesData, 1);
429         prefs_themes_data = tdata;
430
431         prefs_themes_get_themes_and_names(tdata);
432
433         page = g_new0(ThemesPage, 1);
434
435         page->page.path = path;
436         page->page.create_widget = prefs_themes_create_widget;
437         page->page.destroy_widget = prefs_themes_destroy_widget;
438         page->page.save_page = prefs_themes_save;
439         page->page.weight = 130.0;
440         prefs_gtk_register_page((PrefsPage *) page);
441
442         tdata->page = page;
443
444         tpaths = g_list_first(tdata->themes);
445         if (tdata->displayed == NULL)
446                 tdata->displayed = (gchar *)(tpaths->data);
447 #ifdef HAVE_SVG
448         if (prefs_common.pixmap_scaling_auto)
449                 prefs_common.pixmap_scaling_ppi = prefs_themes_get_adjusted_ppi();
450 #endif
451 }
452
453 static void prefs_themes_free_names(ThemesData *tdata)
454 {
455         GList *names;
456
457         cm_return_if_fail(tdata != NULL);
458
459         if (tdata->names == NULL)
460                 return;
461
462         names = tdata->names;
463         while (names != NULL) {
464                 ThemeName *tn = (ThemeName *)(names->data);
465
466                 tn->item = NULL;
467                 g_free(tn->name);
468                 g_free(tn);
469
470                 names = g_list_next(names);
471         }
472         g_list_free(tdata->names);
473         tdata->names = NULL;
474 }
475
476 void prefs_themes_done(void)
477 {
478         ThemesData *tdata = prefs_themes_data;
479
480         debug_print("Finished preferences for themes.\n");
481
482         stock_pixmap_themes_list_free(tdata->themes);
483         prefs_themes_free_names(tdata); 
484         g_free(tdata->page);
485         g_free(tdata);
486 }
487
488 static void prefs_themes_btn_remove_clicked_cb(GtkWidget *widget, gpointer data)
489 {
490         ThemesData *tdata = prefs_themes_data;
491         gchar      *theme_str;
492         gchar      *alert_title = NULL;
493         AlertValue  val = 0;
494         gchar      *tmp = NULL;
495
496         theme_str = tdata->displayed;
497
498         tmp = g_path_get_basename(theme_str);
499         if (IS_SYSTEM_THEME(theme_str)) {
500                 alert_title = g_strdup_printf(_("Remove system theme '%s'"), tmp);
501         } else {
502                 alert_title = g_strdup_printf(_("Remove theme '%s'"), tmp);
503         }
504         g_free(tmp);
505
506         val = alertpanel(alert_title,
507                          _("Are you sure you want to remove this theme?"),
508                          GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_FIRST);
509         g_free(alert_title);
510
511         if (G_ALERTALTERNATE == val) {
512                 gchar *status = NULL;
513
514                 prefs_themes_foreach_file(theme_str, prefs_themes_file_remove, &status); 
515                 if (0 != rmdir(theme_str)) {
516                         if (status != NULL) {
517                                 alertpanel_error(_("File %s failed\nwhile removing theme."), status);
518                                 g_free(status);
519                         }
520                         else
521                                 alertpanel_error(_("Removing theme directory failed."));
522                 }
523                 else {  
524                         alertpanel_notice(_("Theme removed successfully"));
525                         /* update interface back to first theme */
526                         prefs_themes_get_themes_and_names(tdata);
527                         prefs_themes_set_themes_menu(GTK_COMBO_BOX(tdata->page->op_menu), tdata);
528                         prefs_themes_display_global_stats(tdata);
529                         tdata->displayed = (gchar *)((g_list_first(tdata->themes))->data);
530                         prefs_themes_get_theme_info(tdata);
531                 }
532         }
533 }
534
535 static void prefs_themes_btn_install_clicked_cb(GtkWidget *widget, gpointer data)
536 {
537         gchar      *filename, *source;
538         gchar      *themeinfo, *themename;
539         gchar      *alert_title = NULL;
540         CopyInfo   *cinfo;
541         AlertValue  val = 0;
542         ThemesData *tdata = prefs_themes_data;
543
544         filename = filesel_select_file_open_folder(_("Select theme folder"), NULL);
545         if (filename == NULL) 
546                 return;
547
548         if (filename[strlen(filename) - 1] != G_DIR_SEPARATOR)
549                 filename = g_strconcat(filename, G_DIR_SEPARATOR_S, NULL);
550         else
551                 filename = g_strdup(filename);
552
553         cinfo = g_new0(CopyInfo, 1);
554         source = g_path_get_dirname(filename);
555         themename = g_path_get_basename(source);
556         debug_print("Installing '%s' theme from %s\n", themename, filename);
557
558         themeinfo = g_strconcat(source, G_DIR_SEPARATOR_S, THEMEINFO_FILE, NULL);
559         alert_title = g_strdup_printf(_("Install theme '%s'"), themename);
560         if (file_exist(themeinfo, FALSE) == FALSE) {
561                 val = alertpanel(alert_title,
562                                  _("This folder doesn't seem to be a theme"
563                                    "folder.\nInstall anyway?"),
564                                  GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_FIRST);
565                 if (G_ALERTALTERNATE != val) {
566                         g_free(alert_title);
567                         goto end_inst;
568                 }
569         }
570
571         val = alertpanel(alert_title,
572                          _("Do you want to install theme for all users?"),
573                          GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_FIRST);
574         g_free(alert_title);
575         switch (val) {
576         case G_ALERTALTERNATE:
577                 cinfo->dest = stock_pixmap_get_system_theme_dir_for_theme(
578                                         themename);
579                 break;
580         case G_ALERTDEFAULT:
581                 break;
582         default:
583                 goto end_inst;
584         }
585
586         if (cinfo->dest == NULL) {
587                 cinfo->dest = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
588                                           PIXMAP_THEME_DIR, G_DIR_SEPARATOR_S,
589                                           themename, NULL);
590         }
591         if (TRUE == is_dir_exist(cinfo->dest)) {
592                 AlertValue val = alertpanel_full(_("Theme exists"),
593                                 _("A theme with the same name is\n"
594                                   "already installed in this location.\n\n"
595                                   "Do you want to replace it?"),
596                                 GTK_STOCK_CANCEL, _("Overwrite"), NULL, ALERTFOCUS_FIRST,
597                                 FALSE, NULL, ALERT_WARNING);
598                 if (val == G_ALERTALTERNATE) {
599                         if (remove_dir_recursive(cinfo->dest) < 0) {
600                                 alertpanel_error(_("Couldn't delete the old theme in %s."),
601                                                  cinfo->dest);
602                                 goto end_inst;
603                         }
604                 } else {
605                         goto end_inst;
606                 }
607         }
608         if (0 != make_dir_hier(cinfo->dest)) {
609                 alertpanel_error(_("Couldn't create destination directory %s."),
610                                  cinfo->dest);
611                 goto end_inst;
612         }
613         prefs_themes_foreach_file(source, prefs_themes_file_install, cinfo);
614         if (cinfo->status == NULL) {
615                 GList *insted;
616
617                 /* update interface to show newly installed theme */
618                 prefs_themes_get_themes_and_names(tdata);
619                 insted = g_list_find_custom(tdata->themes,
620                                             (gpointer)(cinfo->dest),
621                                             (GCompareFunc)g_strcmp0);
622                 if (NULL != insted) {
623                         alertpanel_notice(_("Theme installed successfully."));
624                         tdata->displayed = (gchar *)(insted->data);
625                         prefs_themes_set_themes_menu(GTK_COMBO_BOX(tdata->page->op_menu), tdata);
626                         prefs_themes_display_global_stats(tdata);
627                         prefs_themes_get_theme_info(tdata);
628                 }
629                 else
630                         alertpanel_error(_("Failed installing theme"));
631         }
632         else
633                 alertpanel_error(_("File %s failed\nwhile installing theme."), cinfo->status);
634 end_inst:
635         g_free(cinfo->dest);
636         g_free(filename);
637         g_free(source);
638         g_free(themeinfo);
639         g_free(cinfo);
640         g_free(themename);
641 }
642
643 static void prefs_themes_menu_item_activated_cb(GtkWidget *widget, gpointer data)
644 {
645         ThemesData *tdata = prefs_themes_data;
646         gchar      *path;
647         GtkTreeModel *model;
648         GtkTreeIter iter;
649
650         cm_return_if_fail(gtk_combo_box_get_active_iter(GTK_COMBO_BOX(widget), &iter));
651
652         model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget));
653         gtk_tree_model_get(model, &iter, 1, &path, -1);
654
655         tdata->displayed = path;
656         prefs_themes_get_theme_info(tdata);
657 }
658
659 #ifdef HAVE_SVG
660 #define MM_INCH 0.0393700787402
661 static gdouble prefs_themes_compute_ppi(GdkScreen *screen)
662 {
663         gdouble wp = gdk_screen_get_width(screen);
664         gdouble hp = gdk_screen_get_height(screen);
665         gdouble wi = gdk_screen_get_width_mm(screen);
666         gdouble hi = gdk_screen_get_height_mm(screen);
667         gdouble dp, di;
668
669         debug_print("screen is %f x %f pixels, %f x %f mm\n", wp, hp, wi, hi);
670
671         /* https://en.wikipedia.org/wiki/Pixel_density */
672         wi *= MM_INCH;
673         hi *= MM_INCH;
674         dp = sqrt(wp * wp + hp * hp);
675         di = sqrt(wi * wi + hi * hi);
676
677         return (di != 0.0)? dp / di: 0.0;
678 }
679
680 static gdouble prefs_themes_get_adjusted_ppi(void)
681 {
682         gdouble ppi, cppi;
683         GdkScreen * screen = gdk_screen_get_default();
684
685         if (screen == NULL) { /* oops! */
686                 g_warning("unable to get default GDK screen");
687                 return MIN_PPI;
688         }
689
690         ppi = gdk_screen_get_resolution(screen);
691         cppi = prefs_themes_compute_ppi(screen);
692         debug_print("returned PPI: %f / computed PPI: %f\n", ppi, cppi);
693         /*
694          gdk_screen_get_resolution doesn't seem to work well when running
695          on a remote display and returns the value of the local display.
696          height/width functions do this better, so we can compute a PPI
697          from them and take the highest value.
698         */
699         return MAX(ppi, cppi);
700 }
701
702 static void prefs_themes_checkbtn_enable_scaling_toggled_cb (GtkWidget *widget, gpointer data)
703 {
704         ThemesPage *page = (ThemesPage *) data;
705         gboolean enabled = gtk_toggle_button_get_active(
706                                 GTK_TOGGLE_BUTTON (widget));
707         gboolean automatic = gtk_toggle_button_get_active(
708                                 GTK_TOGGLE_BUTTON (page->checkbtn_scaling_auto));
709
710         gtk_widget_set_sensitive(page->checkbtn_scaling_auto, enabled);
711         gtk_widget_set_sensitive(page->spinbtn_scaling_ppi, enabled && !automatic);
712         gtk_widget_set_sensitive(page->label_scaling_ppi, enabled && !automatic);
713 }
714
715 static void prefs_themes_checkbtn_scaling_auto_toggled_cb(GtkWidget *widget, gpointer data)
716 {
717         ThemesPage *page = (ThemesPage *) data;
718         gboolean automatic = gtk_toggle_button_get_active(
719                                 GTK_TOGGLE_BUTTON (widget));
720
721         gtk_widget_set_sensitive(page->spinbtn_scaling_ppi, !automatic);
722         gtk_widget_set_sensitive(page->label_scaling_ppi, !automatic);
723
724         if (automatic) /* update PPI value */
725                 gtk_spin_button_set_value(
726                                 GTK_SPIN_BUTTON (page->spinbtn_scaling_ppi),
727                                 prefs_themes_get_adjusted_ppi());
728 }
729 #endif
730
731 static void prefs_themes_update_buttons(const ThemesData *tdata)
732 {
733         ThemesPage *theme = tdata->page;
734         gboolean    can_rem, can_use;
735
736         can_use = !IS_CURRENT_THEME(tdata->displayed);
737         can_rem = can_use && !IS_INTERNAL_THEME(tdata->displayed);
738
739         if (theme->btn_remove != NULL)
740                 gtk_widget_set_sensitive(theme->btn_remove, can_rem);
741 }
742
743 /* placeholders may already be utf8 (i18n) */
744 #define SET_LABEL_TEXT_UTF8(label, text)                                \
745 {                                                                       \
746         gchar *tmpstr;                                                  \
747                                                                         \
748         if (!g_utf8_validate(text, -1, NULL))                           \
749                 tmpstr = conv_codeset_strdup(text,                      \
750                         conv_get_locale_charset_str(),  CS_UTF_8);      \
751         else                                                            \
752                 tmpstr = g_strdup(text);                                \
753                                                                         \
754         gtk_label_set_text(GTK_LABEL(label), tmpstr);                   \
755         gtk_label_set_selectable(GTK_LABEL(label), TRUE);               \
756         g_free(tmpstr);                                                 \
757 }
758 static void prefs_themes_display_theme_info(ThemesData *tdata, const ThemeInfo *info)
759 {
760         ThemesPage *theme = tdata->page;
761         gchar *save_prefs_path;
762         gint   i;
763
764         SET_LABEL_TEXT_UTF8(theme->name,        info->name);
765         SET_LABEL_TEXT_UTF8(theme->author,      info->author);
766         SET_LABEL_TEXT_UTF8(theme->url,         info->url);
767         SET_LABEL_TEXT_UTF8(theme->status,      info->status);
768
769         save_prefs_path = prefs_common.pixmap_theme_path;
770         prefs_common.pixmap_theme_path = tdata->displayed;
771         for (i = 0; i < PREVIEW_ICONS; ++i) {
772                 stock_pixbuf_gdk(prefs_themes_icons[i], &(theme->pixbufs[i]));
773                 gtk_image_set_from_pixbuf(GTK_IMAGE(theme->icons[i]),
774                                 theme->pixbufs[i]);
775         }
776         prefs_common.pixmap_theme_path = save_prefs_path;
777
778         prefs_themes_update_buttons(tdata);
779 }
780 #undef SET_LABEL_TEXT_UTF8
781
782 static void prefs_themes_display_global_stats(const ThemesData *tdata)
783 {
784         ThemesPage *theme = tdata->page;
785         GList      *tnames = tdata->names;
786         gchar      *gstats;
787         gint        sys = 0;
788         gint        usr = 0;
789         gint        all = 0;
790
791         while (tnames != NULL) {
792                 ThemeName *tname = (ThemeName *)(tnames->data);
793                 gchar     *tpath = (gchar *)(tname->item->data);
794                 
795                 if (IS_SYSTEM_THEME(tpath)) 
796                         ++sys;
797                 else if (!IS_INTERNAL_THEME(tpath)) 
798                         ++usr;
799                 ++all;
800                 tnames = g_list_next(tnames);
801         }
802
803         gstats = g_strdup_printf(_("%d themes available (%d user, %d system, 1 internal)"),
804                                  all, usr, sys);
805         gtk_label_set_text(GTK_LABEL(theme->global), gstats);
806         gtk_label_set_justify (GTK_LABEL (theme->global), GTK_JUSTIFY_LEFT);
807         gtkut_widget_set_small_font_size (theme->global);
808         g_free(gstats);
809 }
810
811 #define INFOFILE_LINE_LEN 80
812
813 #define FGETS_INFOFILE_LINE() \
814         line[0] = '\0'; \
815         if (claws_fgets(line, INFOFILE_LINE_LEN, finfo) != NULL && (len = strlen(line)) > 0) { \
816                 if (line[len - 1] == '\n') line[len - 1] = '\0'; \
817         } \
818         else { \
819                 g_strlcpy(line, _("Unknown"),sizeof(line)); \
820         }
821
822 static void prefs_themes_get_theme_info(ThemesData *tdata)
823 {
824         FILE  *finfo;
825         gchar *sinfo;
826         gchar *path;
827         gchar  line[INFOFILE_LINE_LEN];
828         gint   len;
829         ThemeInfo *info;
830         ThemesPage *theme = tdata->page;
831
832         cm_return_if_fail(theme != NULL);
833         path = tdata->displayed;
834         cm_return_if_fail(path != NULL);
835
836         debug_print("Getting theme info for %s\n", path);
837
838         info = g_new0(ThemeInfo, 1);
839
840         if (IS_INTERNAL_THEME(path)) {
841                 info->name = g_strdup(_("Default internal theme"));
842                 info->author = g_strdup(_("The Claws Mail Team"));
843                 info->url = g_strdup(HOMEPAGE_URI);
844                 info->status = g_strdup_printf(_("Internal theme has %d icons"), N_STOCK_PIXMAPS);
845         }
846         else {
847                 sinfo = g_strconcat(path, G_DIR_SEPARATOR_S, THEMEINFO_FILE, NULL);
848                 finfo = claws_fopen(sinfo, "r");
849                 if (finfo == NULL) {
850                         info->name = g_strdup(_("No info file available for this theme"));
851                         info->author = g_strdup(_("Unknown"));
852                         info->url = g_strdup(_("Unknown"));
853                 }
854                 else {
855                         FGETS_INFOFILE_LINE()
856                         info->name = g_strdup(line);
857                         FGETS_INFOFILE_LINE()
858                         info->author = g_strdup(line);
859                         FGETS_INFOFILE_LINE()
860                         info->url = g_strdup(line);
861                 
862                         claws_fclose(finfo);
863                 }
864                 g_free(sinfo);
865
866                 info->status = prefs_themes_get_theme_stats(path);
867                 if (info->status == NULL) {
868                         info->status = g_strdup(_("Error: couldn't get theme status"));
869                 }
870         }
871
872         prefs_themes_display_theme_info(tdata, info);
873
874         g_free(info->name);
875         g_free(info->author);
876         g_free(info->url);
877         g_free(info->status);
878
879         g_free(info);
880 }
881
882 #undef FGETS_INFOFILE_LINE
883
884 static gchar *prefs_themes_get_theme_stats(const gchar *dirname)
885 {
886         gchar   *stats;
887         DirInfo *dinfo;
888         gint     i;
889
890         dinfo = g_new0(DirInfo, 1);
891         dinfo->supported = stock_pixmap_theme_extensions();
892         for (i = 0; (dinfo->supported)[i] != NULL; ++i);
893         dinfo->length = g_malloc(i * sizeof(gint));
894         for (i = 0; (dinfo->supported)[i] != NULL; ++i) {
895                 (dinfo->length)[i] = strlen((dinfo->supported)[i]);
896         }
897         prefs_themes_foreach_file(dirname, prefs_themes_file_stats, dinfo);
898         stats = g_strdup_printf(_("%d files (%d icons), size: %s"),
899                                 dinfo->files, dinfo->pixms,
900                                 to_human_readable((goffset)dinfo->bytes));
901         g_free(dinfo->length);
902         g_free(dinfo);
903         return stats;
904 }
905
906 static void prefs_themes_create_widget(PrefsPage *page, GtkWindow *window, gpointer data)
907 {
908         ThemesPage *prefs_themes = (ThemesPage *)page;
909         ThemesData *tdata = prefs_themes_data;
910
911         GtkWidget *vbox1;
912         GtkWidget *frame1;
913         GtkWidget *vbox2;
914         GtkWidget *hbox3;
915         GtkWidget *menu_themes;
916         GtkWidget *btn_install;
917         GtkWidget *btn_more;
918         GtkWidget *label_global_status;
919         GtkWidget *frame_info;
920         GtkWidget *table1;
921         GtkWidget *label1;
922         GtkWidget *label2;
923         GtkWidget *label3;
924         GtkWidget *label_name;
925         GtkWidget *label_author;
926         GtkWidget *label_url;
927         GtkWidget *label4;
928         GtkWidget *label_status;
929         GtkWidget *frame_preview;
930         GtkWidget *hbox1;
931         GtkWidget *icon_1;
932         GtkWidget *icon_2;
933         GtkWidget *icon_3;
934         GtkWidget *icon_4;
935         GtkWidget *icon_5;
936         GtkWidget *icon_6;
937         GtkWidget *icon_7;
938         GtkWidget *frame_buttons;
939         GtkWidget *hbuttonbox1;
940         GtkWidget *btn_remove;
941         GtkCellRenderer *renderer;
942 #ifdef HAVE_SVG
943         GtkWidget *frame_scaling;
944         GtkWidget *checkbtn_enable_alpha;
945         GtkWidget *checkbtn_enable_scaling;
946         GtkWidget *checkbtn_scaling_auto;
947         GtkWidget *label_scaling_ppi;
948         GtkWidget *spinbtn_scaling_ppi;
949         GtkAdjustment *spinbtn_scaling_ppi_adj;
950 #endif
951
952         vbox1 = gtk_vbox_new (FALSE, VSPACING);
953         gtk_container_set_border_width (GTK_CONTAINER (vbox1), 5);
954         gtk_widget_show (vbox1);
955
956         vbox2 = gtkut_get_options_frame(vbox1, &frame1, _("Selector"));
957
958         hbox3 = gtk_hbox_new (FALSE, 5);
959         gtk_widget_show (hbox3);
960         gtk_box_pack_start (GTK_BOX (vbox2), hbox3, FALSE, FALSE, 0);
961         // gtk_container_set_border_width (GTK_CONTAINER (hbox3), 5);
962
963         menu_themes = gtk_combo_box_new();
964         gtk_widget_show (menu_themes);
965         gtk_box_pack_start (GTK_BOX (hbox3), menu_themes, FALSE, FALSE, 0);
966
967         btn_install = gtk_button_new_with_label (_("Install new..."));
968         gtk_widget_show (btn_install);
969         gtk_box_pack_start (GTK_BOX (hbox3), btn_install, FALSE, FALSE, 0);
970         gtk_widget_set_can_default (btn_install, TRUE);
971
972         btn_more = gtkut_get_link_btn((GtkWidget *)window, THEMES_URI, _("Get more..."));
973         gtk_widget_show (btn_more);
974         gtk_box_pack_start (GTK_BOX (hbox3), btn_more, FALSE, FALSE, 0);
975
976         label_global_status = gtk_label_new ("");
977         gtk_widget_show (label_global_status);
978         gtk_box_pack_start (GTK_BOX (vbox2), label_global_status, FALSE, FALSE, 0);
979         gtk_label_set_justify (GTK_LABEL (label_global_status), GTK_JUSTIFY_LEFT);
980         gtk_misc_set_alignment (GTK_MISC (label_global_status), 0, 0.5);
981         gtk_misc_set_padding (GTK_MISC (label_global_status), 1, 0);
982
983         PACK_FRAME(vbox1, frame_info, _("Information"));
984
985         table1 = gtk_table_new (4, 2, FALSE);
986         gtk_widget_show (table1);
987         gtk_container_add (GTK_CONTAINER (frame_info), table1);
988         gtk_container_set_border_width (GTK_CONTAINER (table1), 5);
989
990         label1 = gtk_label_new (_("Name"));
991         gtk_widget_show (label1);
992         gtk_table_attach (GTK_TABLE (table1), label1, 0, 1, 0, 1,
993                         (GtkAttachOptions) (GTK_FILL),
994                         (GtkAttachOptions) (0), 5, 4);
995         gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
996         gtk_misc_set_alignment (GTK_MISC (label1), 1, 0.5);
997
998         label2 = gtk_label_new (_("Author"));
999         gtk_widget_show (label2);
1000         gtk_table_attach (GTK_TABLE (table1), label2, 0, 1, 1, 2,
1001                         (GtkAttachOptions) (GTK_FILL),
1002                         (GtkAttachOptions) (0), 5, 4);
1003         gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
1004         gtk_misc_set_alignment (GTK_MISC (label2), 1, 0.5);
1005
1006         label3 = gtk_label_new (_("URL"));
1007         gtk_widget_show (label3);
1008         gtk_table_attach (GTK_TABLE (table1), label3, 0, 1, 2, 3,
1009                         (GtkAttachOptions) (GTK_FILL),
1010                         (GtkAttachOptions) (0), 5, 4);
1011         gtk_misc_set_alignment (GTK_MISC (label3), 1, 0.5);
1012
1013         label_name = gtk_label_new ("");
1014         gtk_widget_show (label_name);
1015         gtk_table_attach (GTK_TABLE (table1), label_name, 1, 2, 0, 1,
1016                         (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1017                         (GtkAttachOptions) (0), 5, 0);
1018         gtk_misc_set_alignment (GTK_MISC (label_name), 0, 0.5);
1019
1020         label_author = gtk_label_new ("");
1021         gtk_widget_show (label_author);
1022         gtk_table_attach (GTK_TABLE (table1), label_author, 1, 2, 1, 2,
1023                         (GtkAttachOptions) (GTK_FILL),
1024                         (GtkAttachOptions) (0), 5, 0);
1025         gtk_misc_set_alignment (GTK_MISC (label_author), 0, 0.5);
1026
1027         label_url = gtk_label_new ("");
1028         gtk_widget_show (label_url);
1029         gtk_table_attach (GTK_TABLE (table1), label_url, 1, 2, 2, 3,
1030                         (GtkAttachOptions) (GTK_FILL),
1031                         (GtkAttachOptions) (0), 5, 0);
1032         gtk_misc_set_alignment (GTK_MISC (label_url), 0, 0.5);
1033
1034         label4 = gtk_label_new (_("Status"));
1035         gtk_widget_show (label4);
1036         gtk_table_attach (GTK_TABLE (table1), label4, 0, 1, 3, 4,
1037                         (GtkAttachOptions) (GTK_FILL),
1038                         (GtkAttachOptions) (0), 5, 4);
1039         gtk_misc_set_alignment (GTK_MISC (label4), 1, 0.5);
1040
1041         label_status = gtk_label_new ("");
1042         gtk_widget_show (label_status);
1043         gtk_table_attach (GTK_TABLE (table1), label_status, 1, 2, 3, 4,
1044                         (GtkAttachOptions) (GTK_FILL),
1045                         (GtkAttachOptions) (0), 5, 0);
1046         gtk_misc_set_alignment (GTK_MISC (label_status), 0, 0.5);
1047
1048         PACK_FRAME(vbox1, frame_preview, _("Preview"));
1049
1050         hbox1 = gtk_hbox_new (FALSE, 0);
1051         gtk_widget_show (hbox1);
1052         gtk_container_add (GTK_CONTAINER (frame_preview), hbox1);
1053         gtk_container_set_border_width (GTK_CONTAINER (hbox1), 5);
1054
1055         icon_1 = gtk_image_new();
1056         gtk_widget_show (icon_1);
1057         gtk_box_pack_start (GTK_BOX (hbox1), icon_1, TRUE, TRUE, 2);
1058         gtk_misc_set_padding (GTK_MISC (icon_1), 0, 5);
1059
1060         icon_2 = gtk_image_new();
1061         gtk_widget_show (icon_2);
1062         gtk_box_pack_start (GTK_BOX (hbox1), icon_2, TRUE, TRUE, 2);
1063         gtk_misc_set_padding (GTK_MISC (icon_2), 0, 5);
1064
1065         icon_3 = gtk_image_new();
1066         gtk_widget_show (icon_3);
1067         gtk_box_pack_start (GTK_BOX (hbox1), icon_3, TRUE, TRUE, 2);
1068         gtk_misc_set_padding (GTK_MISC (icon_3), 0, 5);
1069
1070         icon_4 = gtk_image_new();
1071         gtk_widget_show (icon_4);
1072         gtk_box_pack_start (GTK_BOX (hbox1), icon_4, TRUE, TRUE, 2);
1073         gtk_misc_set_padding (GTK_MISC (icon_4), 0, 5);
1074
1075         icon_5 = gtk_image_new();
1076         gtk_widget_show (icon_5);
1077         gtk_box_pack_start (GTK_BOX (hbox1), icon_5, TRUE, TRUE, 2);
1078         gtk_misc_set_padding (GTK_MISC (icon_5), 0, 5);
1079
1080         icon_6 = gtk_image_new();
1081         gtk_widget_show (icon_6);
1082         gtk_box_pack_start (GTK_BOX (hbox1), icon_6, TRUE, TRUE, 2);
1083         gtk_misc_set_padding (GTK_MISC (icon_6), 0, 5);
1084
1085         icon_7 = gtk_image_new();
1086         gtk_widget_show (icon_7);
1087         gtk_box_pack_start (GTK_BOX (hbox1), icon_7, TRUE, TRUE, 2);
1088         gtk_misc_set_padding (GTK_MISC (icon_7), 0, 5);
1089
1090         PACK_FRAME(vbox1, frame_buttons, _("Actions"));
1091
1092         hbuttonbox1 = gtk_hbutton_box_new ();
1093         gtk_widget_show (hbuttonbox1);
1094         gtk_container_add (GTK_CONTAINER (frame_buttons), hbuttonbox1);
1095         gtk_container_set_border_width (GTK_CONTAINER (hbuttonbox1), 8);
1096         gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox1), GTK_BUTTONBOX_START);
1097         gtk_box_set_spacing (GTK_BOX (hbuttonbox1), 5);
1098
1099         btn_remove = gtk_button_new_with_label (_("Remove"));
1100         gtk_widget_show (btn_remove);
1101         gtk_container_add (GTK_CONTAINER (hbuttonbox1), btn_remove);
1102         gtk_widget_set_can_default (btn_remove, TRUE);
1103
1104 #ifdef HAVE_SVG
1105         PACK_FRAME(vbox1, frame_scaling, _("SVG rendering"));
1106
1107         vbox2 = gtk_vbox_new (FALSE, VSPACING);
1108         gtk_widget_show (vbox2);
1109         gtk_container_set_border_width (GTK_CONTAINER (vbox2), 5);
1110         gtk_container_add (GTK_CONTAINER (frame_scaling), vbox2);
1111
1112         PACK_CHECK_BUTTON(vbox2, checkbtn_enable_alpha, _("Enable alpha channel"));
1113         PACK_CHECK_BUTTON(vbox2, checkbtn_enable_scaling, _("Force scaling"));
1114         PACK_CHECK_BUTTON(vbox2, checkbtn_scaling_auto, _("Automatic"));
1115
1116         hbox3 = gtk_hbox_new (FALSE, 5);
1117         gtk_widget_show (hbox3);
1118
1119         label_scaling_ppi = gtk_label_new (_("Pixels per inch (PPI)"));
1120         gtk_widget_show (label_scaling_ppi);
1121         gtk_box_pack_start (GTK_BOX (hbox3), label_scaling_ppi,
1122                         FALSE, FALSE, 5);
1123
1124         spinbtn_scaling_ppi_adj = GTK_ADJUSTMENT(
1125                 gtk_adjustment_new (MIN_PPI, MIN_PPI, MAX_PPI, 1, 10, 0));
1126         spinbtn_scaling_ppi = gtk_spin_button_new(
1127                         spinbtn_scaling_ppi_adj, 1.0, 0);
1128         gtk_widget_show (spinbtn_scaling_ppi);
1129         gtk_box_pack_start (GTK_BOX (hbox3), spinbtn_scaling_ppi,
1130                         FALSE, FALSE, 5);
1131
1132         gtk_box_pack_start (GTK_BOX (vbox2), hbox3, FALSE, FALSE, 0);
1133
1134         /* initialize widget values */
1135         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (checkbtn_enable_alpha),
1136                         prefs_common.enable_alpha_svg);
1137         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (checkbtn_enable_scaling),
1138                         prefs_common.enable_pixmap_scaling);
1139         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (checkbtn_scaling_auto),
1140                         prefs_common.pixmap_scaling_auto);
1141         gtk_spin_button_set_value(GTK_SPIN_BUTTON (spinbtn_scaling_ppi),
1142                         prefs_common.pixmap_scaling_ppi);
1143
1144         /* sensitivity */
1145         gtk_widget_set_sensitive(checkbtn_scaling_auto,
1146                         prefs_common.enable_pixmap_scaling);
1147         gtk_widget_set_sensitive(spinbtn_scaling_ppi,
1148                         prefs_common.enable_pixmap_scaling
1149                                 && !prefs_common.pixmap_scaling_auto);
1150         gtk_widget_set_sensitive(label_scaling_ppi,
1151                         prefs_common.enable_pixmap_scaling
1152                                 && !prefs_common.pixmap_scaling_auto);
1153
1154         /* signals */
1155         g_signal_connect(G_OBJECT(checkbtn_enable_scaling), "toggled",
1156                          G_CALLBACK(prefs_themes_checkbtn_enable_scaling_toggled_cb),
1157                          prefs_themes);
1158         g_signal_connect(G_OBJECT(checkbtn_scaling_auto), "toggled",
1159                          G_CALLBACK(prefs_themes_checkbtn_scaling_auto_toggled_cb),
1160                          prefs_themes);
1161 #endif
1162
1163         g_signal_connect(G_OBJECT(btn_remove), "clicked",
1164                          G_CALLBACK(prefs_themes_btn_remove_clicked_cb),
1165                          NULL);
1166         g_signal_connect(G_OBJECT(btn_install), "clicked",
1167                          G_CALLBACK(prefs_themes_btn_install_clicked_cb),
1168                          NULL);
1169
1170         prefs_themes->window = GTK_WIDGET(window);
1171
1172         prefs_themes->name   = label_name;
1173         prefs_themes->author = label_author;
1174         prefs_themes->url    = label_url;
1175         prefs_themes->status = label_status;
1176         prefs_themes->global = label_global_status;
1177
1178         prefs_themes->icons[0] = icon_1;
1179         prefs_themes->icons[1] = icon_2;
1180         prefs_themes->icons[2] = icon_3;
1181         prefs_themes->icons[3] = icon_4;
1182         prefs_themes->icons[4] = icon_5;
1183         prefs_themes->icons[5] = icon_6;
1184         prefs_themes->icons[6] = icon_7;
1185
1186         prefs_themes->btn_remove  = btn_remove;
1187         prefs_themes->btn_install = btn_install;
1188         prefs_themes->btn_more    = btn_more;
1189
1190         prefs_themes->op_menu     = menu_themes;
1191
1192 #ifdef HAVE_SVG
1193         prefs_themes->checkbtn_enable_alpha   = checkbtn_enable_alpha;
1194         prefs_themes->checkbtn_enable_scaling = checkbtn_enable_scaling;
1195         prefs_themes->checkbtn_scaling_auto   = checkbtn_scaling_auto;
1196         prefs_themes->label_scaling_ppi       = label_scaling_ppi;
1197         prefs_themes->spinbtn_scaling_ppi     = spinbtn_scaling_ppi;
1198 #endif
1199
1200         prefs_themes->page.widget = vbox1;
1201         
1202         prefs_themes_set_themes_menu(GTK_COMBO_BOX(menu_themes), tdata);
1203         renderer = gtk_cell_renderer_text_new();
1204         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(menu_themes), renderer, TRUE);
1205         gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(menu_themes), renderer,
1206                                         "text", 0, NULL);
1207
1208         prefs_themes_get_theme_info(tdata);
1209         prefs_themes_display_global_stats(tdata);
1210 }
1211
1212 static void prefs_themes_destroy_widget(PrefsPage *page)
1213 {
1214         /* ThemesPage *theme = (ThemesPage *)page; */
1215 }
1216
1217 static void prefs_themes_save(PrefsPage *page)
1218 {
1219         ThemesData *tdata = prefs_themes_data;
1220         gchar      *theme_str = tdata->displayed;
1221 #ifdef HAVE_SVG
1222         ThemesPage *tpage = (ThemesPage *) page;
1223         gboolean alpha = prefs_common.enable_alpha_svg;
1224         gboolean scaling = prefs_common.enable_pixmap_scaling;
1225         gboolean scaling_auto = prefs_common.pixmap_scaling_auto;
1226         gint scaling_ppi = prefs_common.pixmap_scaling_ppi;
1227
1228         prefs_common.enable_alpha_svg = gtk_toggle_button_get_active(
1229                 GTK_TOGGLE_BUTTON (tpage->checkbtn_enable_alpha));
1230         prefs_common.enable_pixmap_scaling = gtk_toggle_button_get_active(
1231                 GTK_TOGGLE_BUTTON (tpage->checkbtn_enable_scaling));
1232         prefs_common.pixmap_scaling_auto = gtk_toggle_button_get_active(
1233                 GTK_TOGGLE_BUTTON (tpage->checkbtn_scaling_auto));
1234         prefs_common.pixmap_scaling_ppi = gtk_spin_button_get_value_as_int (
1235                 GTK_SPIN_BUTTON (tpage->spinbtn_scaling_ppi));
1236 #endif
1237
1238         if (!IS_CURRENT_THEME(theme_str)) {
1239                 debug_print("Changing theme to %s\n", theme_str);
1240                 g_free(prefs_common.pixmap_theme_path);
1241
1242                 prefs_common.pixmap_theme_path = g_strdup(theme_str);
1243
1244                 main_window_reflect_prefs_all_real(TRUE);
1245                 compose_reflect_prefs_pixmap_theme();
1246                 addrcompl_reflect_prefs_pixmap_theme();
1247
1248                 prefs_themes_update_buttons(tdata);
1249         }
1250 #ifdef HAVE_SVG
1251         else if (scaling != prefs_common.enable_pixmap_scaling
1252                         || alpha != prefs_common.enable_alpha_svg
1253                         || (scaling_auto != prefs_common.pixmap_scaling_auto
1254                                 && scaling_ppi != prefs_common.pixmap_scaling_ppi)) {
1255                 /* same theme, different scaling options */
1256                 debug_print("Updating theme scaling\n");
1257                 stock_pixmap_invalidate_all_icons();
1258                 main_window_reflect_prefs_all_real(TRUE);
1259                 compose_reflect_prefs_pixmap_theme();
1260                 addrcompl_reflect_prefs_pixmap_theme();
1261         }
1262 #endif
1263 }
1264