2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2014-2015 Ricardo Mones and 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/>.
21 #include "claws-features.h"
25 #include <glib/gi18n.h>
27 #include "libravatar.h"
30 #include <gtk/filesel.h>
33 #include "libravatar_prefs.h"
34 #include "libravatar_cache.h"
35 #include "prefs_common.h"
36 #include "prefs_gtk.h"
37 #include "alertpanel.h"
39 #define PREFS_BLOCK_NAME "Libravatar"
40 #define NUM_DEF_BUTTONS 7
41 #define CUSTOM_URL_BUTTON_INDEX 6
42 /* cache interval goes from 1 hour to 30 days */
43 #define INTERVAL_MIN_H 1.0
44 #define INTERVAL_MAX_H 720.0
45 /* timeout interval goes from 0 seconds (= use general timeout value)
46 to (general timeout value - 1) seconds */
47 #define TIMEOUT_MIN_S 0.0
49 LibravatarPrefs libravatarprefs;
50 GHashTable *libravatarmisses;
52 struct LibravatarPrefsPage
56 GtkWidget *cache_interval_spin;
57 GtkWidget *cache_icons_check;
58 GtkWidget *defm_radio[NUM_DEF_BUTTONS];
59 GtkWidget *defm_url_text;
60 GtkWidget *allow_redirects_check;
61 #if (defined USE_GNUTLS && GLIB_CHECK_VERSION(2,22,0))
62 GtkWidget *allow_federated_check;
67 struct LibravatarPrefsPage libravatarprefs_page;
69 static PrefParam param[] = {
70 { "base_url", "http://cdn.libravatar.org/avatar",
71 &libravatarprefs.base_url,
72 P_STRING, NULL, NULL, NULL },
73 { "cache_interval", "24",
74 &libravatarprefs.cache_interval,
75 P_INT, NULL, NULL, NULL },
76 { "cache_icons", "TRUE",
77 &libravatarprefs.cache_icons,
78 P_BOOL, NULL, NULL, NULL },
79 { "default_mode", "0",
80 &libravatarprefs.default_mode,
81 P_INT, NULL, NULL, NULL },
82 { "default_mode_url", "",
83 &libravatarprefs.default_mode_url,
84 P_STRING, NULL, NULL, NULL },
85 { "allow_redirects", "TRUE",
86 &libravatarprefs.allow_redirects,
87 P_BOOL, NULL, NULL, NULL },
88 #if (defined USE_GNUTLS && GLIB_CHECK_VERSION(2,22,0))
89 { "allow_federated", "TRUE",
90 &libravatarprefs.allow_federated,
91 P_BOOL, NULL, NULL, NULL },
94 &libravatarprefs.timeout,
95 P_INT, NULL, NULL, NULL },
96 {NULL, NULL, NULL, P_OTHER, NULL, NULL, NULL}
99 static GtkWidget *create_checkbox(gchar *label, gchar *hint)
101 GtkWidget *cb = gtk_check_button_new_with_mnemonic(label);
102 CLAWS_SET_TIP(cb, hint);
108 static void cache_icons_check_toggled_cb(GtkToggleButton *button, gpointer data)
110 gtk_widget_set_sensitive(libravatarprefs_page.cache_interval_spin,
111 gtk_toggle_button_get_active(button));
114 static GtkWidget *labeled_spinner_box(gchar *label, GtkWidget *spinner, gchar *units, gchar *hint)
116 GtkWidget *lbl, *lbla, *hbox;
118 lbl = gtk_label_new(label);
119 gtk_widget_show(lbl);
120 lbla = gtk_label_new(units);
121 gtk_widget_show(lbla);
122 hbox = gtk_hbox_new(FALSE, 6);
124 CLAWS_SET_TIP(spinner, hint);
126 gtk_box_pack_start(GTK_BOX(hbox), lbl, FALSE, FALSE, 0);
127 gtk_box_pack_start(GTK_BOX(hbox), spinner, FALSE, FALSE, 0);
128 gtk_box_pack_start(GTK_BOX(hbox), lbla, FALSE, FALSE, 0);
133 static gchar *avatar_stats_label_markup(AvatarCacheStats *stats)
136 return g_strdup(g_strconcat("<span color=\"red\">",
137 _("Error reading cache stats"), "</span>", NULL));
139 if (stats->errors > 0)
140 return g_markup_printf_escaped(g_strconcat("<span color=\"red\">",
141 _("Using %s in %d files, %d "
142 "directories, %d others and %d errors"), "</span>", NULL),
143 to_human_readable((goffset) stats->bytes),
149 return g_strdup_printf(
150 _("Using %s in %d files, %d directories and %d others"),
151 to_human_readable((goffset) stats->bytes),
157 static void cache_clean_button_clicked_cb(GtkButton *button, gpointer data)
159 GtkLabel *label = (GtkLabel *) data;
161 AvatarCleanupResult *acr;
164 val = alertpanel_full(_("Clear icon cache"),
165 _("Are you sure you want to remove all cached avatar icons?"),
166 GTK_STOCK_NO, GTK_STOCK_YES, NULL, FALSE,
167 NULL, ALERT_WARNING, G_ALERTDEFAULT);
168 if (val != G_ALERTALTERNATE)
171 debug_print("cleaning missing cache");
172 misses = g_hash_table_size(libravatarmisses);
173 g_hash_table_remove_all(libravatarmisses);
175 debug_print("cleaning disk cache");
176 acr = libravatar_cache_clean();
178 alertpanel_error(_("Not enough memory for operation"));
182 if (acr->e_stat == 0 && acr->e_unlink == 0) {
183 alertpanel_notice(_("Icon cache successfully cleared:\n"
184 "• %u missing entries removed.\n"
185 "• %u files removed."),
186 misses, acr->removed);
187 gtk_label_set_markup(label, g_strconcat("<span color=\"#006400\">",
188 _("Icon cache successfully cleared!"), "</span>", NULL));
191 alertpanel_warning(_("Errors clearing icon cache:\n"
192 "• %u missing entries removed.\n"
193 "• %u files removed.\n"
194 "• %u files failed to be read.\n"
195 "• %u files couldn't be removed."),
196 misses, acr->removed, acr->e_stat, acr->e_unlink);
197 gtk_label_set_markup(label, g_strconcat("<span color=\"red\">",
198 _("Error clearing icon cache."), "</span>", NULL));
200 gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
204 static GtkWidget *p_create_frame_cache(struct LibravatarPrefsPage *page)
206 GtkWidget *vbox, *checkbox, *spinner, *hbox, *label, *button;
208 AvatarCacheStats *stats;
211 vbox = gtk_vbox_new(FALSE, 6);
213 checkbox = create_checkbox(_("_Use cached icons"),
214 _("Keep icons on disk for reusing instead "
215 "of making another network request"));
216 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
217 libravatarprefs.cache_icons);
218 g_signal_connect(checkbox, "toggled",
219 G_CALLBACK(cache_icons_check_toggled_cb), NULL);
220 page->cache_icons_check = checkbox;
222 adj = (GtkAdjustment *) gtk_adjustment_new(
223 libravatarprefs.cache_interval,
224 INTERVAL_MIN_H, INTERVAL_MAX_H, 1.0,
226 spinner = gtk_spin_button_new(adj, 1.0, 0);
227 gtk_widget_show(spinner);
228 gtk_widget_set_sensitive(spinner, libravatarprefs.cache_icons);
229 hbox = labeled_spinner_box(_("Cache refresh interval"), spinner, _("hours"), NULL);
230 page->cache_interval_spin = spinner;
232 gtk_box_pack_start(GTK_BOX(vbox), checkbox, FALSE, FALSE, 0);
233 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
235 label = gtk_label_new(NULL);
236 gtk_widget_show(label);
237 stats = libravatar_cache_stats();
238 markup = avatar_stats_label_markup(stats);
239 gtk_label_set_markup(GTK_LABEL(label), markup);
241 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
243 button = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
244 gtk_widget_show(button);
245 g_signal_connect(button, "clicked",
246 G_CALLBACK(cache_clean_button_clicked_cb), label);
247 gtk_widget_set_sensitive(button, (stats != NULL && stats->bytes > 0));
249 hbox = gtk_hbox_new(FALSE, 6);
250 gtk_widget_show(hbox);
251 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
252 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
253 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
261 static void default_mode_radio_button_cb(GtkToggleButton *button, gpointer data)
266 if (gtk_toggle_button_get_active(button) != TRUE)
269 mode = *((guint *)data);
270 is_url = (mode == DEF_MODE_URL)? TRUE: FALSE;
272 gtk_widget_set_sensitive(libravatarprefs_page.defm_url_text, is_url);
273 if (is_url) /* custom URL requires following redirects */
274 gtk_toggle_button_set_active(
275 GTK_TOGGLE_BUTTON(libravatarprefs_page.allow_redirects_check),
278 if (mode == DEF_MODE_NONE) {
279 prefs_common_get_prefs()->enable_avatars = AVATARS_ENABLE_BOTH;
281 /* don't waste time with headers that won't be displayed */
282 prefs_common_get_prefs()->enable_avatars = AVATARS_DISABLE;
283 /* empty missing cache when switching to generated */
284 g_hash_table_remove_all(libravatarmisses);
288 static const guint radio_value[] = {
298 static GtkWidget *p_create_frame_missing(struct LibravatarPrefsPage *page)
300 GtkWidget *vbox, *radio[NUM_DEF_BUTTONS], *hbox, *entry;
301 gboolean enable = FALSE;
303 gchar *radio_label[] = {
312 gchar *radio_hint[] = {
314 _("The unobtrusive low-contrast greyish silhouette"),
315 _("A generated geometric pattern"),
316 _("A generated full-body monster"),
317 _("A generated almost unique face"),
318 _("A generated 8-bit arcade-style pixelated image"),
319 _("Redirect to a user provided URL")
322 vbox = gtk_vbox_new(FALSE, 6);
324 for (i = 0; i < NUM_DEF_BUTTONS; ++i) {
325 enable = (libravatarprefs.default_mode == radio_value[i])? TRUE: FALSE;
327 radio[i] = gtk_radio_button_new_with_label_from_widget(
328 (i > 0)? GTK_RADIO_BUTTON(radio[i - 1]): NULL, radio_label[i]);
329 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio[i]), enable);
330 if (i == CUSTOM_URL_BUTTON_INDEX) {
331 /* set related entry next to radio button */
332 entry = gtk_entry_new_with_max_length(MAX_URL_LENGTH);
333 CLAWS_SET_TIP(entry, _("Enter the URL you want to be "
334 "redirected when no user icon is available. "
335 "Leave an empty URL to use the default "
336 "libravatar orange icon."));
337 gtk_widget_show(entry);
338 gtk_entry_set_text(GTK_ENTRY(entry),
339 libravatarprefs.default_mode_url);
340 hbox = gtk_hbox_new(FALSE, 6);
341 gtk_box_pack_start(GTK_BOX(hbox), radio[i], FALSE, FALSE, 0);
342 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
343 gtk_widget_set_sensitive(entry,
344 (libravatarprefs.default_mode == DEF_MODE_URL)
346 page->defm_url_text = entry;
347 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
349 gtk_box_pack_start(GTK_BOX(vbox), radio[i], FALSE, FALSE, 0);
351 g_signal_connect(radio[i], "toggled",
352 G_CALLBACK(default_mode_radio_button_cb),
353 (gpointer) &(radio_value[i]));
354 CLAWS_SET_TIP(radio[i], radio_hint[i]);
355 gtk_widget_show(radio[i]);
356 page->defm_radio[i] = radio[i];
358 if (e == 0) { /* unknown value, go default */
359 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio[0]), TRUE);
360 libravatarprefs.default_mode = DEF_MODE_NONE;
362 /* don't waste time with headers that won't be displayed */
363 prefs_common_get_prefs()->enable_avatars =
364 (libravatarprefs.default_mode == DEF_MODE_NONE)
365 ? AVATARS_ENABLE_BOTH: AVATARS_DISABLE;
372 static GtkWidget *p_create_frame_network(struct LibravatarPrefsPage *page)
374 GtkWidget *vbox, *chk_redirects, *spinner, *hbox;
376 #if (defined USE_GNUTLS && GLIB_CHECK_VERSION(2,22,0))
377 GtkWidget *chk_federated;
380 vbox = gtk_vbox_new(FALSE, 6);
382 chk_redirects = create_checkbox(_("_Allow redirects to other sites"),
383 _("Follow redirect responses received from "
384 "libravatar server to other avatar "
385 "services like gravatar.com"));
386 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(chk_redirects),
387 libravatarprefs.allow_redirects);
388 page->allow_redirects_check = chk_redirects;
389 gtk_box_pack_start(GTK_BOX(vbox), chk_redirects, FALSE, FALSE, 0);
391 #if (defined USE_GNUTLS && GLIB_CHECK_VERSION(2,22,0))
392 chk_federated = create_checkbox(_("_Enable federated servers"),
393 _("Try to get avatar from sender's domain "
394 "libravatar server"));
395 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(chk_federated),
396 libravatarprefs.allow_federated);
397 page->allow_federated_check = chk_federated;
398 gtk_box_pack_start(GTK_BOX(vbox), chk_federated, FALSE, FALSE, 0);
401 adj = (GtkAdjustment *) gtk_adjustment_new(
402 libravatarprefs.timeout,
404 (prefs_common_get_prefs()->io_timeout_secs > 0)
405 ? (prefs_common_get_prefs()->io_timeout_secs - 1)
408 spinner = gtk_spin_button_new(adj, 1.0, 0);
409 gtk_widget_show(spinner);
410 hbox = labeled_spinner_box(_("Request timeout"), spinner, _("second(s)"),
411 _("Set to 0 to use global socket I/O timeout. "
412 "Maximum value must be also less than global socket "
414 page->timeout = spinner;
415 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
421 ┌─Icon cache───────────────────────────────────────────┐
422 │ [✔] Use cached icons │
423 │ Cache refresh interval [ 24 |⬘] hours │
424 │ Using X KB in Y files and Z directories [Clear] │
425 └──────────────────────────────────────────────────────┘
426 ┌─Default missing icon mode────────────────────────────┐
433 │ ( ) Custom URL [___________________________________] │
434 └──────────────────────────────────────────────────────┘
435 ┌─Network──────────────────────────────────────────────┐
436 │ [✔] Allow redirects │
437 │ [✔] Federated servers │
438 │ Timeout [ 10 |⬘] seconds │
439 └──────────────────────────────────────────────────────┘
441 static void libravatar_prefs_create_widget_func(PrefsPage * _page,
445 struct LibravatarPrefsPage *page = (struct LibravatarPrefsPage *) _page;
446 GtkWidget *vbox, *vbox1, *vbox2, *vbox3, *frame;
448 vbox1 = p_create_frame_cache(page);
449 vbox2 = p_create_frame_missing(page);
450 vbox3 = p_create_frame_network(page);
452 vbox = gtk_vbox_new(FALSE, 6);
453 gtk_container_set_border_width(GTK_CONTAINER(vbox), VBOX_BORDER);
455 PACK_FRAME (vbox, frame, _("Icon cache"));
456 gtk_container_set_border_width(GTK_CONTAINER(vbox1), 6);
457 gtk_container_add(GTK_CONTAINER(frame), vbox1);
459 PACK_FRAME (vbox, frame, _("Default missing icon mode"));
460 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 6);
461 gtk_container_add(GTK_CONTAINER(frame), vbox2);
463 PACK_FRAME (vbox, frame, _("Network"));
464 gtk_container_set_border_width(GTK_CONTAINER(vbox3), 6);
465 gtk_container_add(GTK_CONTAINER(frame), vbox3);
467 gtk_widget_show_all(vbox);
468 page->page.widget = vbox;
471 static void libravatar_prefs_destroy_widget_func(PrefsPage *_page)
476 static void libravatar_save_config(void)
481 debug_print("Saving Libravatar Page\n");
483 rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
484 pfile = prefs_write_open(rcpath);
486 if (!pfile || (prefs_set_block_label(pfile, PREFS_BLOCK_NAME) < 0))
489 if (prefs_write_param(param, pfile->fp) < 0) {
490 g_warning("failed to write Libravatar configuration to file");
491 prefs_file_close_revert(pfile);
494 if (fprintf(pfile->fp, "\n") < 0) {
495 FILE_OP_ERROR(rcpath, "fprintf");
496 prefs_file_close_revert(pfile);
498 prefs_file_close(pfile);
501 static void libravatar_prefs_save_func(PrefsPage * _page)
503 struct LibravatarPrefsPage *page = (struct LibravatarPrefsPage *) _page;
507 libravatarprefs.cache_icons = gtk_toggle_button_get_active(
508 GTK_TOGGLE_BUTTON(page->cache_icons_check));
510 libravatarprefs.cache_interval = gtk_spin_button_get_value_as_int(
511 GTK_SPIN_BUTTON(page->cache_interval_spin));
513 for (i = 0; i < NUM_DEF_BUTTONS; ++i) {
514 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(page->defm_radio[i]))) {
515 libravatarprefs.default_mode = radio_value[i];
520 if (libravatarprefs.default_mode_url != NULL) {
521 g_free(libravatarprefs.default_mode_url);
523 libravatarprefs.default_mode_url = gtk_editable_get_chars(
524 GTK_EDITABLE(page->defm_url_text), 0, -1);
526 libravatarprefs.allow_redirects = gtk_toggle_button_get_active(
527 GTK_TOGGLE_BUTTON(page->allow_redirects_check));
529 #if (defined USE_GNUTLS && GLIB_CHECK_VERSION(2,22,0))
530 libravatarprefs.allow_federated = gtk_toggle_button_get_active(
531 GTK_TOGGLE_BUTTON(page->allow_federated_check));
534 libravatarprefs.timeout = gtk_spin_button_get_value_as_int(
535 GTK_SPIN_BUTTON(page->timeout));
537 libravatar_save_config();
540 void libravatar_prefs_init(void)
542 static gchar *path[3];
545 path[0] = _("Plugins");
546 path[1] = _("Libravatar");
549 prefs_set_default(param);
550 rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
551 prefs_read_config(param, PREFS_BLOCK_NAME, rcpath, NULL);
554 libravatarprefs_page.page.path = path;
555 libravatarprefs_page.page.create_widget = libravatar_prefs_create_widget_func;
556 libravatarprefs_page.page.destroy_widget = libravatar_prefs_destroy_widget_func;
557 libravatarprefs_page.page.save_page = libravatar_prefs_save_func;
558 libravatarprefs_page.page.weight = 40.0;
560 prefs_gtk_register_page((PrefsPage *) &libravatarprefs_page);
563 void libravatar_prefs_done(void)
565 prefs_gtk_unregister_page((PrefsPage *) &libravatarprefs_page);