RSSyl: Fix argument order for strstr() in Atom parser.
[claws.git] / src / prefs_themes.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2003-2012 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
20 #ifdef HAVE_CONFIG_H
21 #  include <config.h>
22 #endif
23
24 #include "defs.h"
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/stat.h>
30
31 #include <glib.h>
32 #include <glib/gi18n.h>
33 #include <gtk/gtk.h>
34
35 #include "utils.h"
36 #include "codeconv.h"
37 #include "prefs_common.h"
38 #include "prefs_gtk.h"
39
40 #include "gtk/gtkutils.h"
41 #include "gtk/prefswindow.h"
42 #include "gtk/filesel.h"
43
44 #include "stock_pixmap.h"
45 #include "mainwindow.h"
46 #include "compose.h"
47 #include "alertpanel.h"
48 #include "addr_compl.h"
49
50 #define IS_CURRENT_THEME(path)  (strcmp(prefs_common.pixmap_theme_path, path) == 0)
51 #define IS_INTERNAL_THEME(path) (strcmp(DEFAULT_PIXMAP_THEME, path) == 0)
52 #define IS_SYSTEM_THEME(path)   (prefs_themes_is_system_theme(path))
53
54 #define PREVIEW_ICONS 7
55
56 typedef struct _ThemesPage
57 {
58         PrefsPage page;
59
60         GtkWidget *window;              /* do not modify */
61
62         GtkWidget *op_menu;
63         GtkWidget *btn_install;
64         GtkWidget *btn_more;
65         GtkWidget *global;
66
67         GtkWidget *name;
68         GtkWidget *author;
69         GtkWidget *url;
70         GtkWidget *status;
71         
72         GtkWidget *icons[PREVIEW_ICONS];
73         
74         GtkWidget *btn_use;
75         GtkWidget *btn_remove;
76
77         GdkPixbuf *pixbufs[PREVIEW_ICONS];
78
79         /* gchar     *theme_path; */
80 } ThemesPage;
81
82 typedef struct _ThemeInfo
83 {
84         gchar *name;
85         gchar *author;
86         gchar *url;
87         gchar *status;
88 } ThemeInfo;
89
90 typedef struct _ThemeName
91 {
92         gchar *name;
93         GList *item;
94 } ThemeName;
95
96 typedef struct _ThemesData
97 {
98         GList      *themes;
99         GList      *names;
100         gchar      *displayed;
101         ThemesPage *page;
102 } ThemesData;
103
104 typedef void (*FileFunc) (const gchar *filename, gpointer data);
105
106 typedef struct _DirInfo {
107         gint bytes;
108         gint files;
109         gint pixms;
110 } DirInfo;
111
112 typedef struct _CopyInfo {
113         gchar *dest;
114         gchar *status;
115 } CopyInfo;
116
117 static ThemesData *prefs_themes_data;
118
119 StockPixmap prefs_themes_icons[PREVIEW_ICONS] = { 
120         STOCK_PIXMAP_DIR_CLOSE,
121         STOCK_PIXMAP_MAIL_SEND,
122         STOCK_PIXMAP_MAIL_RECEIVE, 
123         STOCK_PIXMAP_MAIL_ATTACH,
124         STOCK_PIXMAP_BOOK, 
125         STOCK_PIXMAP_MIME_TEXT_PLAIN, 
126         STOCK_PIXMAP_REPLIED
127 };
128
129
130
131 static void prefs_themes_btn_use_clicked_cb     (GtkWidget *widget, gpointer data);
132 static void prefs_themes_btn_remove_clicked_cb  (GtkWidget *widget, gpointer data);
133 static void prefs_themes_btn_install_clicked_cb (GtkWidget *widget, gpointer data);
134 static void prefs_themes_menu_item_activated_cb (GtkWidget *widget, gpointer data);
135
136 static void prefs_themes_update_buttons         (const ThemesData *tdata);
137 static void prefs_themes_display_global_stats   (const ThemesData *tdata);
138 static void prefs_themes_get_theme_info         (ThemesData *tdata);
139 static void prefs_themes_display_theme_info     (ThemesData *tdata, const ThemeInfo *info);
140 static void prefs_themes_get_themes_and_names   (ThemesData *tdata);
141 static int prefs_themes_cmp_name(gconstpointer a, gconstpointer b);
142 static void prefs_themes_free_names             (ThemesData *tdata);
143
144 static void prefs_themes_set_themes_menu        (GtkComboBox *combo, const ThemesData *tdata);
145
146 static gchar *prefs_themes_get_theme_stats      (const gchar *dirname);
147 static gboolean prefs_themes_is_system_theme    (const gchar *dirname);
148
149 static void prefs_themes_create_widget          (PrefsPage *page, GtkWindow *window, gpointer data);
150 static void prefs_themes_destroy_widget         (PrefsPage *page);
151 static void prefs_themes_save                   (PrefsPage *page);
152
153 static void prefs_themes_foreach_file           (const gchar *dirname, const FileFunc func, gpointer data);
154 static void prefs_themes_file_stats             (const gchar *filename, gpointer data);
155 static void prefs_themes_file_remove            (const gchar *filename, gpointer data);
156 static void prefs_themes_file_install           (const gchar *filename, gpointer data);
157
158
159
160 static void prefs_themes_file_stats(const gchar *filename, gpointer data)
161 {
162         struct stat s;
163         DirInfo    *di = (DirInfo *)data;
164         gint        len;
165         
166         if (0 == g_stat(filename, &s) && 0 != S_ISREG(s.st_mode)) {
167                 di->bytes += s.st_size;
168                 di->files++;
169                 len = strlen(filename);
170                 if (len > 4) {
171                         const gchar *extension = filename+(len-4);
172                         if (!strcmp(extension, ".xpm"))
173                                 di->pixms++;
174                         else if (!strcmp(extension, ".png"))
175                                 di->pixms++;
176                 }
177         }
178 }
179         
180 static void prefs_themes_file_remove(const gchar *filename, gpointer data)
181 {
182         gchar **status = (gchar **)data;
183         gchar *base;
184         
185         if ((*status) != NULL)
186                 return;
187         
188         base = g_path_get_basename(filename);
189         if (TRUE == is_dir_exist(filename)) {
190                 if (strcmp(base, ".") != 0 && strcmp(base, "..") != 0)
191                         g_warning("prefs_themes_file_remove(): subdir in theme dir skipped: '%s'.\n",
192                                                 base);
193         }
194         else if (0 != claws_unlink(filename)) {
195                 (*status) = g_strdup(filename);
196         }
197         g_free(base);
198 }
199
200 static void prefs_themes_file_install(const gchar *filename, gpointer data)
201 {
202         CopyInfo *ci = (CopyInfo *)data;
203         gchar *base;
204         
205         if (ci->status != NULL)
206                 return;
207         
208         base = g_path_get_basename(filename);
209         if (TRUE == is_dir_exist(filename)) {
210                 if (strcmp(base, ".") != 0 && strcmp(base, "..") !=0 )
211                         g_warning("prefs_themes_file_install(): subdir in theme dir skipped: '%s'.\n",
212                                                 base);
213         }
214         else {
215                 gchar *fulldest;
216                 
217                 fulldest = g_strconcat(ci->dest, G_DIR_SEPARATOR_S, base, NULL);
218                 
219                 if (0 != copy_file(filename, fulldest, FALSE)) {
220                         ci->status = g_strdup(filename);
221                 }
222                 g_free(fulldest);
223         }
224         g_free(base);
225 }
226
227 static void prefs_themes_foreach_file(const gchar *dirname, const FileFunc func, gpointer data)
228 {
229         struct dirent *d;
230         DIR           *dp;
231
232         cm_return_if_fail(dirname != NULL);
233         cm_return_if_fail(func != NULL);
234         
235         if ((dp = opendir(dirname)) == NULL) {
236                 debug_print("directory %s not found\n", dirname);
237                 return;
238         }
239
240         while ((d = readdir(dp)) != NULL) {
241                 gchar *entry;
242                 gchar *fullentry;
243
244                 entry     = d->d_name;
245                 fullentry = g_strconcat(dirname, G_DIR_SEPARATOR_S, entry, NULL);
246
247                 (*func)(fullentry, data);
248                 
249                 g_free(fullentry);
250         }
251         closedir(dp);
252 }
253
254 static gboolean prefs_themes_is_system_theme(const gchar *dirname)
255 {
256         gint len;
257         gchar *system_theme_dir;
258         gboolean is_sys = FALSE;
259
260         cm_return_val_if_fail(dirname != NULL, FALSE);
261
262         system_theme_dir = stock_pixmap_get_system_theme_dir_for_theme(NULL);
263         len = strlen(system_theme_dir);
264         if (strlen(dirname) > len && 0 == strncmp(dirname, system_theme_dir, len))
265                 is_sys = TRUE;
266         
267         g_free(system_theme_dir);
268
269         return is_sys;
270 }
271
272 static void prefs_themes_set_themes_menu(GtkComboBox *combo, const ThemesData *tdata)
273 {
274         GtkListStore *store;
275         GtkTreeIter iter;
276         GList     *themes = tdata->names;
277         gint       i = 0, active = 0;
278         GList     *sorted_list = NULL;
279
280         cm_return_if_fail(combo != NULL);
281
282         /* sort theme data list by data name */
283         while (themes != NULL) {
284                 ThemeName *tname = (ThemeName *)(themes->data);
285
286                 sorted_list = g_list_insert_sorted(sorted_list, (gpointer)(tname),
287                                                    (GCompareFunc)prefs_themes_cmp_name);
288
289                 themes = g_list_next(themes);
290         }
291
292         store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
293         
294         /* feed gtk_menu w/ sorted themes names */
295         themes = sorted_list;
296         while (themes != NULL) {
297                 ThemeName *tname = (ThemeName *)(themes->data);
298                 gchar     *tpath = (gchar *)(tname->item->data);
299
300                 gtk_list_store_append(store, &iter);
301                 gtk_list_store_set(store, &iter,
302                                    0, tname->name,
303                                    1, tname->item->data, -1);
304
305                 if (tdata->displayed != NULL && !strcmp2(tdata->displayed,tpath))
306                         active = i;
307                 ++i;
308
309                 themes = g_list_next(themes);
310         }
311
312         g_list_free(sorted_list);
313
314         g_signal_connect(G_OBJECT(combo), "changed",
315                          G_CALLBACK(prefs_themes_menu_item_activated_cb),
316                          NULL);
317
318         gtk_combo_box_set_model(combo, GTK_TREE_MODEL(store));
319         gtk_combo_box_set_active(combo, active);
320 }
321
322 static int prefs_themes_cmp_name(gconstpointer a_p, gconstpointer b_p)
323 {
324         /* compare two ThemeData structures by their name attribute */
325         return strcmp2((gchar *)(((ThemeName*)a_p)->name),
326                                         (gchar *)(((ThemeName*)b_p)->name));
327 }
328
329 static void prefs_themes_get_themes_and_names(ThemesData *tdata)
330 {
331         GList *tpaths;
332         
333         cm_return_if_fail(tdata != NULL);
334         
335         if (tdata->themes != NULL)
336                 stock_pixmap_themes_list_free(tdata->themes);
337         if (tdata->names != NULL)
338                 prefs_themes_free_names(tdata);
339         
340         tdata->themes = stock_pixmap_themes_list_new();
341         
342         tpaths = tdata->themes;
343         while (tpaths != NULL) {
344                 ThemeName *name = g_new0(ThemeName, 1);
345                 gchar *sname = g_path_get_basename((const gchar *)(tpaths->data));
346                 
347                 if (IS_INTERNAL_THEME(sname))
348                         name->name = g_strdup(_("Default internal theme"));
349                 else
350                         name->name = g_strdup(sname);
351                 name->item = tpaths;
352                         
353                 tdata->names = g_list_append(tdata->names, name);
354                 if (!strcmp2(tpaths->data, prefs_common.pixmap_theme_path)) {
355                         tdata->displayed = (gchar *)tpaths->data;
356                 }
357                 tpaths = g_list_next(tpaths);
358                 g_free(sname);  
359         }
360 }
361
362 void prefs_themes_init(void)
363 {
364         ThemesData   *tdata;
365         ThemesPage   *page;
366         GList        *tpaths;
367         static gchar *path[3];
368
369         path[0] = _("Display");
370         path[1] = _("Themes");
371         path[2] = NULL;
372
373         debug_print("Creating preferences for themes...\n");
374         
375         tdata = g_new0(ThemesData, 1);
376         prefs_themes_data = tdata;
377
378         prefs_themes_get_themes_and_names(tdata);
379         
380         page = g_new0(ThemesPage, 1);
381         
382         page->page.path = path;
383         page->page.create_widget = prefs_themes_create_widget;
384         page->page.destroy_widget = prefs_themes_destroy_widget;
385         page->page.save_page = prefs_themes_save;
386         page->page.weight = 130.0;
387         prefs_gtk_register_page((PrefsPage *) page);
388
389         tdata->page = page;
390
391         tpaths = g_list_first(tdata->themes);
392         if (tdata->displayed == NULL)
393                 tdata->displayed = (gchar *)(tpaths->data);
394 }
395
396 static void prefs_themes_free_names(ThemesData *tdata)
397 {
398         GList *names;
399         
400         names = tdata->names;
401         while (names != NULL) {
402                 ThemeName *tn = (ThemeName *)(names->data);
403                 
404                 tn->item = NULL;
405                 g_free(tn->name);
406                 g_free(tn);
407                 
408                 names = g_list_next(names);
409         }
410         g_list_free(tdata->names);
411         tdata->names = NULL;
412 }
413
414 void prefs_themes_done(void)
415 {
416         ThemesData *tdata = prefs_themes_data;
417
418         debug_print("Finished preferences for themes.\n");
419         
420         stock_pixmap_themes_list_free(tdata->themes);
421         prefs_themes_free_names(tdata); 
422         g_free(tdata->page);
423         g_free(tdata);
424 }
425
426 static void prefs_themes_btn_use_clicked_cb(GtkWidget *widget, gpointer data)
427 {
428         ThemesData *tdata = prefs_themes_data;
429         gchar      *theme_str;
430
431         theme_str = tdata->displayed;
432
433         g_free(prefs_common.pixmap_theme_path);
434
435         prefs_common.pixmap_theme_path = g_strdup(theme_str);
436
437         main_window_reflect_prefs_all_real(TRUE);
438         compose_reflect_prefs_pixmap_theme();
439         addrcompl_reflect_prefs_pixmap_theme();
440
441         prefs_themes_update_buttons(tdata);
442 }
443
444 static void prefs_themes_btn_remove_clicked_cb(GtkWidget *widget, gpointer data)
445 {
446         ThemesData *tdata = prefs_themes_data;
447         gchar      *theme_str;
448         gchar      *alert_title = NULL;
449         AlertValue  val = 0;
450         gchar      *tmp = NULL;
451
452         theme_str = tdata->displayed;
453         
454         tmp = g_path_get_basename(theme_str);
455
456         if (IS_SYSTEM_THEME(theme_str)) {
457                 if (!superuser_p()) {
458                         alertpanel_error(_("Only root can remove system themes"));
459                         return;
460                 }
461                 alert_title = g_strdup_printf(_("Remove system theme '%s'"), tmp);
462         }
463         if (NULL == alert_title) {
464                 alert_title = g_strdup_printf(_("Remove theme '%s'"), tmp);
465         }
466
467         g_free(tmp);
468
469         val = alertpanel(alert_title,
470                          _("Are you sure you want to remove this theme?"),
471                          GTK_STOCK_NO, GTK_STOCK_YES, NULL);
472         g_free(alert_title);
473
474         if (G_ALERTALTERNATE == val) {
475                 gchar *status = NULL;
476                 
477                 prefs_themes_foreach_file(theme_str, prefs_themes_file_remove, &status); 
478                 if (0 != rmdir(theme_str)) {
479                         if (status != NULL) {
480                                 alertpanel_error(_("File %s failed\nwhile removing theme."), status);
481                                 g_free(status);
482                         }
483                         else
484                                 alertpanel_error(_("Removing theme directory failed."));
485                 }
486                 else {  
487                         alertpanel_notice(_("Theme removed successfully"));
488                         /* update interface back to first theme */
489                         prefs_themes_get_themes_and_names(tdata);
490                         prefs_themes_set_themes_menu(GTK_COMBO_BOX(tdata->page->op_menu), tdata);
491                         prefs_themes_display_global_stats(tdata);
492                         tdata->displayed = (gchar *)((g_list_first(tdata->themes))->data);
493                         prefs_themes_get_theme_info(tdata);
494                 }
495         }
496 }
497
498 static void prefs_themes_btn_install_clicked_cb(GtkWidget *widget, gpointer data)
499 {
500         gchar      *filename, *source;
501         gchar      *themeinfo, *themename;
502         gchar      *alert_title = NULL;
503         CopyInfo   *cinfo;
504         AlertValue  val = 0;
505         ThemesData *tdata = prefs_themes_data;
506         
507         filename = filesel_select_file_open_folder(_("Select theme folder"), NULL);
508         if (filename == NULL) 
509                 return;
510         
511         if (filename[strlen(filename) - 1] != G_DIR_SEPARATOR)
512                 filename = g_strconcat(filename, G_DIR_SEPARATOR_S, NULL);
513         else
514                 filename = g_strdup(filename);
515
516         cinfo = g_new0(CopyInfo, 1);
517         source = g_path_get_dirname(filename);
518         themename = g_path_get_basename(source);
519         debug_print("Installing '%s' theme from %s\n", themename, filename);
520
521         themeinfo = g_strconcat(source, G_DIR_SEPARATOR_S, THEMEINFO_FILE, NULL);
522         alert_title = g_strdup_printf(_("Install theme '%s'"), themename);
523         if (file_exist(themeinfo, FALSE) == FALSE) {
524                 val = alertpanel(alert_title,
525                                  _("This folder doesn't seem to be a theme folder.\nInstall anyway?"),
526                                  GTK_STOCK_NO, GTK_STOCK_YES, NULL);
527                 if (G_ALERTALTERNATE != val)
528                         goto end_inst;
529         }
530         if (superuser_p ()) {
531                 val = alertpanel(alert_title,
532                                  _("Do you want to install theme for all users?"),
533                                  GTK_STOCK_NO, GTK_STOCK_YES, NULL);
534                 switch (val) {
535                 case G_ALERTALTERNATE:
536                         cinfo->dest = stock_pixmap_get_system_theme_dir_for_theme(
537                                                 themename);
538                         break;
539                 case G_ALERTDEFAULT:
540                         break;
541                 default:
542                         goto end_inst;
543                 }
544         }
545         g_free(alert_title);
546         if (cinfo->dest == NULL) {
547                 cinfo->dest = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
548                                           PIXMAP_THEME_DIR, G_DIR_SEPARATOR_S,
549                                           themename, NULL);
550         }
551         if (TRUE == is_dir_exist(cinfo->dest)) {
552                 AlertValue val = alertpanel_full(_("Theme exists"),
553                                 _("A theme with the same name is\nalready installed in this location.\n\n"
554                                   "Do you want to replace it?"),
555                                 GTK_STOCK_CANCEL, _("Overwrite"), NULL, FALSE,
556                                 NULL, ALERT_WARNING, G_ALERTDEFAULT);
557                 if (val == G_ALERTALTERNATE) {
558                         if (remove_dir_recursive(cinfo->dest) < 0) {
559                                 alertpanel_error(_("Couldn't delete the old theme in %s."), cinfo->dest);
560                                 goto end_inst;
561                         }
562                 } else {
563                         goto end_inst;
564                 }
565         }
566         if (0 != make_dir_hier(cinfo->dest)) {
567                 alertpanel_error(_("Couldn't create destination directory %s."), cinfo->dest);
568                 goto end_inst;
569         }
570         prefs_themes_foreach_file(source, prefs_themes_file_install, cinfo);
571         if (cinfo->status == NULL) {
572                 GList *insted;
573
574                 /* update interface to show newly installed theme */
575                 prefs_themes_get_themes_and_names(tdata);
576                 insted = g_list_find_custom(tdata->themes, 
577                                             (gpointer)(cinfo->dest), 
578                                             (GCompareFunc)strcmp2);
579                 if (NULL != insted) {
580                         alertpanel_notice(_("Theme installed successfully."));
581                         tdata->displayed = (gchar *)(insted->data);
582                         prefs_themes_set_themes_menu(GTK_COMBO_BOX(tdata->page->op_menu), tdata);
583                         prefs_themes_display_global_stats(tdata);
584                         prefs_themes_get_theme_info(tdata);
585                 }
586                 else
587                         alertpanel_error(_("Failed installing theme"));
588         }
589         else
590                 alertpanel_error(_("File %s failed\nwhile installing theme."), cinfo->status);
591 end_inst:
592         g_free(cinfo->dest);
593         g_free(filename);
594         g_free(source);
595         g_free(themeinfo);
596         g_free(cinfo);
597         g_free(themename);
598 }
599
600 static void prefs_themes_menu_item_activated_cb(GtkWidget *widget, gpointer data)
601 {
602         ThemesData *tdata = prefs_themes_data;
603         gchar      *path;
604         GtkTreeModel *model;
605         GtkTreeIter iter;
606         
607         cm_return_if_fail(gtk_combo_box_get_active_iter(GTK_COMBO_BOX(widget), &iter));
608         
609         model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget));                 
610         gtk_tree_model_get(model, &iter, 1, &path, -1); 
611
612         tdata->displayed = path;
613         prefs_themes_get_theme_info(tdata);
614 }
615
616 static void prefs_themes_update_buttons(const ThemesData *tdata)
617 {
618         ThemesPage *theme = tdata->page;
619         gboolean    can_rem, can_use;
620
621         can_use = !IS_CURRENT_THEME(tdata->displayed);
622         can_rem = can_use && !IS_INTERNAL_THEME(tdata->displayed);
623         
624         if (theme->btn_use != NULL)
625                 gtk_widget_set_sensitive(theme->btn_use, can_use);
626         if (theme->btn_remove != NULL)
627                 gtk_widget_set_sensitive(theme->btn_remove, can_rem);
628 }
629
630 /* placeholders may already be utf8 (i18n) */
631 #define SET_LABEL_TEXT_UTF8(label, text)                                \
632 {                                                                       \
633         gchar *tmpstr;                                                  \
634                                                                         \
635         if (!g_utf8_validate(text, -1, NULL))                           \
636                 tmpstr = conv_codeset_strdup(text,                      \
637                         conv_get_locale_charset_str(),  CS_UTF_8);      \
638         else                                                            \
639                 tmpstr = g_strdup(text);                                \
640                                                                         \
641         gtk_label_set_text(GTK_LABEL(label), tmpstr);                   \
642         gtk_label_set_selectable(GTK_LABEL(label), TRUE);               \
643         g_free(tmpstr);                                                 \
644 }
645 static void prefs_themes_display_theme_info(ThemesData *tdata, const ThemeInfo *info)
646 {
647         ThemesPage *theme = tdata->page;
648         gchar *save_prefs_path;
649         gint   i;
650
651         SET_LABEL_TEXT_UTF8(theme->name,        info->name);
652         SET_LABEL_TEXT_UTF8(theme->author,      info->author);
653         SET_LABEL_TEXT_UTF8(theme->url,         info->url);
654         SET_LABEL_TEXT_UTF8(theme->status,      info->status);
655
656         save_prefs_path = prefs_common.pixmap_theme_path;
657         prefs_common.pixmap_theme_path = tdata->displayed;
658         for (i = 0; i < PREVIEW_ICONS; ++i) {
659                 stock_pixbuf_gdk(theme->window, prefs_themes_icons[i], 
660                                 &(theme->pixbufs[i]));
661                 gtk_image_set_from_pixbuf(GTK_IMAGE(theme->icons[i]),
662                                 theme->pixbufs[i]);
663         }
664         prefs_common.pixmap_theme_path = save_prefs_path;
665
666         prefs_themes_update_buttons(tdata);
667 }
668 #undef SET_LABEL_TEXT_UTF8
669
670 static void prefs_themes_display_global_stats(const ThemesData *tdata)
671 {
672         ThemesPage *theme = tdata->page;
673         GList      *tnames = tdata->names;
674         gchar      *gstats;
675         gint        sys = 0;
676         gint        usr = 0;
677         gint        all = 0;
678
679         while (tnames != NULL) {
680                 ThemeName *tname = (ThemeName *)(tnames->data);
681                 gchar     *tpath = (gchar *)(tname->item->data);
682                 
683                 if (IS_SYSTEM_THEME(tpath)) 
684                         ++sys;
685                 else if (!IS_INTERNAL_THEME(tpath)) 
686                         ++usr;
687                 ++all;
688                 tnames = g_list_next(tnames);
689         }
690
691         gstats = g_strdup_printf(_("%d themes available (%d user, %d system, 1 internal)"),
692                                  all, usr, sys);
693         gtk_label_set_text(GTK_LABEL(theme->global), gstats);
694         gtk_label_set_justify (GTK_LABEL (theme->global), GTK_JUSTIFY_LEFT);
695         gtkut_widget_set_small_font_size (theme->global);
696         g_free(gstats);
697 }
698
699 #define INFOFILE_LINE_LEN 80
700
701 #define FGETS_INFOFILE_LINE() \
702         line[0] = '\0'; \
703         if (fgets(line, INFOFILE_LINE_LEN, finfo) != NULL && (len = strlen(line)) > 0) { \
704                 if (line[len - 1] == '\n') line[len - 1] = '\0'; \
705         } \
706         else { \
707                 g_strlcpy(line, _("Unknown"),sizeof(line)); \
708         }
709
710 static void prefs_themes_get_theme_info(ThemesData *tdata)
711 {
712         FILE  *finfo;
713         gchar *sinfo;
714         gchar *path;
715         gchar  line[INFOFILE_LINE_LEN];
716         gint   len;
717         ThemeInfo *info;
718         ThemesPage *theme = tdata->page;
719
720         cm_return_if_fail(theme != NULL);
721         path = tdata->displayed;
722         cm_return_if_fail(path != NULL);
723
724         debug_print("Getting theme info for %s\n", path);
725         
726         info = g_new0(ThemeInfo, 1);
727         
728         if (IS_INTERNAL_THEME(path)) {
729                 info->name = g_strdup(_("Default internal theme"));
730                 info->author = g_strdup(_("The Claws Mail Team"));
731                 info->url = g_strdup(HOMEPAGE_URI);
732                 info->status = g_strdup_printf(_("Internal theme has %d icons"), N_STOCK_PIXMAPS);
733         }
734         else {
735                 sinfo = g_strconcat(path, G_DIR_SEPARATOR_S, THEMEINFO_FILE, NULL);
736                 finfo = g_fopen(sinfo, "r");
737                 if (finfo == NULL) {
738                         info->name = g_strdup(_("No info file available for this theme"));
739                         info->author = g_strdup(_("Unknown"));
740                         info->url = g_strdup(_("Unknown"));
741                 }
742                 else {
743                         FGETS_INFOFILE_LINE()
744                         info->name = g_strdup(line);
745                         FGETS_INFOFILE_LINE()
746                         info->author = g_strdup(line);
747                         FGETS_INFOFILE_LINE()
748                         info->url = g_strdup(line);
749                 
750                         fclose(finfo);
751                 }
752                 g_free(sinfo);
753
754                 info->status = prefs_themes_get_theme_stats(path);
755                 if (info->status == NULL) {
756                         info->status = g_strdup(_("Error: couldn't get theme status"));
757                 }
758         }
759
760         prefs_themes_display_theme_info(tdata, info);
761
762         g_free(info->name);
763         g_free(info->author);
764         g_free(info->url);
765         g_free(info->status);
766         
767         g_free(info);
768 }
769
770 #undef FGETS_INFOFILE_LINE
771
772 static gchar *prefs_themes_get_theme_stats(const gchar *dirname)
773 {
774         gchar   *stats;
775         DirInfo *dinfo;
776
777         dinfo = g_new0(DirInfo, 1);
778         
779         prefs_themes_foreach_file(dirname, prefs_themes_file_stats, dinfo);
780         stats = g_strdup_printf(_("%d files (%d icons), size: %s"), 
781                                 dinfo->files, dinfo->pixms, to_human_readable((goffset)dinfo->bytes));
782         
783         g_free(dinfo);
784         return stats;
785 }
786
787 static void prefs_themes_create_widget(PrefsPage *page, GtkWindow *window, gpointer data)
788 {
789         ThemesPage *prefs_themes = (ThemesPage *)page;
790         ThemesData *tdata = prefs_themes_data;
791
792         GtkWidget *vbox1;
793         GtkWidget *frame1;
794         GtkWidget *vbox2;
795         GtkWidget *hbox3;
796         GtkWidget *menu_themes;
797         GtkWidget *btn_install;
798         GtkWidget *btn_more;
799         GtkWidget *label_global_status;
800         GtkWidget *frame_info;
801         GtkWidget *table1;
802         GtkWidget *label1;
803         GtkWidget *label2;
804         GtkWidget *label3;
805         GtkWidget *label_name;
806         GtkWidget *label_author;
807         GtkWidget *label_url;
808         GtkWidget *label4;
809         GtkWidget *label_status;
810         GtkWidget *frame_preview;
811         GtkWidget *hbox1;
812         GtkWidget *icon_1;
813         GtkWidget *icon_2;
814         GtkWidget *icon_3;
815         GtkWidget *icon_4;
816         GtkWidget *icon_5;
817         GtkWidget *icon_6;
818         GtkWidget *icon_7;
819         GtkWidget *frame_buttons;
820         GtkWidget *hbuttonbox1;
821         GtkWidget *btn_use;
822         GtkWidget *btn_remove;
823         GtkCellRenderer *renderer;
824
825         vbox1 = gtk_vbox_new (FALSE, VSPACING);
826         gtk_container_set_border_width (GTK_CONTAINER (vbox1), VBOX_BORDER);
827         gtk_widget_show (vbox1);
828
829         vbox2 = gtkut_get_options_frame(vbox1, &frame1, _("Selector"));
830
831         hbox3 = gtk_hbox_new (FALSE, 5);
832         gtk_widget_show (hbox3);
833         gtk_box_pack_start (GTK_BOX (vbox2), hbox3, FALSE, FALSE, 0);
834         gtk_container_set_border_width (GTK_CONTAINER (hbox3), 5);
835
836         menu_themes = gtk_combo_box_new();
837         gtk_widget_show (menu_themes);
838         gtk_box_pack_start (GTK_BOX (hbox3), menu_themes, FALSE, FALSE, 0);
839
840         btn_install = gtk_button_new_with_label (_("Install new..."));
841         gtk_widget_show (btn_install);
842         gtk_box_pack_start (GTK_BOX (hbox3), btn_install, FALSE, FALSE, 0);
843         gtkut_widget_set_can_default (btn_install, TRUE);
844
845         btn_more = gtkut_get_link_btn((GtkWidget *)window, THEMES_URI, _("Get more..."));
846         gtk_widget_show (btn_more);
847         gtk_box_pack_start (GTK_BOX (hbox3), btn_more, FALSE, FALSE, 0);
848
849         label_global_status = gtk_label_new ("");
850         gtk_widget_show (label_global_status);
851         gtk_box_pack_start (GTK_BOX (vbox2), label_global_status, FALSE, FALSE, 0);
852         gtk_label_set_justify (GTK_LABEL (label_global_status), GTK_JUSTIFY_LEFT);
853         gtk_misc_set_alignment (GTK_MISC (label_global_status), 0, 0.5);
854         gtk_misc_set_padding (GTK_MISC (label_global_status), 6, 0);
855
856         PACK_FRAME(vbox1, frame_info, _("Information"));
857
858         table1 = gtk_table_new (4, 2, FALSE);
859         gtk_widget_show (table1);
860         gtk_container_add (GTK_CONTAINER (frame_info), table1);
861
862         label1 = gtk_label_new (_("Name: "));
863         gtk_widget_show (label1);
864         gtk_table_attach (GTK_TABLE (table1), label1, 0, 1, 0, 1,
865                         (GtkAttachOptions) (GTK_FILL),
866                         (GtkAttachOptions) (0), 8, 2);
867         gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
868         gtk_misc_set_alignment (GTK_MISC (label1), 0, 0.5);
869
870         label2 = gtk_label_new (_("Author: "));
871         gtk_widget_show (label2);
872         gtk_table_attach (GTK_TABLE (table1), label2, 0, 1, 1, 2,
873                         (GtkAttachOptions) (GTK_FILL),
874                         (GtkAttachOptions) (0), 8, 2);
875         gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
876         gtk_misc_set_alignment (GTK_MISC (label2), 0, 0.5);
877
878         label3 = gtk_label_new (_("URL:"));
879         gtk_widget_show (label3);
880         gtk_table_attach (GTK_TABLE (table1), label3, 0, 1, 2, 3,
881                         (GtkAttachOptions) (GTK_FILL),
882                         (GtkAttachOptions) (0), 8, 2);
883         gtk_misc_set_alignment (GTK_MISC (label3), 0, 0.5);
884
885         label_name = gtk_label_new ("");
886         gtk_widget_show (label_name);
887         gtk_table_attach (GTK_TABLE (table1), label_name, 1, 2, 0, 1,
888                         (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
889                         (GtkAttachOptions) (0), 0, 0);
890         gtk_misc_set_alignment (GTK_MISC (label_name), 0, 0.5);
891
892         label_author = gtk_label_new ("");
893         gtk_widget_show (label_author);
894         gtk_table_attach (GTK_TABLE (table1), label_author, 1, 2, 1, 2,
895                         (GtkAttachOptions) (GTK_FILL),
896                         (GtkAttachOptions) (0), 0, 0);
897         gtk_misc_set_alignment (GTK_MISC (label_author), 0, 0.5);
898
899         label_url = gtk_label_new ("");
900         gtk_widget_show (label_url);
901         gtk_table_attach (GTK_TABLE (table1), label_url, 1, 2, 2, 3,
902                         (GtkAttachOptions) (GTK_FILL),
903                         (GtkAttachOptions) (0), 0, 0);
904         gtk_misc_set_alignment (GTK_MISC (label_url), 0, 0.5);
905
906         label4 = gtk_label_new (_("Status:"));
907         gtk_widget_show (label4);
908         gtk_table_attach (GTK_TABLE (table1), label4, 0, 1, 3, 4,
909                         (GtkAttachOptions) (GTK_FILL),
910                         (GtkAttachOptions) (0), 8, 2);
911         gtk_misc_set_alignment (GTK_MISC (label4), 0, 0.5);
912
913         label_status = gtk_label_new ("");
914         gtk_widget_show (label_status);
915         gtk_table_attach (GTK_TABLE (table1), label_status, 1, 2, 3, 4,
916                         (GtkAttachOptions) (GTK_FILL),
917                         (GtkAttachOptions) (0), 0, 0);
918         gtk_misc_set_alignment (GTK_MISC (label_status), 0, 0.5);
919
920         PACK_FRAME(vbox1, frame_preview, _("Preview"));
921
922         hbox1 = gtk_hbox_new (FALSE, 0);
923         gtk_widget_show (hbox1);
924         gtk_container_add (GTK_CONTAINER (frame_preview), hbox1);
925
926         icon_1 = gtk_image_new();
927         gtk_widget_show (icon_1);
928         gtk_box_pack_start (GTK_BOX (hbox1), icon_1, TRUE, TRUE, 2);
929         gtk_misc_set_padding (GTK_MISC (icon_1), 0, 5);
930
931         icon_2 = gtk_image_new();
932         gtk_widget_show (icon_2);
933         gtk_box_pack_start (GTK_BOX (hbox1), icon_2, TRUE, TRUE, 2);
934         gtk_misc_set_padding (GTK_MISC (icon_2), 0, 5);
935
936         icon_3 = gtk_image_new();
937         gtk_widget_show (icon_3);
938         gtk_box_pack_start (GTK_BOX (hbox1), icon_3, TRUE, TRUE, 2);
939         gtk_misc_set_padding (GTK_MISC (icon_3), 0, 5);
940
941         icon_4 = gtk_image_new();
942         gtk_widget_show (icon_4);
943         gtk_box_pack_start (GTK_BOX (hbox1), icon_4, TRUE, TRUE, 2);
944         gtk_misc_set_padding (GTK_MISC (icon_4), 0, 5);
945
946         icon_5 = gtk_image_new();
947         gtk_widget_show (icon_5);
948         gtk_box_pack_start (GTK_BOX (hbox1), icon_5, TRUE, TRUE, 2);
949         gtk_misc_set_padding (GTK_MISC (icon_5), 0, 5);
950
951         icon_6 = gtk_image_new();
952         gtk_widget_show (icon_6);
953         gtk_box_pack_start (GTK_BOX (hbox1), icon_6, TRUE, TRUE, 0);
954         gtk_misc_set_padding (GTK_MISC (icon_6), 0, 5);
955
956         icon_7 = gtk_image_new();
957         gtk_widget_show (icon_7);
958         gtk_box_pack_start (GTK_BOX (hbox1), icon_7, TRUE, TRUE, 0);
959         gtk_misc_set_padding (GTK_MISC (icon_7), 0, 5);
960
961         PACK_FRAME(vbox1, frame_buttons, _("Actions"));
962
963         hbuttonbox1 = gtk_hbutton_box_new ();
964         gtk_widget_show (hbuttonbox1);
965         gtk_container_add (GTK_CONTAINER (frame_buttons), hbuttonbox1);
966         gtk_container_set_border_width (GTK_CONTAINER (hbuttonbox1), 5);
967         gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox1), GTK_BUTTONBOX_START);
968         gtk_box_set_spacing (GTK_BOX (hbuttonbox1), 5);
969
970         btn_use = gtk_button_new_with_label (_("Use this"));
971         gtk_widget_show (btn_use);
972         gtk_container_add (GTK_CONTAINER (hbuttonbox1), btn_use);
973         gtkut_widget_set_can_default (btn_use, TRUE);
974
975         btn_remove = gtk_button_new_with_label (_("Remove"));
976         gtk_widget_show (btn_remove);
977         gtk_container_add (GTK_CONTAINER (hbuttonbox1), btn_remove);
978         gtkut_widget_set_can_default (btn_remove, TRUE);
979
980         g_signal_connect(G_OBJECT(btn_use), "clicked",
981                          G_CALLBACK(prefs_themes_btn_use_clicked_cb),
982                          NULL);
983         g_signal_connect(G_OBJECT(btn_remove), "clicked",
984                          G_CALLBACK(prefs_themes_btn_remove_clicked_cb),
985                          NULL);
986         g_signal_connect(G_OBJECT(btn_install), "clicked",
987                          G_CALLBACK(prefs_themes_btn_install_clicked_cb),
988                          NULL);
989
990         prefs_themes->window = GTK_WIDGET(window);
991         
992         prefs_themes->name   = label_name;
993         prefs_themes->author = label_author;
994         prefs_themes->url    = label_url;
995         prefs_themes->status = label_status;
996         prefs_themes->global = label_global_status;
997
998         prefs_themes->icons[0] = icon_1;
999         prefs_themes->icons[1] = icon_2;
1000         prefs_themes->icons[2] = icon_3;
1001         prefs_themes->icons[3] = icon_4;
1002         prefs_themes->icons[4] = icon_5;
1003         prefs_themes->icons[5] = icon_6;
1004         prefs_themes->icons[6] = icon_7;
1005         
1006         prefs_themes->btn_use     = btn_use;
1007         prefs_themes->btn_remove  = btn_remove;
1008         prefs_themes->btn_install = btn_install;
1009         prefs_themes->btn_more    = btn_more;
1010
1011         prefs_themes->op_menu     = menu_themes;
1012
1013         prefs_themes->page.widget = vbox1;
1014         
1015         prefs_themes_set_themes_menu(GTK_COMBO_BOX(menu_themes), tdata);
1016         renderer = gtk_cell_renderer_text_new();
1017         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(menu_themes), renderer, TRUE);
1018         gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(menu_themes), renderer,
1019                                         "text", 0, NULL);
1020         
1021         prefs_themes_get_theme_info(tdata);
1022         prefs_themes_display_global_stats(tdata);
1023 }
1024
1025 static void prefs_themes_destroy_widget(PrefsPage *page)
1026 {
1027         /* ThemesPage *theme = (ThemesPage *)page; */
1028 }
1029
1030 static void prefs_themes_save(PrefsPage *page)
1031 {
1032         /* ThemesPage *theme = (ThemesPage *)page; */
1033 }
1034