first try to journal_store_get_latest_id() for sqlite
[k8lowj.git] / src / settings.c
blob2eb34f01cf04eb22f175eeb5bd53406894fa5929
1 /* logjam - a GTK client for LiveJournal.
2 * Copyright (C) 2000-2003 Evan Martin <evan@livejournal.com>
3 */
4 #include "gtk-all.h"
5 #include "util-gtk.h"
7 #include <stdlib.h>
8 #include <string.h>
10 #include "account.h"
11 #include "checkfriends.h"
12 #include "conf.h"
13 #include "groupedbox.h"
14 #include "jam.h"
15 #include "jamview.h"
16 #include "music.h"
17 #include "remote.h"
18 #include "security.h"
19 #include "settings.h"
20 #include "smartquotes.h"
21 #include "spawn.h"
22 #include "tie.h"
23 #include "util.h"
26 /* what's this? all of these funny structures in the settings box?
27 * well, instead of creating and tearing down all of these widgets, i
28 * try to pull out shared functionality.
30 * it's loosely inspired by george lebl's pong http://www.5z.com/jirka/pong-documentation/
31 * but i've never actaully read how that works.
33 * we have a collection of SettingsWidgets that track their name and
34 * configuration option, and then it's all nicely automated through functions
35 * of the form sw_*().
38 typedef enum {
39 SW_TOGGLE,
40 SW_RADIO,
41 SW_TEXT,
42 SW_INTEGER,
43 SW_COMBO,
44 SW_SPIN_INTEGER,
45 SW_COMMAND,
46 SW_CUSTOM
47 } SettingsWidgetType;
50 typedef struct _SettingsWidget SettingsWidget;
51 struct _SettingsWidget {
52 const char *name;
53 void *conf;
54 SettingsWidgetType type;
55 const char *caption;
56 gpointer data; /* extra pointer, to be used as needed. */
57 gpointer data2; /* extra pointer for wimps. */
58 GtkWidget *subwidget;
59 GtkWidget *widget;
63 static void run_cfmask_settings_dlg (SettingsWidget *sw);
66 static SettingsWidget settingswidgets[] = {
67 {"ui_revertusejournal", &conf.options.revertusejournal,
68 SW_TOGGLE, N_("_Revert to primary journal after posting on a community"), NULL},
69 {"ui_defaultsecurity", &conf.defaultsecurity,
70 SW_CUSTOM, N_("Default _security on posts:")},
71 {"ui_autosave", &conf.options.autosave,
72 SW_TOGGLE, N_("Automatically save _drafts (for crash recovery)")},
74 {"ui_allowmultipleinstances", &conf.options.allowmultipleinstances,
75 SW_TOGGLE, N_("Allow multiple _instances of LogJam to run simultaneously")},
76 #ifdef HAVE_GTKSPELL
77 {"ui_spellcheck", &conf.options.usespellcheck,
78 SW_TOGGLE, N_("_Use spell check")},
79 {"ui_spell_language", &conf.spell_language,
80 SW_TEXT, N_("Entry _language:")},
81 #endif
82 {"ui_smartquotes", &conf.options.smartquotes,
83 SW_TOGGLE, N_("Automatically change _quotes to matching pairs")},
84 {"ui_smartquotes_ru", &conf.options.smartquotes_russian,
85 SW_TOGGLE, N_("'Russian' _typographical mode")},
86 {"ui_font", &conf.uifont,
87 SW_CUSTOM, N_("Entry display font:")},
88 {"ui_docklet", &conf.options.docklet,
89 SW_TOGGLE, N_("Add icon to system _tray (for GNOME/KDE/etc. dock)")},
91 {"web_spawn", &conf.spawn_command,
92 SW_COMMAND, N_("Web browser:"), (gpointer) spawn_commands},
94 {"music_command", &conf.music_command,
95 SW_COMMAND, N_("Detect music from:"), (gpointer) music_commands},
97 {"net_useproxy", &conf.options.useproxy,
98 SW_TOGGLE, N_("Use _proxy server")},
99 {"net_proxy", &conf.proxy,
100 SW_TEXT, N_("UR_L:")},
102 {"net_useproxyauth", &conf.options.useproxyauth,
103 SW_TOGGLE, N_("Use proxy _authentication")},
104 {"net_proxyuser", &conf.proxyuser,
105 SW_TEXT, N_("_User:")},
106 {"net_proxypass", &conf.proxypass,
107 SW_TEXT, N_("_Password:")},
109 {"debug_netdump", &conf.options.netdump,
110 SW_TOGGLE, N_("Dump _network data on stderr")},
111 {"debug_nofork", &conf.options.nofork,
112 SW_TOGGLE, N_("Don't _fork on network requests")},
115 {"cf_autostart", &conf.options.cfautostart,
116 SW_TOGGLE, N_("_Monitor friends list for new entries upon login")},
117 {"cf_usemask", &conf.options.cfusemask,
118 SW_TOGGLE, N_("_Limit monitoring to groups of friends")},
119 {"cf_mask", NULL /* set at runtime to that of current user */ ,
120 SW_CUSTOM, ""},
121 {"cf_userinterval", &conf.cfuserinterval,
122 SW_SPIN_INTEGER, N_("_Check friends list every " /* X seconds */ )},
123 {"cf_threshold", &conf.cfthreshold,
124 SW_SPIN_INTEGER, N_("_Notify me after " /* X new entires */ )},
125 {"cf_float", &conf.options.cffloat,
126 SW_TOGGLE, N_("_Floating indicator")},
127 {"cf_floatraise", &conf.options.cffloatraise,
128 SW_TOGGLE, N_("_Raise floating indicator when new entries detected")},
129 {"cf_float_decorate", &conf.options.cffloat_decorate,
130 SW_TOGGLE, N_("Show _titlebar on floating indicator")},
132 {NULL}
136 static SettingsWidget *sw_lookup (const char *name) {
137 for (SettingsWidget *sw = settingswidgets; sw->name; ++sw) if (strcmp(name, sw->name) == 0) return sw;
138 g_error("sw_lookup failed for %s", name);
139 return NULL;
143 static void toggle_enable_cb (GtkWidget *toggle, GtkWidget *target) {
144 gtk_widget_set_sensitive(target, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle)));
148 static void toggle_tie_enable (GtkWidget *toggle, GtkWidget *target) {
149 gtk_widget_set_sensitive(target, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle)));
150 g_signal_connect(G_OBJECT(toggle), "toggled", G_CALLBACK(toggle_enable_cb), target);
154 static void toggle_tie (SettingsWidget *sw) {
155 tie_toggle(GTK_TOGGLE_BUTTON(sw->widget), (gboolean *)sw->conf);
159 static void radio_tie_cb (GtkToggleButton *toggle, SettingsWidget *sw) {
160 if (gtk_toggle_button_get_active(toggle)) *(int *)sw->conf = GPOINTER_TO_INT(sw->data);
164 static void radio_tie(SettingsWidget *sw) {
165 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(sw->widget), *(gint *)sw->conf == GPOINTER_TO_INT(sw->data));
166 g_signal_connect(G_OBJECT(sw->widget), "toggled", G_CALLBACK(radio_tie_cb), sw);
170 static void text_tie (SettingsWidget *sw) {
171 tie_text(GTK_ENTRY(sw->widget), (char **)sw->conf);
175 static void integer_tie_cb (GtkEditable *e, SettingsWidget *sw) {
176 char *text = gtk_editable_get_chars(e, 0, -1);
177 *(int *)sw->conf = atoi(text);
178 g_free(text);
182 static void integer_tie (SettingsWidget *sw) {
183 char buf[30];
184 snprintf(buf, sizeof(buf), "%d", *(int *)sw->conf);
185 gtk_entry_set_text(GTK_ENTRY(sw->widget), buf);
186 g_signal_connect(G_OBJECT(sw->widget), "changed", G_CALLBACK(integer_tie_cb), sw);
190 static void spin_integer_tie_cb (GtkSpinButton *b, SettingsWidget *sw) {
191 *(gint *)sw->conf = (gint) gtk_spin_button_get_value(b);
195 static void spin_integer_tie (SettingsWidget *sw) {
196 g_signal_connect(G_OBJECT(sw->widget), "value-changed", G_CALLBACK(spin_integer_tie_cb), sw);
200 static void combo_tie(SettingsWidget *sw) {
201 tie_combo(GTK_COMBO(sw->widget), (char **)sw->conf);
205 static void command_changed_cb (GtkOptionMenu *omenu, SettingsWidget *sw) {
206 CommandList *cmds = sw->data;
207 int cur = gtk_option_menu_get_history(GTK_OPTION_MENU(sw->widget));
208 jam_widget_set_visible(sw->data2, cmds[cur].label == NULL);
209 if (cmds[cur].label != NULL) {
210 const char *cmd = cmds[cur].command;
211 string_replace((char **)sw->conf, (cmd ? g_strdup(cmd) : NULL));
212 gtk_entry_set_text(GTK_ENTRY(sw->subwidget), (cmd ? cmd : ""));
217 static GtkWidget *command_make (SettingsWidget *sw) {
218 CommandList *cmds = sw->data;
219 GtkWidget *vbox, *menu;
220 GtkSizeGroup *sg;
221 char *cmd = *((char **)sw->conf);
222 int cur = -1;
223 int i;
225 menu = gtk_menu_new();
226 for (i = 0; cmds[i].label; ++i) {
227 const char *curcmd = cmds[i].command;
228 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_menu_item_new_with_label(_(cmds[i].label)));
229 if (cur == -1 && ((cmd == curcmd) || (cmd && curcmd && (strcmp(cmd, curcmd) == 0))))
230 cur = i;
232 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_menu_item_new_with_label(_("Custom Command")));
233 if (cur == -1) cur = i;
235 sw->widget = gtk_option_menu_new();
236 gtk_option_menu_set_menu(GTK_OPTION_MENU(sw->widget), menu);
237 gtk_option_menu_set_history(GTK_OPTION_MENU(sw->widget), cur);
239 sw->subwidget = gtk_entry_new();
240 tie_text(GTK_ENTRY(sw->subwidget), (char **)sw->conf);
242 g_signal_connect(G_OBJECT(sw->widget), "changed", G_CALLBACK(command_changed_cb), sw);
243 jam_widget_set_visible(sw->subwidget, cur == i);
245 sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
246 vbox = gtk_vbox_new(FALSE, 3);
247 gtk_box_pack_start(GTK_BOX(vbox), labelled_box_new_sg(_(sw->caption), sw->widget, sg), FALSE, FALSE, 0);
248 sw->data2 = labelled_box_new_sg(NULL, sw->subwidget, sg); /* ugh. */
249 gtk_box_pack_start(GTK_BOX(vbox), sw->data2, FALSE, FALSE, 0);
250 return vbox;
254 static GtkWidget *sw_make_sg (const char *name, GtkSizeGroup *sg) {
255 SettingsWidget *sw = sw_lookup(name);
256 switch (sw->type) {
257 case SW_TOGGLE:
258 sw->widget = gtk_check_button_new_with_mnemonic(_(sw->caption));
259 toggle_tie(sw);
260 return sw->widget;
261 case SW_RADIO:
262 sw->widget = gtk_radio_button_new_with_mnemonic(NULL, _(sw->caption));
263 radio_tie(sw);
264 return sw->widget;
265 case SW_TEXT:
266 sw->widget = gtk_entry_new();
267 text_tie(sw);
268 return labelled_box_new_sg(_(sw->caption), sw->widget, sg);
269 case SW_INTEGER:
270 sw->widget = gtk_entry_new();
271 gtk_entry_set_width_chars(GTK_ENTRY(sw->widget), 4);
272 integer_tie(sw);
273 return labelled_box_new(_(sw->caption), sw->widget);
274 case SW_SPIN_INTEGER:
276 gdouble default_value = *(gint *) sw->conf;
277 GtkObject *adj = gtk_adjustment_new(default_value,
278 /* client code should override these */
279 G_MINDOUBLE, G_MAXDOUBLE, 1.0, 1.0, 1.0);
280 sw->widget = gtk_spin_button_new(GTK_ADJUSTMENT(adj), 1, 1);
282 spin_integer_tie(sw);
283 return labelled_box_new_expand(_(sw->caption), sw->widget, FALSE);
284 case SW_COMBO:
285 sw->widget = gtk_combo_new();
286 combo_tie(sw);
287 return labelled_box_new(_(sw->caption), sw->widget);
288 case SW_COMMAND:
289 return command_make(sw);
290 case SW_CUSTOM:
291 if (sw->caption && sw->widget) return labelled_box_new(_(sw->caption), sw->widget);
292 break;
293 default:
294 break;
296 return NULL;
300 static GtkWidget *sw_make (const char *name) {
301 return sw_make_sg(name, NULL);
305 static void sec_changed_cb (SecMgr *sm, SettingsWidget *sw) {
306 secmgr_security_get(sm, (LJSecurity *)sw->conf);
310 static void run_fontsel_settings_dlg (SettingsWidget *sw) {
311 GtkWidget *dlg;
312 const gchar *newfont;
313 gchar *oldfont;
315 dlg = gtk_font_selection_dialog_new(_("Select font"));
316 gtk_font_selection_dialog_set_font_name(GTK_FONT_SELECTION_DIALOG(dlg), gtk_label_get_text(GTK_LABEL(sw->widget)));
318 if (gtk_dialog_run(GTK_DIALOG(dlg)) == GTK_RESPONSE_OK) {
319 gtk_label_set_text(GTK_LABEL(sw->widget), gtk_font_selection_dialog_get_font_name(GTK_FONT_SELECTION_DIALOG(dlg)));
322 newfont = gtk_label_get_text(GTK_LABEL(sw->widget));
323 oldfont = pango_font_description_to_string(pango_context_get_font_description(gtk_widget_get_pango_context(GTK_WIDGET(sw->data))));
325 if (newfont && g_ascii_strcasecmp(oldfont, newfont) != 0) {
326 string_replace(sw->conf, g_strdup(newfont));
327 jam_widget_set_font(sw->widget, newfont);
328 jam_widget_set_font(sw->data, newfont);
330 g_free(oldfont);
332 gtk_widget_destroy(dlg);
336 #ifdef USE_DOCK
337 extern void docklet_enable (GtkWidget *win, gboolean enable);
339 static void docklet_change_cb (GtkToggleButton *tb, JamWin *jw) {
340 docklet_enable(GTK_WIDGET(jw), gtk_toggle_button_get_active(tb));
342 #endif /* USE_DOCK */
345 static GtkWidget *uisettings (JamWin *jw) {
346 SettingsWidget *sw;
347 GtkWidget *vbox, *hbox, *button;
348 char *fontname = NULL;
349 GtkWidget *post, *entry, *misc;
351 vbox = gtk_vbox_new(FALSE, 18);
352 gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
354 post = groupedbox_new_with_text(_("Posting"));
355 gtk_box_pack_start(GTK_BOX(vbox), post, FALSE, FALSE, 0);
357 groupedbox_pack(GROUPEDBOX(post), sw_make("ui_revertusejournal"), FALSE);
359 sw = sw_lookup("ui_defaultsecurity");
360 sw->widget = secmgr_new(FALSE);
361 secmgr_security_set(SECMGR(sw->widget), (LJSecurity *) sw->conf);
362 g_signal_connect(G_OBJECT(sw->widget), "changed", G_CALLBACK(sec_changed_cb), sw);
363 groupedbox_pack(GROUPEDBOX(post), sw_make("ui_defaultsecurity"), FALSE);
365 entry = groupedbox_new_with_text(_("Entries"));
366 gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);
368 groupedbox_pack(GROUPEDBOX(entry), sw_make("ui_autosave"), FALSE);
370 #ifdef HAVE_GTKSPELL
372 GtkWidget *toggle = sw_make("ui_spellcheck");
373 GtkWidget *label, *box;
375 hbox = sw_make("ui_spell_language");
376 label = gtk_label_new(" ");
377 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
378 gtk_box_reorder_child(GTK_BOX(hbox), label, 0);
380 sw = sw_lookup("ui_spell_language");
381 gtk_entry_set_width_chars(GTK_ENTRY(sw->widget), 5);
383 box = gtk_vbox_new(FALSE, 3);
384 gtk_box_pack_start(GTK_BOX(box), toggle, FALSE, FALSE, 0);
385 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
387 groupedbox_pack(GROUPEDBOX(entry), box, FALSE);
388 toggle_tie_enable(toggle, hbox);
390 #endif
392 groupedbox_pack(GROUPEDBOX(entry), sw_make("ui_smartquotes"), FALSE);
393 groupedbox_pack(GROUPEDBOX(entry), sw_make("ui_smartquotes_ru"), FALSE);
395 sw = sw_lookup("ui_font");
396 if (conf.uifont == NULL) {
397 fontname = pango_font_description_to_string(pango_context_get_font_description(gtk_widget_get_pango_context(jw->view)));
398 sw->widget = gtk_label_new(fontname ? fontname : _("[gtk default]"));
399 if (fontname) jam_widget_set_font(sw->widget, fontname);
400 g_free(fontname);
401 } else {
402 sw->widget = gtk_label_new(conf.uifont);
403 jam_widget_set_font(sw->widget, conf.uifont);
405 button = gtk_button_new_from_stock(GTK_STOCK_SELECT_FONT);
406 sw->data = jw->view;
408 hbox = labelled_box_new_expand(_(sw->caption), button, FALSE);
409 gtk_box_pack_start(GTK_BOX(hbox), sw->widget, TRUE, TRUE, 0);
410 gtk_box_reorder_child(GTK_BOX(hbox), sw->widget, 1);
412 groupedbox_pack(GROUPEDBOX(entry), hbox, FALSE);
413 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(run_fontsel_settings_dlg), sw);
415 misc = groupedbox_new_with_text(_("Behavior"));
416 gtk_box_pack_start(GTK_BOX(vbox), misc, FALSE, FALSE, 0);
418 groupedbox_pack(GROUPEDBOX(misc), sw_make("ui_allowmultipleinstances"), FALSE);
420 #ifdef USE_DOCK
421 button = sw_make("ui_docklet");
422 g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(docklet_change_cb), jw);
423 groupedbox_pack(GROUPEDBOX(misc), button, FALSE);
424 #endif /* USE_DOCK */
426 return vbox;
430 static GtkWidget *proxyuserpass (void) {
431 GtkWidget *vbox = gtk_vbox_new(FALSE, 6);
432 GtkSizeGroup *sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
433 gtk_box_pack_start(GTK_BOX(vbox), sw_make_sg("net_proxyuser", sg), FALSE, FALSE, 0);
434 gtk_box_pack_start(GTK_BOX(vbox), sw_make_sg("net_proxypass", sg), FALSE, FALSE, 0);
435 return vbox;
439 static void music_diagnose (GtkWidget *button) {
440 GtkWindow *dlg = GTK_WINDOW(gtk_widget_get_toplevel(button));
441 GError *err = NULL;
442 char *music, *result;
443 MusicSource source = music_current_source();
445 if (!music_can_detect(&err)) {
446 jam_warning(dlg, "%s", err->message);
447 g_error_free(err);
448 return;
451 music = music_detect(&err);
452 if (!music) {
453 if (source == MUSIC_SOURCE_XMMS && err->domain == G_SPAWN_ERROR && err->code == G_SPAWN_ERROR_NOENT) {
454 jam_warning(dlg, _("LogJam XMMS helper not found. " "Did you install LogJam's XMMS support?"));
455 } else {
456 jam_warning(dlg, _("Error detecting music: %s"), err->message);
458 g_error_free(err);
459 return;
461 result = g_strdup_printf(_("Music detection succeeded.\n\nCurrent music:\n%s"), music);
462 jam_messagebox(dlg, _("Music Detection"), result);
463 g_free(music);
464 g_free(result);
468 static GtkWidget *proxysettings (void) {
469 GtkWidget *group, *wbox, *hbox, *cb, *table, *authgroup;
471 group = groupedbox_new_with_text(_("Proxy"));
473 cb = sw_make("net_useproxy");
474 groupedbox_pack(GROUPEDBOX(group), cb, FALSE);
476 wbox = gtk_vbox_new(FALSE, 6);
477 toggle_tie_enable(cb, wbox);
478 groupedbox_pack(GROUPEDBOX(group), wbox, FALSE);
480 hbox = sw_make("net_proxy");
481 gtk_box_pack_start(GTK_BOX(wbox), hbox, FALSE, FALSE, 0);
483 cb = sw_make("net_useproxyauth");
484 gtk_box_pack_start(GTK_BOX(wbox), cb, FALSE, FALSE, 0);
486 table = proxyuserpass();
487 authgroup = groupedbox_new();
488 groupedbox_pack(GROUPEDBOX(authgroup), table, FALSE);
489 gtk_box_pack_start(GTK_BOX(wbox), authgroup, FALSE, FALSE, 0);
491 toggle_tie_enable(cb, table);
493 return group;
497 static GtkWidget *programsettings (JamWin *jw) {
498 GtkWidget *group;
499 GtkWidget *button, *hbox;
500 SettingsWidget *sw;
501 JamView *jv = jam_win_get_cur_view(jw);
503 group = groupedbox_new_with_text(_("Programs"));
504 groupedbox_pack(GROUPEDBOX(group), sw_make("web_spawn"), FALSE);
506 groupedbox_pack(GROUPEDBOX(group), sw_make("music_command"), FALSE);
507 sw = sw_lookup("music_command");
508 g_signal_connect_swapped(G_OBJECT(sw->widget), "changed", G_CALLBACK(jam_view_settings_changed), jv);
510 button = gtk_button_new_with_mnemonic(_("_Diagnose"));
511 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(music_diagnose), NULL);
512 hbox = labelled_box_new_expand(_("Diagnose problems detecting music:"), button, FALSE);
513 groupedbox_pack(GROUPEDBOX(group), hbox, FALSE);
515 return group;
519 static GtkWidget *systemsettings (JamWin *jw) {
520 GtkWidget *vbox;
522 vbox = gtk_vbox_new(FALSE, 18);
523 gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
524 gtk_box_pack_start(GTK_BOX(vbox), programsettings(jw), FALSE, FALSE, 0);
525 gtk_box_pack_start(GTK_BOX(vbox), proxysettings(), FALSE, FALSE, 0);
526 return vbox;
530 static GtkWidget *debugsettings (GtkWidget *dlg) {
531 GtkWidget *vbox, *gb;
533 vbox = gtk_vbox_new(FALSE, 18);
534 gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
536 gb = groupedbox_new_with_text(_("Network"));
537 gtk_box_pack_start(GTK_BOX(vbox), gb, FALSE, FALSE, 0);
539 groupedbox_pack(GROUPEDBOX(gb), sw_make("debug_netdump"), FALSE);
540 groupedbox_pack(GROUPEDBOX(gb), sw_make("debug_nofork"), FALSE);
542 return vbox;
546 static GtkWidget *cfriends_general_settings (JamAccountLJ *acc) {
547 GtkWidget *general, *b, *w;
548 SettingsWidget *sw;
550 general = groupedbox_new_with_text(_("General"));
552 groupedbox_pack(GROUPEDBOX(general), sw_make("cf_autostart"), FALSE);
554 b = sw_make("cf_threshold");
555 sw = sw_lookup("cf_threshold");
556 w = sw->widget;
557 jam_spin_button_set(GTK_SPIN_BUTTON(w), TRUE /* numeric */ ,
558 1.0, 7.0 /* range */ , 1.0, 1.0 /*increments */ , 0 /* digits */ );
559 gtk_box_pack_start(GTK_BOX(b), gtk_label_new(_("new entries by my friends")), FALSE, FALSE, 5);
560 /* XXX: ugly, because it defaults to "1 entries". Yuck. */
561 groupedbox_pack(GROUPEDBOX(general), b, FALSE);
563 b = sw_make("cf_userinterval");
564 sw = sw_lookup("cf_userinterval");
565 w = sw->widget;
566 jam_spin_button_set(GTK_SPIN_BUTTON(w), TRUE /*numeric */ , 45.0, 3600.0 /*range */ , 1.0, 10.0 /*increments */ , 0 /*digits */ );
567 gtk_box_pack_start(GTK_BOX(b), gtk_label_new(_("seconds")), FALSE, FALSE, 5);
568 groupedbox_pack(GROUPEDBOX(general), b, FALSE);
570 return general;
574 static GtkWidget *cfriends_filter_settings (JamAccountLJ *acc) {
575 GtkWidget *filter, *maskhbox, *l;
576 SettingsWidget *sw;
577 LJUser *u = jam_account_lj_get_user(acc);
579 filter = groupedbox_new_with_text(_("Filter"));
581 maskhbox = gtk_hbox_new(FALSE, 5);
582 gtk_box_pack_start(GTK_BOX(maskhbox), sw_make("cf_usemask"), FALSE, FALSE, 0);
583 sw = sw_lookup("cf_mask");
584 sw->conf = acc;
585 sw->widget = gtk_button_new_with_label(_("Choose filter"));
586 gtk_box_pack_start(GTK_BOX(maskhbox), GTK_WIDGET(sw->widget), FALSE, FALSE, 0);
587 gtk_widget_set_sensitive(sw->widget, u->friendgroups != NULL);
588 g_signal_connect_swapped(G_OBJECT(sw->widget), "clicked", G_CALLBACK(run_cfmask_settings_dlg), sw);
589 l = gtk_label_new(NULL);
590 gtk_label_set_markup(GTK_LABEL(l), _("<small>The filter chosen here only affects the " "current user.</small>"));
591 gtk_label_set_line_wrap(GTK_LABEL(l), TRUE);
592 gtk_label_set_justify(GTK_LABEL(l), GTK_JUSTIFY_LEFT);
594 groupedbox_pack(GROUPEDBOX(filter), maskhbox, FALSE);
595 groupedbox_pack(GROUPEDBOX(filter), l, FALSE);
597 return filter;
601 static void float_change_cb (GtkWidget *w, CFMgr *cfm) {
602 cf_app_update_float();
606 static GtkWidget *cfriends_indicators_settings (CFMgr *cfm) {
607 GtkWidget *indicators, *floaters, *b, *w;
608 SettingsWidget *sw;
610 indicators = groupedbox_new_with_text(_("Indicators"));
612 b = sw_make("cf_float");
613 g_signal_connect(G_OBJECT(b), "toggled", G_CALLBACK(float_change_cb), cfm);
614 sw = sw_lookup("cf_float");
615 w = sw->widget;
616 groupedbox_pack(GROUPEDBOX(indicators), b, FALSE);
618 floaters = groupedbox_new();
619 groupedbox_pack(GROUPEDBOX(indicators), floaters, FALSE);
621 groupedbox_pack(GROUPEDBOX(floaters), sw_make("cf_floatraise"), FALSE);
622 b = sw_make("cf_float_decorate");
623 g_signal_connect(G_OBJECT(b), "toggled", G_CALLBACK(cf_float_decorate_refresh), NULL);
624 groupedbox_pack(GROUPEDBOX(floaters), b, FALSE);
626 toggle_tie_enable(w, floaters);
628 return indicators;
632 static GtkWidget *cfriendssettings (CFMgr *cfm) {
633 GtkWidget *vbox;
634 JamAccountLJ *acc = cfmgr_get_account(cfm);
636 vbox = gtk_vbox_new(FALSE, 18);
637 gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
639 /* general */
640 gtk_box_pack_start(GTK_BOX(vbox), cfriends_general_settings(acc), FALSE, FALSE, 0);
642 /* filter */
643 gtk_box_pack_start(GTK_BOX(vbox), cfriends_filter_settings(acc), FALSE, FALSE, 0);
645 /* indicators */
646 gtk_box_pack_start(GTK_BOX(vbox), cfriends_indicators_settings(cfm), FALSE, FALSE, 0);
648 return vbox;
652 static void run_settings_dialog (JamWin *jw) {
653 GtkWidget *dlg, *nb;
655 dlg = gtk_dialog_new_with_buttons(_("Preferences"), GTK_WINDOW(jw), GTK_DIALOG_MODAL, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
657 /* the order of notebook pages created here should match the
658 * SettingsPage enum in settings.h */
659 nb = gtk_notebook_new();
660 gtk_notebook_append_page(GTK_NOTEBOOK(nb), uisettings(jw), gtk_label_new_with_mnemonic(_("Interface")));
661 gtk_notebook_append_page(GTK_NOTEBOOK(nb), systemsettings(jw), gtk_label_new_with_mnemonic(_("System")));
662 if (JAM_ACCOUNT_IS_LJ(jw->account)) gtk_notebook_append_page(GTK_NOTEBOOK(nb), cfriendssettings(app.cfmgr), gtk_label_new_with_mnemonic(_("Check Friends")));
663 gtk_notebook_append_page(GTK_NOTEBOOK(nb), debugsettings(dlg), gtk_label_new_with_mnemonic(_("Debug")));
665 jam_dialog_set_contents(GTK_DIALOG(dlg), nb);
667 /* XXX HACK: set_contents calls show_all, but the "command"
668 * settings widgets sometimes want to have hidden children.
669 * so we let them rehide. */
670 for (SettingsWidget *sw = settingswidgets; sw->name; ++sw) if (sw->type == SW_COMMAND) command_changed_cb(GTK_OPTION_MENU(sw->widget), sw);
672 gtk_dialog_run(GTK_DIALOG(dlg));
673 gtk_widget_destroy(dlg);
677 void settings_cf_run (CFMgr *cfm) {
678 GtkWidget *dlg;
679 dlg = gtk_dialog_new_with_buttons(_("Checkfriends Preferences"), NULL /* no parent */ , GTK_DIALOG_MODAL, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
680 jam_dialog_set_contents(GTK_DIALOG(dlg), cfriendssettings(cfm));
681 gtk_dialog_run(GTK_DIALOG(dlg));
682 gtk_widget_destroy(dlg);
686 void settings_run (JamWin *jw) {
688 #ifdef HAVE_GTKSPELL
689 gboolean hadspell = conf.options.usespellcheck;
690 #endif
692 gboolean hadautosave = conf.options.autosave;
693 gboolean hadquotes = conf.options.smartquotes;
695 run_settings_dialog(jw);
697 g_slist_foreach(app.secmgr_list, (GFunc) secmgr_security_set_force, &conf.defaultsecurity);
699 cf_threshold_normalize(&conf.cfthreshold);
701 if (!hadautosave && conf.options.autosave) jam_autosave_init(jw);
702 if (hadautosave && !conf.options.autosave) jam_autosave_stop(jw);
704 if (conf.options.allowmultipleinstances) {
705 GError *err = NULL;
706 if (!logjam_remote_stop_listening(app.remote, &err)) {
707 jam_warning(GTK_WINDOW(jw), _("Error stopping remote listener: %s."), err->message);
708 g_error_free(err);
711 if (!conf.options.allowmultipleinstances) {
712 GError *err = NULL;
713 if (!logjam_remote_listen(app.remote, &err) && err) {
714 jam_warning(GTK_WINDOW(jw), _("Error starting remote listener: %s."), err->message);
715 g_error_free(err);
719 if (hadquotes) smartquotes_detach(jam_doc_get_text_buffer(jw->doc));
720 if (conf.options.smartquotes) smartquotes_attach(jam_doc_get_text_buffer(jw->doc), conf.options.smartquotes_russian);
722 jam_view_settings_changed(jam_win_get_cur_view(jw));
726 static void run_cfmask_settings_dlg (SettingsWidget *sw) {
727 GtkWindow *window = GTK_WINDOW(gtk_widget_get_toplevel(sw->widget));
728 JamAccountLJ *acc = sw->conf;
729 jam_account_lj_set_cfmask(acc, custom_security_dlg_run(window, jam_account_lj_get_cfmask(acc), acc));