Libravatar: cache usage info label
[claws.git] / src / plugins / libravatar / libravatar_prefs.c
1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2014-2015 Ricardo Mones and the Claws Mail Team
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #ifdef HAVE_CONFIG_H
20 #  include "config.h"
21 #include "claws-features.h"
22 #endif
23
24 #include <glib.h>
25 #include <glib/gi18n.h>
26
27 #include "libravatar.h"
28
29 #include <gtk/gtk.h>
30 #include <gtk/filesel.h>
31
32 #include "defs.h"
33 #include "libravatar_prefs.h"
34 #include "libravatar_cache.h"
35 #include "prefs_common.h"
36 #include "prefs_gtk.h"
37
38 #define PREFS_BLOCK_NAME "Libravatar"
39 #define NUM_DEF_BUTTONS 7
40 #define CUSTOM_URL_BUTTON_INDEX 6
41 /* cache interval goes from 1 hour to 30 days */
42 #define INTERVAL_MIN_H 1.0
43 #define INTERVAL_MAX_H 720.0
44 /* timeout interval goes from 0 seconds (= use general timeout value)
45    to (general timeout value - 1) seconds */
46 #define TIMEOUT_MIN_S 0.0
47
48 LibravatarPrefs libravatarprefs;
49 GHashTable *libravatarmisses;
50
51 struct LibravatarPrefsPage
52 {
53         PrefsPage page;
54
55         GtkWidget *cache_interval_spin;
56         GtkWidget *cache_icons_check;
57         GtkWidget *defm_radio[NUM_DEF_BUTTONS];
58         GtkWidget *defm_url_text;
59         GtkWidget *allow_redirects_check;
60 #if (defined USE_GNUTLS && GLIB_CHECK_VERSION(2,22,0))
61         GtkWidget *allow_federated_check;
62 #endif
63         GtkWidget *timeout;
64 };
65
66 struct LibravatarPrefsPage libravatarprefs_page;
67
68 static PrefParam param[] = {
69         { "base_url", "http://cdn.libravatar.org/avatar",
70           &libravatarprefs.base_url,
71           P_STRING, NULL, NULL, NULL },
72         { "cache_interval", "24",
73           &libravatarprefs.cache_interval,
74           P_INT, NULL, NULL, NULL },
75         { "cache_icons", "TRUE",
76           &libravatarprefs.cache_icons,
77           P_BOOL, NULL, NULL, NULL },
78         { "default_mode", "0",
79           &libravatarprefs.default_mode,
80           P_INT, NULL, NULL, NULL },
81         { "default_mode_url", "",
82           &libravatarprefs.default_mode_url,
83           P_STRING, NULL, NULL, NULL },
84         { "allow_redirects", "TRUE",
85           &libravatarprefs.allow_redirects,
86           P_BOOL, NULL, NULL, NULL },
87 #if (defined USE_GNUTLS && GLIB_CHECK_VERSION(2,22,0))
88         { "allow_federated", "TRUE",
89           &libravatarprefs.allow_federated,
90           P_BOOL, NULL, NULL, NULL },
91 #endif
92         { "timeout", "0",
93           &libravatarprefs.timeout,
94           P_INT, NULL, NULL, NULL },
95         {NULL, NULL, NULL, P_OTHER, NULL, NULL, NULL}
96 };
97
98 static GtkWidget *create_checkbox(gchar *label, gchar *hint)
99 {
100         GtkWidget *cb = gtk_check_button_new_with_mnemonic(label);
101         CLAWS_SET_TIP(cb, hint);
102         gtk_widget_show(cb);
103
104         return cb;
105 }
106
107 static void cache_icons_check_toggled_cb(GtkToggleButton *button, gpointer data)
108 {
109         gtk_widget_set_sensitive(libravatarprefs_page.cache_interval_spin,
110                                  gtk_toggle_button_get_active(button));
111 }
112
113 static GtkWidget *labeled_spinner_box(gchar *label, GtkWidget *spinner, gchar *units, gchar *hint)
114 {
115         GtkWidget *lbl, *lbla, *hbox;
116
117         lbl = gtk_label_new(label);
118         gtk_widget_show(lbl);
119         lbla = gtk_label_new(units);
120         gtk_widget_show(lbla);
121         hbox = gtk_hbox_new(FALSE, 6);
122         if (hint != NULL) {
123                 CLAWS_SET_TIP(spinner, hint);
124         }
125         gtk_box_pack_start(GTK_BOX(hbox), lbl, FALSE, FALSE, 0);
126         gtk_box_pack_start(GTK_BOX(hbox), spinner, FALSE, FALSE, 0);
127         gtk_box_pack_start(GTK_BOX(hbox), lbla, FALSE, FALSE, 0);
128
129         return hbox;
130 }
131
132 static gchar *avatar_stats_label_markup(AvatarCacheStats *stats)
133 {
134         if (stats == NULL)
135                 return g_strdup(
136                         _("<span color=\"red\">Error reading cache stats</span>"));
137
138         if (stats->errors > 0)
139                 return g_markup_printf_escaped(
140                         _("<span color=\"red\">Using %s in %d files, %d "
141                         "directories, %d others and %d errors</span>"),
142                         to_human_readable((goffset) stats->bytes),
143                         stats->files,
144                         stats->dirs,
145                         stats->others,
146                         stats->errors);
147
148         return g_strdup_printf(
149                 _("Using %s in %d files, %d directories and %d others"),
150                 to_human_readable((goffset) stats->bytes),
151                 stats->files,
152                 stats->dirs,
153                 stats->others);
154 }
155
156 static GtkWidget *p_create_frame_cache(struct LibravatarPrefsPage *page)
157 {
158         GtkWidget *vbox, *checkbox, *spinner, *hbox, *label;
159         GtkAdjustment *adj;
160         AvatarCacheStats *stats;
161         gchar *markup;
162
163         vbox =  gtk_vbox_new(FALSE, 6);
164
165         checkbox = create_checkbox(_("_Use cached icons"),
166                                    _("Keep icons on disk for reusing instead "
167                                      "of making another network request"));
168         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
169                                      libravatarprefs.cache_icons);
170         g_signal_connect(checkbox, "toggled",
171                          G_CALLBACK(cache_icons_check_toggled_cb), NULL);
172         page->cache_icons_check = checkbox;
173
174         adj = (GtkAdjustment *) gtk_adjustment_new(
175                                         libravatarprefs.cache_interval,
176                                         INTERVAL_MIN_H, INTERVAL_MAX_H, 1.0,
177                                         0.0, 0.0);
178         spinner = gtk_spin_button_new(adj, 1.0, 0);
179         gtk_widget_show(spinner);
180         gtk_widget_set_sensitive(spinner, libravatarprefs.cache_icons);
181         hbox = labeled_spinner_box(_("Cache refresh interval"), spinner, _("hours"), NULL);
182         page->cache_interval_spin = spinner;
183
184         gtk_box_pack_start(GTK_BOX(vbox), checkbox, FALSE, FALSE, 0);
185         gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
186
187         label = gtk_label_new(NULL);
188         gtk_widget_show(label);
189         stats = libravatar_cache_stats();
190         markup = avatar_stats_label_markup(stats);
191         if (stats != NULL)
192                 g_free(stats);
193         gtk_label_set_markup(GTK_LABEL(label), markup);
194         g_free(markup);
195         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
196         gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
197
198         return vbox;
199 }
200
201 static void default_mode_radio_button_cb(GtkToggleButton *button, gpointer data)
202 {
203         guint mode;
204         gboolean is_url;
205
206         if (gtk_toggle_button_get_active(button) != TRUE)
207                 return;
208
209         mode = *((guint *)data);
210         is_url = (mode == DEF_MODE_URL)? TRUE: FALSE;
211
212         gtk_widget_set_sensitive(libravatarprefs_page.defm_url_text, is_url);
213         if (is_url) /* custom URL requires following redirects */
214                 gtk_toggle_button_set_active(
215                         GTK_TOGGLE_BUTTON(libravatarprefs_page.allow_redirects_check),
216                         TRUE);
217
218         if (mode == DEF_MODE_NONE) {
219                 prefs_common_get_prefs()->enable_avatars = AVATARS_ENABLE_BOTH;
220         } else {
221                 /* don't waste time with headers that won't be displayed */
222                 prefs_common_get_prefs()->enable_avatars = AVATARS_DISABLE;
223                 /* empty missing cache when switching to generated */
224                 g_hash_table_remove_all(libravatarmisses);
225         }
226 }
227
228 static const guint radio_value[] = {
229         DEF_MODE_NONE,
230         DEF_MODE_MM,
231         DEF_MODE_IDENTICON,
232         DEF_MODE_MONSTERID,
233         DEF_MODE_WAVATAR,
234         DEF_MODE_RETRO,
235         DEF_MODE_URL
236 };
237
238 static GtkWidget *p_create_frame_missing(struct LibravatarPrefsPage *page)
239 {
240         GtkWidget *vbox, *radio[NUM_DEF_BUTTONS], *hbox, *entry;
241         gboolean enable = FALSE;
242         int i, e = 0;
243         gchar *radio_label[] = {
244                 _("None"),
245                 _("Mystery man"),
246                 _("Identicon"),
247                 _("MonsterID"),
248                 _("Wavatar"),
249                 _("Retro"),
250                 _("Custom URL")
251         };
252         gchar *radio_hint[] = {
253                 _("A blank image"),
254                 _("The unobtrusive low-contrast greyish silhouette"),
255                 _("A generated geometric pattern"),
256                 _("A generated full-body monster"),
257                 _("A generated almost unique face"),
258                 _("A generated 8-bit arcade-style pixelated image"),
259                 _("Redirect to a user provided URL")
260         };
261
262         vbox =  gtk_vbox_new(FALSE, 6);
263
264         for (i = 0; i < NUM_DEF_BUTTONS; ++i) {
265                 enable = (libravatarprefs.default_mode == radio_value[i])? TRUE: FALSE;
266                 e += enable? 1: 0;
267                 radio[i] = gtk_radio_button_new_with_label_from_widget(
268                                 (i > 0)? GTK_RADIO_BUTTON(radio[i - 1]): NULL, radio_label[i]);
269                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio[i]), enable);
270                 if (i == CUSTOM_URL_BUTTON_INDEX) {
271                         /* set related entry next to radio button */
272                         entry = gtk_entry_new_with_max_length(MAX_URL_LENGTH);
273                         CLAWS_SET_TIP(entry, _("Enter the URL you want to be "
274                                 "redirected when no user icon is available. "
275                                 "Leave an empty URL to use the default "
276                                 "libravatar orange icon."));
277                         gtk_widget_show(entry);
278                         gtk_entry_set_text(GTK_ENTRY(entry),
279                                 libravatarprefs.default_mode_url);
280                         hbox = gtk_hbox_new(FALSE, 6);
281                         gtk_box_pack_start(GTK_BOX(hbox), radio[i], FALSE, FALSE, 0);
282                         gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
283                         gtk_widget_set_sensitive(entry,
284                                 (libravatarprefs.default_mode == DEF_MODE_URL)
285                                 ? TRUE: FALSE);
286                         page->defm_url_text = entry;
287                         gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
288                 } else {
289                         gtk_box_pack_start(GTK_BOX(vbox), radio[i], FALSE, FALSE, 0);
290                 }
291                 g_signal_connect(radio[i], "toggled",
292                                  G_CALLBACK(default_mode_radio_button_cb),
293                                  (gpointer) &(radio_value[i]));
294                 CLAWS_SET_TIP(radio[i], radio_hint[i]);
295                 gtk_widget_show(radio[i]);
296                 page->defm_radio[i] = radio[i];
297         }
298         if (e == 0) { /* unknown value, go default */
299                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio[0]), TRUE);
300                 libravatarprefs.default_mode = DEF_MODE_NONE;
301         }
302         /* don't waste time with headers that won't be displayed */
303         prefs_common_get_prefs()->enable_avatars =
304                 (libravatarprefs.default_mode == DEF_MODE_NONE)
305                 ? AVATARS_ENABLE_BOTH: AVATARS_DISABLE;
306
307
308
309         return vbox;
310 }
311
312 static GtkWidget *p_create_frame_network(struct LibravatarPrefsPage *page)
313 {
314         GtkWidget *vbox, *chk_redirects, *spinner, *hbox;
315         GtkAdjustment *adj;
316 #if (defined USE_GNUTLS && GLIB_CHECK_VERSION(2,22,0))
317         GtkWidget *chk_federated;
318 #endif
319
320         vbox =  gtk_vbox_new(FALSE, 6);
321
322         chk_redirects = create_checkbox(_("_Allow redirects to other sites"),
323                                    _("Follow redirect responses received from "
324                                      "libravatar server to other avatar "
325                                      "services like gravatar.com"));
326         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(chk_redirects),
327                                      libravatarprefs.allow_redirects);
328         page->allow_redirects_check = chk_redirects;
329         gtk_box_pack_start(GTK_BOX(vbox), chk_redirects, FALSE, FALSE, 0);
330
331 #if (defined USE_GNUTLS && GLIB_CHECK_VERSION(2,22,0))
332         chk_federated = create_checkbox(_("_Enable federated servers"),
333                                 _("Try to get avatar from sender's domain "
334                                   "libravatar server"));
335         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(chk_federated),
336                                      libravatarprefs.allow_federated);
337         page->allow_federated_check = chk_federated;
338         gtk_box_pack_start(GTK_BOX(vbox), chk_federated, FALSE, FALSE, 0);
339 #endif
340
341         adj = (GtkAdjustment *) gtk_adjustment_new(
342                                         libravatarprefs.timeout,
343                                         TIMEOUT_MIN_S,
344                                         (prefs_common_get_prefs()->io_timeout_secs > 0)
345                                         ? (prefs_common_get_prefs()->io_timeout_secs - 1)
346                                         : 0,
347                                         1.0, 0.0, 0.0);
348         spinner = gtk_spin_button_new(adj, 1.0, 0);
349         gtk_widget_show(spinner);
350         hbox = labeled_spinner_box(_("Request timeout"), spinner, _("seconds"),
351                 _("Set to 0 to use global socket I/O timeout. "
352                   "Maximum value must be also less than global socket "
353                   "I/O timeout."));
354         page->timeout = spinner;
355         gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
356
357         return vbox;
358 }
359
360 /*
361   ┌─Icon cache───────────────────────────────────────────┐
362   │ [✔] Use cached icons                                 │
363   │ Cache refresh interval [ 24 |⬘] hours                │
364   │ Using X KB in Y files and Z directories              │
365   └──────────────────────────────────────────────────────┘
366   ┌─Default missing icon mode────────────────────────────┐
367   │ (•) None                                             │
368   │ ( ) Mystery man                                      │
369   │ ( ) Identicon                                        │
370   │ ( ) MonsterID                                        │
371   │ ( ) Wavatar                                          │
372   │ ( ) Retro                                            │
373   │ ( ) Custom URL [___________________________________] │
374   └──────────────────────────────────────────────────────┘
375   ┌─Network──────────────────────────────────────────────┐
376   │ [✔] Allow redirects                                  │
377   │ [✔] Federated servers                                │
378   │ Timeout [ 10 |⬘] seconds                             │
379   └──────────────────────────────────────────────────────┘
380  */
381 static void libravatar_prefs_create_widget_func(PrefsPage * _page,
382                                                 GtkWindow * window,
383                                                 gpointer data)
384 {
385         struct LibravatarPrefsPage *page = (struct LibravatarPrefsPage *) _page;
386         GtkWidget *vbox, *vbox1, *vbox2, *vbox3, *frame;
387
388         vbox1 = p_create_frame_cache(page);
389         vbox2 = p_create_frame_missing(page);
390         vbox3 = p_create_frame_network(page);
391
392         vbox = gtk_vbox_new(FALSE, 6);
393         gtk_container_set_border_width(GTK_CONTAINER(vbox), VBOX_BORDER);
394
395         PACK_FRAME (vbox, frame, _("Icon cache"));
396         gtk_container_set_border_width(GTK_CONTAINER(vbox1), 6);
397         gtk_container_add(GTK_CONTAINER(frame), vbox1);
398
399         PACK_FRAME (vbox, frame, _("Default missing icon mode"));
400         gtk_container_set_border_width(GTK_CONTAINER(vbox2), 6);
401         gtk_container_add(GTK_CONTAINER(frame), vbox2);
402
403         PACK_FRAME (vbox, frame, _("Network"));
404         gtk_container_set_border_width(GTK_CONTAINER(vbox3), 6);
405         gtk_container_add(GTK_CONTAINER(frame), vbox3);
406
407         gtk_widget_show_all(vbox);
408         page->page.widget = vbox;
409 }
410
411 static void libravatar_prefs_destroy_widget_func(PrefsPage *_page)
412 {
413         /* nothing */
414 }
415
416 static void libravatar_save_config(void)
417 {
418         PrefFile *pfile;
419         gchar *rcpath;
420
421         debug_print("Saving Libravatar Page\n");
422
423         rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
424         pfile = prefs_write_open(rcpath);
425         g_free(rcpath);
426         if (!pfile || (prefs_set_block_label(pfile, PREFS_BLOCK_NAME) < 0))
427                 return;
428
429         if (prefs_write_param(param, pfile->fp) < 0) {
430                 g_warning("failed to write Libravatar configuration to file");
431                 prefs_file_close_revert(pfile);
432                 return;
433         }
434         if (fprintf(pfile->fp, "\n") < 0) {
435                 FILE_OP_ERROR(rcpath, "fprintf");
436                 prefs_file_close_revert(pfile);
437         } else
438                 prefs_file_close(pfile);
439 }
440
441 static void libravatar_prefs_save_func(PrefsPage * _page)
442 {
443         struct LibravatarPrefsPage *page = (struct LibravatarPrefsPage *) _page;
444         int i;
445
446         /* cache */
447         libravatarprefs.cache_icons = gtk_toggle_button_get_active(
448                 GTK_TOGGLE_BUTTON(page->cache_icons_check));
449         /* cache interval */
450         libravatarprefs.cache_interval = gtk_spin_button_get_value_as_int(
451                 GTK_SPIN_BUTTON(page->cache_interval_spin));
452         /* default mode */
453         for (i = 0; i < NUM_DEF_BUTTONS; ++i) {
454                 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(page->defm_radio[i]))) {
455                         libravatarprefs.default_mode = radio_value[i];
456                         break;
457                 }
458         }
459         /* custom url */
460         if (libravatarprefs.default_mode_url != NULL) {
461                 g_free(libravatarprefs.default_mode_url);
462         }
463         libravatarprefs.default_mode_url = gtk_editable_get_chars(
464                 GTK_EDITABLE(page->defm_url_text), 0, -1);
465         /* redirects */
466         libravatarprefs.allow_redirects = gtk_toggle_button_get_active(
467                 GTK_TOGGLE_BUTTON(page->allow_redirects_check));
468         /* federation */
469 #if (defined USE_GNUTLS && GLIB_CHECK_VERSION(2,22,0))
470         libravatarprefs.allow_federated = gtk_toggle_button_get_active(
471                 GTK_TOGGLE_BUTTON(page->allow_federated_check));
472 #endif
473         /* timeout */
474         libravatarprefs.timeout = gtk_spin_button_get_value_as_int(
475                 GTK_SPIN_BUTTON(page->timeout));
476
477         libravatar_save_config();
478 }
479
480 void libravatar_prefs_init(void)
481 {
482         static gchar *path[3];
483         gchar *rcpath;
484
485         path[0] = _("Plugins");
486         path[1] = _("Libravatar");
487         path[2] = NULL;
488
489         prefs_set_default(param);
490         rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
491         prefs_read_config(param, PREFS_BLOCK_NAME, rcpath, NULL);
492         g_free(rcpath);
493
494         libravatarprefs_page.page.path = path;
495         libravatarprefs_page.page.create_widget = libravatar_prefs_create_widget_func;
496         libravatarprefs_page.page.destroy_widget = libravatar_prefs_destroy_widget_func;
497         libravatarprefs_page.page.save_page = libravatar_prefs_save_func;
498         libravatarprefs_page.page.weight = 40.0;
499
500         prefs_gtk_register_page((PrefsPage *) &libravatarprefs_page);
501 }
502
503 void libravatar_prefs_done(void)
504 {
505         prefs_gtk_unregister_page((PrefsPage *) &libravatarprefs_page);
506 }
507