about.c: cosmetix
[k8lowj.git] / src / settings.c
blob61eae70f3b624945ea819dbebd6c7354518b2108
1 /* logjam - a GTK client for LiveJournal.
2 * Copyright (C) 2000-2003 Evan Martin <evan@livejournal.com>
4 * vim: tabstop=4 shiftwidth=4 noexpandtab :
5 */
7 #include "gtk-all.h"
8 #include <stdlib.h> /* atoi */
9 #include <string.h>
11 #include "conf.h"
12 #include "security.h"
13 #include "util-gtk.h"
14 #include "spawn.h"
15 #include "jam.h"
16 #include "checkfriends.h"
17 #include "settings.h"
18 #include "music.h"
19 #include "groupedbox.h"
20 #include "remote.h"
21 #include "smartquotes.h"
22 #include "tie.h"
23 #include "account.h"
24 #include "jamview.h"
25 #include "util.h"
27 /* what's this? all of these funny structures in the settings box?
28 * well, instead of creating and tearing down all of these widgets, i
29 * try to pull out shared functionality.
31 * it's loosely inspired by george lebl's pong
32 * http://www.5z.com/jirka/pong-documentation/
33 * but i've never actaully read how that works.
36 * we have a collection of SettingsWidgets that track their name and
37 * configuration option, and then it's all nicely automated through functions
38 * of the form sw_*().
41 typedef enum {
42 SW_TOGGLE,
43 SW_RADIO,
44 SW_TEXT,
45 SW_INTEGER,
46 SW_COMBO,
47 SW_SPIN_INTEGER,
48 SW_COMMAND,
49 SW_CUSTOM
50 } SettingsWidgetType;
52 typedef struct _SettingsWidget SettingsWidget;
53 struct _SettingsWidget {
54 char *name;
55 void *conf;
56 SettingsWidgetType type;
57 char *caption;
58 gpointer data; /* extra pointer, to be used as needed. */
59 gpointer data2; /* extra pointer for wimps. */
60 GtkWidget *subwidget;
61 GtkWidget *widget;
64 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 #ifndef G_OS_WIN32
89 { "ui_docklet", &conf.options.docklet,
90 SW_TOGGLE, N_("Add icon to system _tray (for GNOME/KDE/etc. dock)") },
91 #endif
93 #ifndef G_OS_WIN32
94 { "web_spawn", &conf.spawn_command,
95 SW_COMMAND, N_("Web browser:"), (gpointer)spawn_commands },
97 { "music_command", &conf.music_command,
98 SW_COMMAND, N_("Detect music from:"), (gpointer)music_commands },
100 { "net_useproxy", &conf.options.useproxy,
101 SW_TOGGLE, N_("Use _proxy server") },
102 { "net_proxy", &conf.proxy,
103 SW_TEXT, N_("UR_L:") },
105 { "net_useproxyauth", &conf.options.useproxyauth,
106 SW_TOGGLE, N_("Use proxy _authentication") },
107 { "net_proxyuser", &conf.proxyuser,
108 SW_TEXT, N_("_User:") },
109 { "net_proxypass", &conf.proxypass,
110 SW_TEXT, N_("_Password:") },
111 #endif
113 { "debug_netdump", &conf.options.netdump,
114 SW_TOGGLE, N_("Dump _network data on stderr") },
115 #ifndef G_OS_WIN32
116 { "debug_nofork", &conf.options.nofork,
117 SW_TOGGLE, N_("Don't _fork on network requests") },
118 #else
119 { "debug_nofork", &conf.options.nofork,
120 SW_TOGGLE, N_("Don't run network requests in a separate _thread") },
121 #endif
124 { "cf_autostart", &conf.options.cfautostart,
125 SW_TOGGLE, N_("_Monitor friends list for new entries upon login") },
126 { "cf_usemask", &conf.options.cfusemask,
127 SW_TOGGLE, N_("_Limit monitoring to groups of friends") },
128 { "cf_mask", NULL /* set at runtime to that of current user */,
129 SW_CUSTOM, "" },
130 { "cf_userinterval", &conf.cfuserinterval,
131 SW_SPIN_INTEGER, N_("_Check friends list every " /* X seconds */) },
132 { "cf_threshold", &conf.cfthreshold,
133 SW_SPIN_INTEGER, N_("_Notify me after " /* X new entires */) },
134 { "cf_float", &conf.options.cffloat,
135 SW_TOGGLE, N_("_Floating indicator") },
136 { "cf_floatraise", &conf.options.cffloatraise,
137 SW_TOGGLE, N_("_Raise floating indicator when new entries detected") },
138 { "cf_float_decorate", &conf.options.cffloat_decorate,
139 SW_TOGGLE, N_("Show _titlebar on floating indicator") },
141 { NULL }
144 static SettingsWidget*
145 sw_lookup(char *name) {
146 SettingsWidget *sw;
147 for (sw = settingswidgets; sw->name; sw++)
148 if (strcmp(name, sw->name) == 0) return sw;
149 g_error("sw_lookup failed for %s", name);
150 return NULL;
153 static void
154 toggle_enable_cb(GtkWidget *toggle, GtkWidget *target) {
155 gtk_widget_set_sensitive(target,
156 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle)));
158 static void
159 toggle_tie_enable(GtkWidget *toggle, GtkWidget *target) {
160 gtk_widget_set_sensitive(target,
161 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle)));
162 g_signal_connect(G_OBJECT(toggle), "toggled",
163 G_CALLBACK(toggle_enable_cb), target);
166 static void
167 toggle_tie(SettingsWidget *sw) {
168 tie_toggle(GTK_TOGGLE_BUTTON(sw->widget), (gboolean*)sw->conf);
171 static void
172 radio_tie_cb(GtkToggleButton *toggle, SettingsWidget *sw) {
173 if (gtk_toggle_button_get_active(toggle))
174 *(int*)sw->conf = GPOINTER_TO_INT(sw->data);
176 static void
177 radio_tie(SettingsWidget *sw) {
178 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(sw->widget),
179 *(gint*)sw->conf == GPOINTER_TO_INT(sw->data));
180 g_signal_connect(G_OBJECT(sw->widget), "toggled",
181 G_CALLBACK(radio_tie_cb), sw);
184 static void
185 text_tie(SettingsWidget *sw) {
186 tie_text(GTK_ENTRY(sw->widget), (char**)sw->conf);
189 static void
190 integer_tie_cb(GtkEditable *e, SettingsWidget *sw) {
191 char *text = gtk_editable_get_chars(e, 0, -1);
192 *(int*)sw->conf = atoi(text);
193 g_free(text);
195 static void
196 integer_tie(SettingsWidget *sw) {
197 char buf[30];
198 sprintf(buf, "%d", *(int*)sw->conf);
199 gtk_entry_set_text(GTK_ENTRY(sw->widget), buf);
200 g_signal_connect(G_OBJECT(sw->widget), "changed",
201 G_CALLBACK(integer_tie_cb), sw);
204 static void
205 spin_integer_tie_cb(GtkSpinButton *b, SettingsWidget *sw) {
206 *(gint*)sw->conf = (gint)gtk_spin_button_get_value(b);
208 static void
209 spin_integer_tie(SettingsWidget *sw) {
210 g_signal_connect(G_OBJECT(sw->widget), "value-changed",
211 G_CALLBACK(spin_integer_tie_cb), sw);
214 static void
215 combo_tie(SettingsWidget *sw) {
216 tie_combo(GTK_COMBO(sw->widget), (char**)sw->conf);
220 static void
221 command_changed_cb(GtkOptionMenu *omenu, SettingsWidget *sw) {
222 CommandList *cmds = sw->data;
223 int cur = gtk_option_menu_get_history(GTK_OPTION_MENU(sw->widget));
225 jam_widget_set_visible(sw->data2, cmds[cur].label == NULL);
226 if (cmds[cur].label != NULL) {
227 const char *cmd = cmds[cur].command;
228 string_replace((char**)sw->conf, cmd ? g_strdup(cmd) : NULL);
229 gtk_entry_set_text(GTK_ENTRY(sw->subwidget), cmd ? cmd : "");
233 static GtkWidget*
234 command_make(SettingsWidget *sw) {
235 CommandList *cmds = sw->data;
236 GtkWidget *vbox, *menu;
237 GtkSizeGroup *sg;
238 char *cmd = *((char**)sw->conf);
239 int cur = -1;
240 int i;
242 menu = gtk_menu_new();
243 for (i = 0; cmds[i].label; i++) {
244 char *curcmd = cmds[i].command;
245 gtk_menu_shell_append(GTK_MENU_SHELL(menu),
246 gtk_menu_item_new_with_label(_(cmds[i].label)));
247 if (cur == -1 &&
248 ((cmd == curcmd) ||
249 (cmd && curcmd && (strcmp(cmd, curcmd) == 0))))
250 cur = i;
252 gtk_menu_shell_append(GTK_MENU_SHELL(menu),
253 gtk_menu_item_new_with_label(_("Custom Command")));
254 if (cur == -1) cur = i;
256 sw->widget = gtk_option_menu_new();
257 gtk_option_menu_set_menu(GTK_OPTION_MENU(sw->widget), menu);
258 gtk_option_menu_set_history(GTK_OPTION_MENU(sw->widget), cur);
260 sw->subwidget = gtk_entry_new();
261 tie_text(GTK_ENTRY(sw->subwidget), (char**)sw->conf);
263 g_signal_connect(G_OBJECT(sw->widget), "changed",
264 G_CALLBACK(command_changed_cb), sw);
265 jam_widget_set_visible(sw->subwidget, cur == i);
267 sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
268 vbox = gtk_vbox_new(FALSE, 3);
269 gtk_box_pack_start(GTK_BOX(vbox),
270 labelled_box_new_sg(_(sw->caption), sw->widget, sg),
271 FALSE, FALSE, 0);
272 sw->data2 = labelled_box_new_sg(NULL, sw->subwidget, sg); /* ugh. */
273 gtk_box_pack_start(GTK_BOX(vbox),
274 sw->data2,
275 FALSE, FALSE, 0);
276 return vbox;
279 static GtkWidget*
280 sw_make_sg(char *name, GtkSizeGroup *sg) {
281 SettingsWidget *sw = sw_lookup(name);
282 switch (sw->type) {
283 case SW_TOGGLE:
284 sw->widget = gtk_check_button_new_with_mnemonic(_(sw->caption));
285 toggle_tie(sw);
286 return sw->widget;
287 case SW_RADIO:
288 sw->widget = gtk_radio_button_new_with_mnemonic(NULL,
289 _(sw->caption));
290 radio_tie(sw);
291 return sw->widget;
292 case SW_TEXT:
293 sw->widget = gtk_entry_new();
294 text_tie(sw);
295 return labelled_box_new_sg(_(sw->caption), sw->widget, sg);
296 case SW_INTEGER:
297 sw->widget = gtk_entry_new();
298 gtk_entry_set_width_chars(GTK_ENTRY(sw->widget), 4);
299 integer_tie(sw);
300 return labelled_box_new(_(sw->caption), sw->widget);
301 case SW_SPIN_INTEGER:
303 gdouble default_value = *(gint*)sw->conf;
304 GtkObject *adj = gtk_adjustment_new(default_value,
305 /* client code should override these */
306 G_MINDOUBLE, G_MAXDOUBLE, 1.0, 1.0, 1.0);
307 sw->widget = gtk_spin_button_new(GTK_ADJUSTMENT(adj), 1, 1);
309 spin_integer_tie(sw);
310 return labelled_box_new_expand(_(sw->caption), sw->widget, FALSE);
311 case SW_COMBO:
312 sw->widget = gtk_combo_new();
313 combo_tie(sw);
314 return labelled_box_new(_(sw->caption), sw->widget);
315 case SW_COMMAND:
316 return command_make(sw);
317 case SW_CUSTOM:
318 if (sw->caption && sw->widget)
319 return labelled_box_new(_(sw->caption), sw->widget);
320 break;
321 default:
322 break;
324 return NULL;
326 static GtkWidget*
327 sw_make(char *name) {
328 return sw_make_sg(name, NULL);
331 static void
332 sec_changed_cb(SecMgr *sm, SettingsWidget *sw) {
333 secmgr_security_get(sm, (LJSecurity*)sw->conf);
336 static void
337 run_fontsel_settings_dlg(SettingsWidget *sw) {
338 GtkWidget *dlg;
339 const gchar *newfont;
340 gchar *oldfont;
342 dlg = gtk_font_selection_dialog_new(_("Select font"));
343 gtk_font_selection_dialog_set_font_name(GTK_FONT_SELECTION_DIALOG(dlg),
344 gtk_label_get_text(GTK_LABEL(sw->widget)));
346 if (gtk_dialog_run(GTK_DIALOG(dlg)) == GTK_RESPONSE_OK) {
347 gtk_label_set_text(GTK_LABEL(sw->widget),
348 gtk_font_selection_dialog_get_font_name(
349 GTK_FONT_SELECTION_DIALOG(dlg)));
353 newfont = gtk_label_get_text(GTK_LABEL(sw->widget));
354 oldfont = pango_font_description_to_string(
355 pango_context_get_font_description(
356 gtk_widget_get_pango_context(GTK_WIDGET(sw->data))));
358 if (newfont && g_ascii_strcasecmp(oldfont, newfont) != 0) {
359 string_replace(sw->conf, g_strdup(newfont));
360 jam_widget_set_font(sw->widget, newfont);
361 jam_widget_set_font(sw->data, newfont);
363 g_free(oldfont);
365 gtk_widget_destroy(dlg);
368 #ifdef USE_DOCK
369 void docklet_enable(GtkWidget *win, gboolean enable);
370 static void
371 docklet_change_cb(GtkToggleButton *tb, JamWin *jw) {
372 docklet_enable(GTK_WIDGET(jw), gtk_toggle_button_get_active(tb));
374 #endif /* USE_DOCK */
376 static GtkWidget*
377 uisettings(JamWin *jw) {
378 SettingsWidget *sw;
379 GtkWidget *vbox, *hbox, *button;
380 char *fontname = NULL;
381 GtkWidget *post, *entry, *misc;
383 vbox = gtk_vbox_new(FALSE, 18);
384 gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
386 post = groupedbox_new_with_text(_("Posting"));
387 gtk_box_pack_start(GTK_BOX(vbox), post, FALSE, FALSE, 0);
389 groupedbox_pack(GROUPEDBOX(post),
390 sw_make("ui_revertusejournal"), FALSE);
392 sw = sw_lookup("ui_defaultsecurity");
393 sw->widget = secmgr_new(FALSE);
394 secmgr_security_set(SECMGR(sw->widget), (LJSecurity*)sw->conf);
395 g_signal_connect(G_OBJECT(sw->widget), "changed",
396 G_CALLBACK(sec_changed_cb), sw);
397 groupedbox_pack(GROUPEDBOX(post), sw_make("ui_defaultsecurity"), FALSE);
399 entry = groupedbox_new_with_text(_("Entries"));
400 gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);
402 groupedbox_pack(GROUPEDBOX(entry), sw_make("ui_autosave"), FALSE);
404 #ifdef HAVE_GTKSPELL
406 GtkWidget *toggle = sw_make("ui_spellcheck");
407 GtkWidget *label, *box;
409 hbox = sw_make("ui_spell_language");
410 label = gtk_label_new(" ");
411 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
412 gtk_box_reorder_child(GTK_BOX(hbox), label, 0);
414 sw = sw_lookup("ui_spell_language");
415 gtk_entry_set_width_chars(GTK_ENTRY(sw->widget), 5);
417 box = gtk_vbox_new(FALSE, 3);
418 gtk_box_pack_start(GTK_BOX(box), toggle, FALSE, FALSE, 0);
419 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
421 groupedbox_pack(GROUPEDBOX(entry), box, FALSE);
422 toggle_tie_enable(toggle, hbox);
424 #endif
426 groupedbox_pack(GROUPEDBOX(entry),
427 sw_make("ui_smartquotes"), FALSE);
428 groupedbox_pack(GROUPEDBOX(entry),
429 sw_make("ui_smartquotes_ru"), FALSE);
431 sw = sw_lookup("ui_font");
432 if (conf.uifont == NULL) {
433 fontname = pango_font_description_to_string(
434 pango_context_get_font_description(
435 gtk_widget_get_pango_context(jw->view)));
436 sw->widget = gtk_label_new(fontname ? fontname : _("[gtk default]"));
437 if (fontname)
438 jam_widget_set_font(sw->widget, fontname);
439 g_free(fontname);
440 } else {
441 sw->widget = gtk_label_new(conf.uifont);
442 jam_widget_set_font(sw->widget, conf.uifont);
444 button = gtk_button_new_from_stock(GTK_STOCK_SELECT_FONT);
445 sw->data = jw->view;
447 hbox = labelled_box_new_expand(_(sw->caption), button, FALSE);
448 gtk_box_pack_start(GTK_BOX(hbox), sw->widget, TRUE, TRUE, 0);
449 gtk_box_reorder_child(GTK_BOX(hbox), sw->widget, 1);
451 groupedbox_pack(GROUPEDBOX(entry), hbox, FALSE);
452 g_signal_connect_swapped(G_OBJECT(button), "clicked",
453 G_CALLBACK(run_fontsel_settings_dlg), sw);
455 misc = groupedbox_new_with_text(_("Behavior"));
456 gtk_box_pack_start(GTK_BOX(vbox), misc, FALSE, FALSE, 0);
458 groupedbox_pack(GROUPEDBOX(misc),
459 sw_make("ui_allowmultipleinstances"), FALSE);
461 #ifdef USE_DOCK
462 button = sw_make("ui_docklet");
463 g_signal_connect(G_OBJECT(button), "toggled",
464 G_CALLBACK(docklet_change_cb), jw);
465 groupedbox_pack(GROUPEDBOX(misc), button, FALSE);
466 #endif /* USE_DOCK */
468 return vbox;
471 #ifndef G_OS_WIN32
472 static GtkWidget*
473 proxyuserpass() {
474 GtkWidget *vbox;
475 GtkSizeGroup *sg;
477 vbox = gtk_vbox_new(FALSE, 6);
479 sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
481 gtk_box_pack_start(GTK_BOX(vbox),
482 sw_make_sg("net_proxyuser", sg), FALSE, FALSE, 0);
483 gtk_box_pack_start(GTK_BOX(vbox),
484 sw_make_sg("net_proxypass", sg), FALSE, FALSE, 0);
486 return vbox;
489 static void
490 music_diagnose(GtkWidget *button) {
491 GtkWindow *dlg = GTK_WINDOW(gtk_widget_get_toplevel(button));
492 GError *err = NULL;
493 char *music, *result;
494 MusicSource source = music_current_source();
496 if (!music_can_detect(&err)) {
497 jam_warning(dlg, "%s", err->message);
498 g_error_free(err);
499 return;
502 music = music_detect(&err);
503 if (!music) {
504 if (source == MUSIC_SOURCE_XMMS &&
505 err->domain == G_SPAWN_ERROR && err->code == G_SPAWN_ERROR_NOENT) {
506 jam_warning(dlg, _("LogJam XMMS helper not found. "
507 "Did you install LogJam's XMMS support?"));
508 } else {
509 jam_warning(dlg, _("Error detecting music: %s"), err->message);
511 g_error_free(err);
512 return;
514 result = g_strdup_printf(_("Music detection succeeded.\n\nCurrent music:\n%s"), music);
515 jam_messagebox(dlg, _("Music Detection"), result);
516 g_free(music);
517 g_free(result);
520 static GtkWidget*
521 proxysettings(void) {
522 GtkWidget *group, *wbox, *hbox, *cb, *table, *authgroup;
524 group = groupedbox_new_with_text(_("Proxy"));
526 cb = sw_make("net_useproxy");
527 groupedbox_pack(GROUPEDBOX(group), cb, FALSE);
529 wbox = gtk_vbox_new(FALSE, 6);
530 toggle_tie_enable(cb, wbox);
531 groupedbox_pack(GROUPEDBOX(group), wbox, FALSE);
533 hbox = sw_make("net_proxy");
534 gtk_box_pack_start(GTK_BOX(wbox), hbox, FALSE, FALSE, 0);
536 cb = sw_make("net_useproxyauth");
537 gtk_box_pack_start(GTK_BOX(wbox), cb, FALSE, FALSE, 0);
539 table = proxyuserpass();
540 authgroup = groupedbox_new();
541 groupedbox_pack(GROUPEDBOX(authgroup), table, FALSE);
542 gtk_box_pack_start(GTK_BOX(wbox), authgroup, FALSE, FALSE, 0);
544 toggle_tie_enable(cb, table);
546 return group;
549 static GtkWidget*
550 programsettings(JamWin *jw) {
551 GtkWidget *group;
552 GtkWidget *button, *hbox;
553 SettingsWidget *sw;
554 JamView *jv = jam_win_get_cur_view(jw);
556 group = groupedbox_new_with_text(_("Programs"));
557 groupedbox_pack(GROUPEDBOX(group), sw_make("web_spawn"), FALSE);
559 groupedbox_pack(GROUPEDBOX(group), sw_make("music_command"), FALSE);
560 sw = sw_lookup("music_command");
561 g_signal_connect_swapped(G_OBJECT(sw->widget), "changed",
562 G_CALLBACK(jam_view_settings_changed), jv);
564 button = gtk_button_new_with_mnemonic(_("_Diagnose"));
565 g_signal_connect(G_OBJECT(button), "clicked",
566 G_CALLBACK(music_diagnose), NULL);
567 hbox = labelled_box_new_expand(_("Diagnose problems detecting music:"),
568 button, FALSE);
569 groupedbox_pack(GROUPEDBOX(group), hbox, FALSE);
571 return group;
574 static GtkWidget*
575 systemsettings(JamWin *jw) {
576 GtkWidget *vbox;
578 vbox = gtk_vbox_new(FALSE, 18);
579 gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
580 gtk_box_pack_start(GTK_BOX(vbox), programsettings(jw), FALSE, FALSE, 0);
581 gtk_box_pack_start(GTK_BOX(vbox), proxysettings(), FALSE, FALSE, 0);
582 return vbox;
584 #endif /* G_OS_WIN32 */
586 static GtkWidget*
587 debugsettings(GtkWidget *dlg) {
588 GtkWidget *vbox, *gb;
590 vbox = gtk_vbox_new(FALSE, 18);
591 gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
593 gb = groupedbox_new_with_text(_("Network"));
594 gtk_box_pack_start(GTK_BOX(vbox), gb, FALSE, FALSE, 0);
596 groupedbox_pack(GROUPEDBOX(gb), sw_make("debug_netdump"), FALSE);
597 groupedbox_pack(GROUPEDBOX(gb), sw_make("debug_nofork"), FALSE);
599 return vbox;
602 static GtkWidget*
603 cfriends_general_settings(JamAccountLJ *acc) {
604 GtkWidget *general, *b, *w;
605 SettingsWidget *sw;
607 general = groupedbox_new_with_text(_("General"));
609 groupedbox_pack(GROUPEDBOX(general), sw_make("cf_autostart"), FALSE);
611 b = sw_make("cf_threshold");
612 sw = sw_lookup("cf_threshold");
613 w = sw->widget;
614 jam_spin_button_set(GTK_SPIN_BUTTON(w), TRUE /* numeric */,
615 1.0, 7.0 /* range */, 1.0, 1.0 /*increments */, 0 /* digits */);
616 gtk_box_pack_start(GTK_BOX(b),
617 gtk_label_new(_("new entries by my friends")), FALSE, FALSE, 5);
618 /* XXX: ugly, because it defaults to "1 entries". Yuck. */
619 groupedbox_pack(GROUPEDBOX(general), b, FALSE);
621 b = sw_make("cf_userinterval");
622 sw = sw_lookup("cf_userinterval");
623 w = sw->widget;
624 jam_spin_button_set(GTK_SPIN_BUTTON(w), TRUE /*numeric*/,
625 45.0, 3600.0 /*range*/, 1.0, 10.0 /*increments*/, 0 /*digits*/);
626 gtk_box_pack_start(GTK_BOX(b), gtk_label_new(_("seconds")),
627 FALSE, FALSE, 5);
628 groupedbox_pack(GROUPEDBOX(general), b, FALSE);
631 return general;
634 static GtkWidget*
635 cfriends_filter_settings(JamAccountLJ *acc) {
636 GtkWidget *filter, *maskhbox, *l;
637 SettingsWidget *sw;
638 LJUser *u = jam_account_lj_get_user(acc);
640 filter = groupedbox_new_with_text(_("Filter"));
642 maskhbox = gtk_hbox_new(FALSE, 5);
643 gtk_box_pack_start(GTK_BOX(maskhbox),
644 sw_make("cf_usemask"), FALSE, FALSE, 0);
645 sw = sw_lookup("cf_mask");
646 sw->conf = acc;
647 sw->widget = gtk_button_new_with_label(_("Choose filter"));
648 gtk_box_pack_start(GTK_BOX(maskhbox), GTK_WIDGET(sw->widget), FALSE, FALSE, 0);
649 gtk_widget_set_sensitive(sw->widget, u->friendgroups != NULL);
650 g_signal_connect_swapped(G_OBJECT(sw->widget), "clicked",
651 G_CALLBACK(run_cfmask_settings_dlg), sw);
652 l = gtk_label_new(NULL);
653 gtk_label_set_markup(GTK_LABEL(l),
654 _("<small>The filter chosen here only affects the "
655 "current user.</small>"));
656 gtk_label_set_line_wrap(GTK_LABEL(l), TRUE);
657 gtk_label_set_justify(GTK_LABEL(l), GTK_JUSTIFY_LEFT);
659 groupedbox_pack(GROUPEDBOX(filter), maskhbox, FALSE);
660 groupedbox_pack(GROUPEDBOX(filter), l, FALSE);
662 return filter;
665 static void
666 float_change_cb(GtkWidget *w, CFMgr *cfm) {
667 cf_app_update_float();
670 static GtkWidget*
671 cfriends_indicators_settings(CFMgr *cfm) {
672 GtkWidget *indicators, *floaters, *b, *w;
673 SettingsWidget *sw;
675 indicators = groupedbox_new_with_text(_("Indicators"));
677 b = sw_make("cf_float");
678 g_signal_connect(G_OBJECT(b), "toggled",
679 G_CALLBACK(float_change_cb), cfm);
680 sw = sw_lookup("cf_float");
681 w = sw->widget;
682 groupedbox_pack(GROUPEDBOX(indicators), b, FALSE);
684 floaters = groupedbox_new();
685 groupedbox_pack(GROUPEDBOX(indicators), floaters, FALSE);
687 groupedbox_pack(GROUPEDBOX(floaters), sw_make("cf_floatraise"), FALSE);
688 b = sw_make("cf_float_decorate");
689 g_signal_connect(G_OBJECT(b), "toggled",
690 G_CALLBACK(cf_float_decorate_refresh), NULL);
691 groupedbox_pack(GROUPEDBOX(floaters), b, FALSE);
693 toggle_tie_enable(w, floaters);
695 return indicators;
698 static GtkWidget*
699 cfriendssettings(CFMgr *cfm) {
700 GtkWidget *vbox;
701 JamAccountLJ *acc = cfmgr_get_account(cfm);
703 vbox = gtk_vbox_new(FALSE, 18);
704 gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
706 /* general */
707 gtk_box_pack_start(GTK_BOX(vbox), cfriends_general_settings(acc),
708 FALSE, FALSE, 0);
710 /* filter */
711 gtk_box_pack_start(GTK_BOX(vbox), cfriends_filter_settings(acc),
712 FALSE, FALSE, 0);
714 /* indicators */
715 gtk_box_pack_start(GTK_BOX(vbox), cfriends_indicators_settings(cfm),
716 FALSE, FALSE, 0);
718 return vbox;
721 static void
722 run_settings_dialog(JamWin *jw) {
723 GtkWidget *dlg, *nb;
725 dlg = gtk_dialog_new_with_buttons(_("Preferences"),
726 GTK_WINDOW(jw), GTK_DIALOG_MODAL,
727 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
728 NULL);
730 /* the order of notebook pages created here should match the
731 * SettingsPage enum in settings.h */
732 nb = gtk_notebook_new();
733 gtk_notebook_append_page(GTK_NOTEBOOK(nb),
734 uisettings(jw), gtk_label_new_with_mnemonic(_("Interface")));
735 #ifndef G_OS_WIN32
736 gtk_notebook_append_page(GTK_NOTEBOOK(nb),
737 systemsettings(jw), gtk_label_new_with_mnemonic(_("System")));
738 #endif /* G_OS_WIN32 */
739 if (JAM_ACCOUNT_IS_LJ(jw->account))
740 gtk_notebook_append_page(GTK_NOTEBOOK(nb),
741 cfriendssettings(app.cfmgr), gtk_label_new_with_mnemonic(_("Check Friends")));
742 gtk_notebook_append_page(GTK_NOTEBOOK(nb),
743 debugsettings(dlg), gtk_label_new_with_mnemonic(_("Debug")));
745 jam_dialog_set_contents(GTK_DIALOG(dlg), nb);
747 /* XXX HACK: set_contents calls show_all, but the "command"
748 * settings widgets sometimes want to have hidden children.
749 * so we let them rehide. */
751 SettingsWidget *sw;
752 for (sw = settingswidgets; sw->name; sw++)
753 if (sw->type == SW_COMMAND)
754 command_changed_cb(GTK_OPTION_MENU(sw->widget), sw);
757 gtk_dialog_run(GTK_DIALOG(dlg));
758 gtk_widget_destroy(dlg);
761 void
762 settings_cf_run(CFMgr *cfm) {
763 GtkWidget *dlg;
765 dlg = gtk_dialog_new_with_buttons(_("Checkfriends Preferences"),
766 NULL /* no parent */, GTK_DIALOG_MODAL,
767 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
768 NULL);
770 jam_dialog_set_contents(GTK_DIALOG(dlg), cfriendssettings(cfm));
772 gtk_dialog_run(GTK_DIALOG(dlg));
773 gtk_widget_destroy(dlg);
776 void
777 settings_run(JamWin *jw) {
779 #ifdef HAVE_GTKSPELL
780 gboolean hadspell = conf.options.usespellcheck;
781 #endif
783 gboolean hadautosave = conf.options.autosave;
784 gboolean hadquotes = conf.options.smartquotes;
786 run_settings_dialog(jw);
788 g_slist_foreach(app.secmgr_list, (GFunc)secmgr_security_set_force,
789 &conf.defaultsecurity);
791 cf_threshold_normalize(&conf.cfthreshold);
793 if (!hadautosave && conf.options.autosave)
794 jam_autosave_init(jw);
795 if (hadautosave && !conf.options.autosave)
796 jam_autosave_stop(jw);
798 if (conf.options.allowmultipleinstances) {
799 GError *err = NULL;
800 if (!logjam_remote_stop_listening(app.remote, &err)) {
801 jam_warning(GTK_WINDOW(jw),
802 _("Error stopping remote listener: %s."), err->message);
803 g_error_free(err);
806 if (!conf.options.allowmultipleinstances) {
807 GError *err = NULL;
808 if (!logjam_remote_listen(app.remote, &err) && err) {
809 jam_warning(GTK_WINDOW(jw),
810 _("Error starting remote listener: %s."), err->message);
811 g_error_free(err);
815 if (hadquotes) smartquotes_detach(jam_doc_get_text_buffer(jw->doc));
816 if (conf.options.smartquotes)
817 smartquotes_attach(jam_doc_get_text_buffer(jw->doc), conf.options.smartquotes_russian);
819 jam_view_settings_changed(jam_win_get_cur_view(jw));
822 static void
823 run_cfmask_settings_dlg(SettingsWidget *sw) {
824 GtkWindow *window = GTK_WINDOW(gtk_widget_get_toplevel(sw->widget));
825 JamAccountLJ *acc = sw->conf;
826 jam_account_lj_set_cfmask(acc,
827 custom_security_dlg_run(window, jam_account_lj_get_cfmask(acc), acc));