2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2003-2015 Hiroyuki Yamamoto & the Claws Mail team
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.
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.
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/>.
34 #include <glib/gi18n.h>
39 #include "prefs_common.h"
40 #include "prefs_gtk.h"
42 #include "gtk/gtkutils.h"
43 #include "gtk/prefswindow.h"
44 #include "gtk/filesel.h"
46 #include "stock_pixmap.h"
47 #include "mainwindow.h"
49 #include "alertpanel.h"
50 #include "addr_compl.h"
51 #include "file-utils.h"
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))
57 #define PREVIEW_ICONS 7
59 typedef struct _ThemesPage
63 GtkWidget
*window
; /* do not modify */
66 GtkWidget
*btn_install
;
75 GtkWidget
*icons
[PREVIEW_ICONS
];
77 GtkWidget
*btn_remove
;
79 GdkPixbuf
*pixbufs
[PREVIEW_ICONS
];
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
;
90 typedef struct _ThemeInfo
98 typedef struct _ThemeName
104 typedef struct _ThemesData
112 typedef void (*FileFunc
) (const gchar
*filename
, gpointer data
);
114 typedef struct _DirInfo
{
118 /* extensions info */
119 const char **supported
;
123 typedef struct _CopyInfo
{
128 static ThemesData
*prefs_themes_data
;
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
,
136 STOCK_PIXMAP_MIME_TEXT_PLAIN
,
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
);
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
);
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
);
160 static void prefs_themes_set_themes_menu (GtkComboBox
*combo
, const ThemesData
*tdata
);
162 static gchar
*prefs_themes_get_theme_stats (const gchar
*dirname
);
163 static gboolean
prefs_themes_is_system_theme (const gchar
*dirname
);
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
);
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
);
176 static void prefs_themes_file_stats(const gchar
*filename
, gpointer data
)
181 GError
*error
= NULL
;
186 DirInfo
*di
= (DirInfo
*)data
;
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
);
195 g_warning(error
->message
);
200 if (g_file_info_get_file_type(fi
) != G_FILE_TYPE_REGULAR
) {
205 size
= g_file_info_get_size(fi
);
209 if ((i
= g_stat(filename
, &s
)) != 0) {
210 debug_print("g_stat on '%s' failed: %d\n", filename
, i
);
213 if (!S_ISREG(s
.st_mode
)) {
221 len
= strlen(filename
);
222 for (i
= 0; (di
->supported
)[i
] != NULL
; ++i
) {
223 gint curlen
= (di
->length
)[i
];
226 const gchar
*extension
= filename
+ (len
- curlen
);
227 if (!strcmp(extension
, (di
->supported
)[i
])) {
234 static void prefs_themes_file_remove(const gchar
*filename
, gpointer data
)
236 gchar
**status
= (gchar
**)data
;
239 if ((*status
) != NULL
)
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'.",
248 else if (0 != claws_unlink(filename
)) {
249 (*status
) = g_strdup(filename
);
254 static void prefs_themes_file_install(const gchar
*filename
, gpointer data
)
256 CopyInfo
*ci
= (CopyInfo
*)data
;
259 if (ci
->status
!= NULL
)
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'.",
271 fulldest
= g_strconcat(ci
->dest
, G_DIR_SEPARATOR_S
, base
, NULL
);
273 if (0 != copy_file(filename
, fulldest
, FALSE
)) {
274 ci
->status
= g_strdup(filename
);
281 static void prefs_themes_foreach_file(const gchar
*dirname
, const FileFunc func
, gpointer data
)
286 GError
*error
= NULL
;
288 cm_return_if_fail(dirname
!= NULL
);
289 cm_return_if_fail(func
!= NULL
);
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
);
298 while ((entry
= g_dir_read_name(dp
)) != NULL
) {
300 fullentry
= g_strconcat(dirname
, G_DIR_SEPARATOR_S
, entry
, NULL
);
302 (*func
)(fullentry
, data
);
309 static gboolean
prefs_themes_is_system_theme(const gchar
*dirname
)
312 gchar
*system_theme_dir
;
313 gboolean is_sys
= FALSE
;
315 cm_return_val_if_fail(dirname
!= NULL
, FALSE
);
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
))
322 g_free(system_theme_dir
);
327 static void prefs_themes_set_themes_menu(GtkComboBox
*combo
, const ThemesData
*tdata
)
331 GList
*themes
= tdata
->names
;
332 gint i
= 0, active
= 0;
333 GList
*sorted_list
= NULL
;
335 cm_return_if_fail(combo
!= NULL
);
337 /* sort theme data list by data name */
338 while (themes
!= NULL
) {
339 ThemeName
*tname
= (ThemeName
*)(themes
->data
);
341 sorted_list
= g_list_insert_sorted(sorted_list
, (gpointer
)(tname
),
342 (GCompareFunc
)prefs_themes_cmp_name
);
344 themes
= g_list_next(themes
);
347 store
= gtk_list_store_new(2, G_TYPE_STRING
, G_TYPE_POINTER
);
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
);
355 gtk_list_store_append(store
, &iter
);
356 gtk_list_store_set(store
, &iter
,
358 1, tname
->item
->data
, -1);
360 if (tdata
->displayed
!= NULL
&& !g_strcmp0(tdata
->displayed
,tpath
))
364 themes
= g_list_next(themes
);
367 g_list_free(sorted_list
);
369 g_signal_connect(G_OBJECT(combo
), "changed",
370 G_CALLBACK(prefs_themes_menu_item_activated_cb
),
373 gtk_combo_box_set_model(combo
, GTK_TREE_MODEL(store
));
374 gtk_combo_box_set_active(combo
, active
);
377 static int prefs_themes_cmp_name(gconstpointer a_p
, gconstpointer b_p
)
379 /* compare two ThemeData structures by their name attribute */
380 return g_strcmp0((gchar
*)(((ThemeName
*)a_p
)->name
),
381 (gchar
*)(((ThemeName
*)b_p
)->name
));
384 static void prefs_themes_get_themes_and_names(ThemesData
*tdata
)
388 cm_return_if_fail(tdata
!= NULL
);
390 stock_pixmap_themes_list_free(tdata
->themes
);
391 prefs_themes_free_names(tdata
);
393 tdata
->themes
= stock_pixmap_themes_list_new();
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
));
400 if (IS_INTERNAL_THEME(sname
))
401 name
->name
= g_strdup(_("Default internal theme"));
403 name
->name
= g_strdup(sname
);
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
;
410 tpaths
= g_list_next(tpaths
);
415 void prefs_themes_init(void)
420 static gchar
*path
[3];
422 path
[0] = _("Display");
423 path
[1] = _("Themes");
426 debug_print("Creating preferences for themes...\n");
428 tdata
= g_new0(ThemesData
, 1);
429 prefs_themes_data
= tdata
;
431 prefs_themes_get_themes_and_names(tdata
);
433 page
= g_new0(ThemesPage
, 1);
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
);
444 tpaths
= g_list_first(tdata
->themes
);
445 if (tdata
->displayed
== NULL
)
446 tdata
->displayed
= (gchar
*)(tpaths
->data
);
448 if (prefs_common
.pixmap_scaling_auto
)
449 prefs_common
.pixmap_scaling_ppi
= prefs_themes_get_adjusted_ppi();
453 static void prefs_themes_free_names(ThemesData
*tdata
)
457 cm_return_if_fail(tdata
!= NULL
);
459 if (tdata
->names
== NULL
)
462 names
= tdata
->names
;
463 while (names
!= NULL
) {
464 ThemeName
*tn
= (ThemeName
*)(names
->data
);
470 names
= g_list_next(names
);
472 g_list_free(tdata
->names
);
476 void prefs_themes_done(void)
478 ThemesData
*tdata
= prefs_themes_data
;
480 debug_print("Finished preferences for themes.\n");
482 stock_pixmap_themes_list_free(tdata
->themes
);
483 prefs_themes_free_names(tdata
);
488 static void prefs_themes_btn_remove_clicked_cb(GtkWidget
*widget
, gpointer data
)
490 ThemesData
*tdata
= prefs_themes_data
;
492 gchar
*alert_title
= NULL
;
496 theme_str
= tdata
->displayed
;
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
);
502 alert_title
= g_strdup_printf(_("Remove theme '%s'"), tmp
);
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
);
511 if (G_ALERTALTERNATE
== val
) {
512 gchar
*status
= NULL
;
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
);
521 alertpanel_error(_("Removing theme directory failed."));
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
);
535 static void prefs_themes_btn_install_clicked_cb(GtkWidget
*widget
, gpointer data
)
537 gchar
*filename
, *source
;
538 gchar
*themeinfo
, *themename
;
539 gchar
*alert_title
= NULL
;
542 ThemesData
*tdata
= prefs_themes_data
;
544 filename
= filesel_select_file_open_folder(_("Select theme folder"), NULL
);
545 if (filename
== NULL
)
548 if (filename
[strlen(filename
) - 1] != G_DIR_SEPARATOR
)
549 filename
= g_strconcat(filename
, G_DIR_SEPARATOR_S
, NULL
);
551 filename
= g_strdup(filename
);
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
);
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
) {
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
);
576 case G_ALERTALTERNATE
:
577 cinfo
->dest
= stock_pixmap_get_system_theme_dir_for_theme(
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
,
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."),
608 if (0 != make_dir_hier(cinfo
->dest
)) {
609 alertpanel_error(_("Couldn't create destination directory %s."),
613 prefs_themes_foreach_file(source
, prefs_themes_file_install
, cinfo
);
614 if (cinfo
->status
== NULL
) {
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
);
630 alertpanel_error(_("Failed installing theme"));
633 alertpanel_error(_("File %s failed\nwhile installing theme."), cinfo
->status
);
643 static void prefs_themes_menu_item_activated_cb(GtkWidget
*widget
, gpointer data
)
645 ThemesData
*tdata
= prefs_themes_data
;
650 cm_return_if_fail(gtk_combo_box_get_active_iter(GTK_COMBO_BOX(widget
), &iter
));
652 model
= gtk_combo_box_get_model(GTK_COMBO_BOX(widget
));
653 gtk_tree_model_get(model
, &iter
, 1, &path
, -1);
655 tdata
->displayed
= path
;
656 prefs_themes_get_theme_info(tdata
);
660 #define MM_INCH 0.0393700787402
661 static gdouble
prefs_themes_compute_ppi(GdkScreen
*screen
)
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
);
669 debug_print("screen is %f x %f pixels, %f x %f mm\n", wp
, hp
, wi
, hi
);
671 /* https://en.wikipedia.org/wiki/Pixel_density */
674 dp
= sqrt(wp
* wp
+ hp
* hp
);
675 di
= sqrt(wi
* wi
+ hi
* hi
);
677 return (di
!= 0.0)? dp
/ di
: 0.0;
680 static gdouble
prefs_themes_get_adjusted_ppi(void)
683 GdkScreen
* screen
= gdk_screen_get_default();
685 if (screen
== NULL
) { /* oops! */
686 g_warning("unable to get default GDK screen");
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
);
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.
699 return MAX(ppi
, cppi
);
702 static void prefs_themes_checkbtn_enable_scaling_toggled_cb (GtkWidget
*widget
, gpointer data
)
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
));
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
);
715 static void prefs_themes_checkbtn_scaling_auto_toggled_cb(GtkWidget
*widget
, gpointer data
)
717 ThemesPage
*page
= (ThemesPage
*) data
;
718 gboolean automatic
= gtk_toggle_button_get_active(
719 GTK_TOGGLE_BUTTON (widget
));
721 gtk_widget_set_sensitive(page
->spinbtn_scaling_ppi
, !automatic
);
722 gtk_widget_set_sensitive(page
->label_scaling_ppi
, !automatic
);
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());
731 static void prefs_themes_update_buttons(const ThemesData
*tdata
)
733 ThemesPage
*theme
= tdata
->page
;
734 gboolean can_rem
, can_use
;
736 can_use
= !IS_CURRENT_THEME(tdata
->displayed
);
737 can_rem
= can_use
&& !IS_INTERNAL_THEME(tdata
->displayed
);
739 if (theme
->btn_remove
!= NULL
)
740 gtk_widget_set_sensitive(theme
->btn_remove
, can_rem
);
743 /* placeholders may already be utf8 (i18n) */
744 #define SET_LABEL_TEXT_UTF8(label, text) \
748 if (!g_utf8_validate(text, -1, NULL)) \
749 tmpstr = conv_codeset_strdup(text, \
750 conv_get_locale_charset_str(), CS_UTF_8); \
752 tmpstr = g_strdup(text); \
754 gtk_label_set_text(GTK_LABEL(label), tmpstr); \
755 gtk_label_set_selectable(GTK_LABEL(label), TRUE); \
758 static void prefs_themes_display_theme_info(ThemesData
*tdata
, const ThemeInfo
*info
)
760 ThemesPage
*theme
= tdata
->page
;
761 gchar
*save_prefs_path
;
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
);
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
]),
776 prefs_common
.pixmap_theme_path
= save_prefs_path
;
778 prefs_themes_update_buttons(tdata
);
780 #undef SET_LABEL_TEXT_UTF8
782 static void prefs_themes_display_global_stats(const ThemesData
*tdata
)
784 ThemesPage
*theme
= tdata
->page
;
785 GList
*tnames
= tdata
->names
;
791 while (tnames
!= NULL
) {
792 ThemeName
*tname
= (ThemeName
*)(tnames
->data
);
793 gchar
*tpath
= (gchar
*)(tname
->item
->data
);
795 if (IS_SYSTEM_THEME(tpath
))
797 else if (!IS_INTERNAL_THEME(tpath
))
800 tnames
= g_list_next(tnames
);
803 gstats
= g_strdup_printf(_("%d themes available (%d user, %d system, 1 internal)"),
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
);
811 #define INFOFILE_LINE_LEN 80
813 #define FGETS_INFOFILE_LINE() \
815 if (claws_fgets(line, INFOFILE_LINE_LEN, finfo) != NULL && (len = strlen(line)) > 0) { \
816 if (line[len - 1] == '\n') line[len - 1] = '\0'; \
819 g_strlcpy(line, _("Unknown"),sizeof(line)); \
822 static void prefs_themes_get_theme_info(ThemesData
*tdata
)
827 gchar line
[INFOFILE_LINE_LEN
];
830 ThemesPage
*theme
= tdata
->page
;
832 cm_return_if_fail(theme
!= NULL
);
833 path
= tdata
->displayed
;
834 cm_return_if_fail(path
!= NULL
);
836 debug_print("Getting theme info for %s\n", path
);
838 info
= g_new0(ThemeInfo
, 1);
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
);
847 sinfo
= g_strconcat(path
, G_DIR_SEPARATOR_S
, THEMEINFO_FILE
, NULL
);
848 finfo
= claws_fopen(sinfo
, "r");
850 info
->name
= g_strdup(_("No info file available for this theme"));
851 info
->author
= g_strdup(_("Unknown"));
852 info
->url
= g_strdup(_("Unknown"));
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
);
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"));
872 prefs_themes_display_theme_info(tdata
, info
);
875 g_free(info
->author
);
877 g_free(info
->status
);
882 #undef FGETS_INFOFILE_LINE
884 static gchar
*prefs_themes_get_theme_stats(const gchar
*dirname
)
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
]);
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
);
906 static void prefs_themes_create_widget(PrefsPage
*page
, GtkWindow
*window
, gpointer data
)
908 ThemesPage
*prefs_themes
= (ThemesPage
*)page
;
909 ThemesData
*tdata
= prefs_themes_data
;
915 GtkWidget
*menu_themes
;
916 GtkWidget
*btn_install
;
918 GtkWidget
*label_global_status
;
919 GtkWidget
*frame_info
;
924 GtkWidget
*label_name
;
925 GtkWidget
*label_author
;
926 GtkWidget
*label_url
;
928 GtkWidget
*label_status
;
929 GtkWidget
*frame_preview
;
938 GtkWidget
*frame_buttons
;
939 GtkWidget
*hbuttonbox1
;
940 GtkWidget
*btn_remove
;
941 GtkCellRenderer
*renderer
;
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
;
952 vbox1
= gtk_vbox_new (FALSE
, VSPACING
);
953 gtk_container_set_border_width (GTK_CONTAINER (vbox1
), 5);
954 gtk_widget_show (vbox1
);
956 vbox2
= gtkut_get_options_frame(vbox1
, &frame1
, _("Selector"));
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);
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);
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
);
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);
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);
983 PACK_FRAME(vbox1
, frame_info
, _("Information"));
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);
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);
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);
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);
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);
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);
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);
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);
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);
1048 PACK_FRAME(vbox1
, frame_preview
, _("Preview"));
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);
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);
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);
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);
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);
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);
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);
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);
1090 PACK_FRAME(vbox1
, frame_buttons
, _("Actions"));
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);
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
);
1105 PACK_FRAME(vbox1
, frame_scaling
, _("SVG rendering"));
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
);
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"));
1116 hbox3
= gtk_hbox_new (FALSE
, 5);
1117 gtk_widget_show (hbox3
);
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
,
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
,
1132 gtk_box_pack_start (GTK_BOX (vbox2
), hbox3
, FALSE
, FALSE
, 0);
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
);
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
);
1155 g_signal_connect(G_OBJECT(checkbtn_enable_scaling
), "toggled",
1156 G_CALLBACK(prefs_themes_checkbtn_enable_scaling_toggled_cb
),
1158 g_signal_connect(G_OBJECT(checkbtn_scaling_auto
), "toggled",
1159 G_CALLBACK(prefs_themes_checkbtn_scaling_auto_toggled_cb
),
1163 g_signal_connect(G_OBJECT(btn_remove
), "clicked",
1164 G_CALLBACK(prefs_themes_btn_remove_clicked_cb
),
1166 g_signal_connect(G_OBJECT(btn_install
), "clicked",
1167 G_CALLBACK(prefs_themes_btn_install_clicked_cb
),
1170 prefs_themes
->window
= GTK_WIDGET(window
);
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
;
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
;
1186 prefs_themes
->btn_remove
= btn_remove
;
1187 prefs_themes
->btn_install
= btn_install
;
1188 prefs_themes
->btn_more
= btn_more
;
1190 prefs_themes
->op_menu
= menu_themes
;
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
;
1200 prefs_themes
->page
.widget
= vbox1
;
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
,
1208 prefs_themes_get_theme_info(tdata
);
1209 prefs_themes_display_global_stats(tdata
);
1212 static void prefs_themes_destroy_widget(PrefsPage
*page
)
1214 /* ThemesPage *theme = (ThemesPage *)page; */
1217 static void prefs_themes_save(PrefsPage
*page
)
1219 ThemesData
*tdata
= prefs_themes_data
;
1220 gchar
*theme_str
= tdata
->displayed
;
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
;
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
));
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
);
1242 prefs_common
.pixmap_theme_path
= g_strdup(theme_str
);
1244 main_window_reflect_prefs_all_real(TRUE
);
1245 compose_reflect_prefs_pixmap_theme();
1246 addrcompl_reflect_prefs_pixmap_theme();
1248 prefs_themes_update_buttons(tdata
);
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();