1 /*
2 * Copyright 2012 Vincent Sanders <vince@netsurf-browser.org>
3 *
4 * This file is part of NetSurf, http://www.netsurf-browser.org/
5 *
6 * NetSurf is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * NetSurf 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 #include <stdlib.h>
20 #include <stdint.h>
21 #include <math.h>
22 #include <string.h>
23
24 #include "utils/messages.h"
25 #include "utils/nsoption.h"
26 #include "utils/file.h"
27 #include "utils/log.h"
28 #include "utils/nsurl.h"
29 #include "netsurf/browser_window.h"
30 #include "desktop/searchweb.h"
31
32 #include "gtk/compat.h"
33 #include "gtk/toolbar_items.h"
34 #include "gtk/window.h"
35 #include "gtk/gui.h"
36 #include "gtk/scaffolding.h"
37 #include "gtk/resources.h"
38 #include "gtk/preferences.h"
39
40 /* private prefs */
41 struct ppref {
42 /** dialog handle created when window first accessed */
43 GObject *dialog;
44
45 struct browser_window *bw;
46
47 /* widgets which are accessed from outside their own signal handlers */
48 GtkEntry *entryHomePageURL;
49 GtkEntry *entryProxyHost;
50 GtkEntry *entryProxyUser;
51 GtkEntry *entryProxyPassword;
52 GtkEntry *entryProxyNoproxy;
53 GtkSpinButton *spinProxyPort;
54
55 /* dynamic list stores */
56 GtkListStore *content_language;
57 GtkListStore *search_providers;
58 };
59
60 static struct ppref ppref;
61
62
63 /* Set netsurf option based on toggle button state
64 *
65 * This works for any widget which subclasses togglebutton (checkbox,
66 * radiobutton etc.)
67 */
68 #define TOGGLEBUTTON_SIGNALS(WIDGET, OPTION) \
69 G_MODULE_EXPORT void \
70 nsgtk_preferences_##WIDGET##_toggled(GtkToggleButton *togglebutton, \
71 struct ppref *priv); \
72 G_MODULE_EXPORT void \
73 nsgtk_preferences_##WIDGET##_toggled(GtkToggleButton *togglebutton, \
74 struct ppref *priv) \
75 { \
76 nsoption_set_bool(OPTION, \
77 gtk_toggle_button_get_active(togglebutton)); \
78 } \
79 \
80 G_MODULE_EXPORT void \
81 nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, \
82 struct ppref *priv); \
83 G_MODULE_EXPORT void \
84 nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, \
85 struct ppref *priv) \
86 { \
87 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), \
88 nsoption_bool(OPTION)); \
89 }
90
91 #define SPINBUTTON_SIGNALS(WIDGET, OPTION, MULTIPLIER) \
92 G_MODULE_EXPORT void \
93 nsgtk_preferences_##WIDGET##_valuechanged(GtkSpinButton *spinbutton, \
94 struct ppref *priv); \
95 G_MODULE_EXPORT void \
96 nsgtk_preferences_##WIDGET##_valuechanged(GtkSpinButton *spinbutton, \
97 struct ppref *priv) \
98 { \
99 nsoption_set_int(OPTION, \
100 round(gtk_spin_button_get_value(spinbutton) * MULTIPLIER)); \
101 } \
102 \
103 G_MODULE_EXPORT void \
104 nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, struct ppref *priv); \
105 G_MODULE_EXPORT void \
106 nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, struct ppref *priv) \
107 { \
108 gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget), \
109 ((gdouble)nsoption_int(OPTION)) / MULTIPLIER); \
110 }
111
112 #define SPINBUTTON_UINT_SIGNALS(WIDGET, OPTION, MULTIPLIER) \
113 G_MODULE_EXPORT void \
114 nsgtk_preferences_##WIDGET##_valuechanged(GtkSpinButton *spinbutton, \
115 struct ppref *priv); \
116 G_MODULE_EXPORT void \
117 nsgtk_preferences_##WIDGET##_valuechanged(GtkSpinButton *spinbutton, \
118 struct ppref *priv) \
119 { \
120 nsoption_set_uint(OPTION, \
121 round(gtk_spin_button_get_value(spinbutton) * MULTIPLIER)); \
122 } \
123 \
124 G_MODULE_EXPORT void \
125 nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, struct ppref *priv); \
126 G_MODULE_EXPORT void \
127 nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, struct ppref *priv) \
128 { \
129 gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget), \
130 ((gdouble)nsoption_uint(OPTION)) / MULTIPLIER); \
131 }
132
133 #define ENTRY_SIGNALS(WIDGET, OPTION) \
134 G_MODULE_EXPORT void \
135 nsgtk_preferences_##WIDGET##_changed(GtkEditable *editable, struct ppref *priv); \
136 G_MODULE_EXPORT void \
137 nsgtk_preferences_##WIDGET##_changed(GtkEditable *editable, struct ppref *priv)\
138 { \
139 nsoption_set_charp(OPTION, \
140 strdup(gtk_entry_get_text(GTK_ENTRY(editable)))); \
141 } \
142 \
143 G_MODULE_EXPORT void \
144 nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, struct ppref *priv); \
145 G_MODULE_EXPORT void \
146 nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, struct ppref *priv) \
147 { \
148 const char *OPTION; \
149 OPTION = nsoption_charp(OPTION); \
150 if (OPTION != NULL) { \
151 gtk_entry_set_text(GTK_ENTRY(widget), OPTION); \
152 } \
153 }
154
155 /* GTK module requires these to be exported symbols so these all need
156 * forward declaring to avoid warnings
157 */
158 G_MODULE_EXPORT void nsgtk_preferences_comboProxyType_changed(GtkComboBox *combo, struct ppref *priv);
159 G_MODULE_EXPORT void nsgtk_preferences_comboProxyType_realize(GtkWidget *widget, struct ppref *priv);
160 G_MODULE_EXPORT void nsgtk_preferences_comboboxLoadImages_changed(GtkComboBox *combo, struct ppref *priv);
161 G_MODULE_EXPORT void nsgtk_preferences_comboboxLoadImages_realize(GtkWidget *widget, struct ppref *priv);
162 G_MODULE_EXPORT void nsgtk_preferences_comboDefault_changed(GtkComboBox *combo, struct ppref *priv);
163 G_MODULE_EXPORT void nsgtk_preferences_comboDefault_realize(GtkWidget *widget, struct ppref *priv);
164 G_MODULE_EXPORT void nsgtk_preferences_fontPreview_clicked(GtkButton *button, struct ppref *priv);
165 G_MODULE_EXPORT void nsgtk_preferences_comboboxLanguage_changed(GtkComboBox *combo, struct ppref *priv);
166 G_MODULE_EXPORT void nsgtk_preferences_comboboxLanguage_realize(GtkWidget *widget, struct ppref *priv);
167 G_MODULE_EXPORT void nsgtk_preferences_checkShowSingleTab_toggled(GtkToggleButton *togglebutton, struct ppref *priv);
168 G_MODULE_EXPORT void nsgtk_preferences_checkShowSingleTab_realize(GtkWidget *widget, struct ppref *priv);
169 G_MODULE_EXPORT void nsgtk_preferences_comboTabPosition_changed(GtkComboBox *widget, struct ppref *priv);
170 G_MODULE_EXPORT void nsgtk_preferences_comboTabPosition_realize(GtkWidget *widget, struct ppref *priv);
171 G_MODULE_EXPORT void nsgtk_preferences_comboDeveloperView_changed(GtkComboBox *widget, struct ppref *priv);
172 G_MODULE_EXPORT void nsgtk_preferences_comboDeveloperView_realize(GtkWidget *widget, struct ppref *priv);
173 G_MODULE_EXPORT void nsgtk_preferences_comboButtonType_changed(GtkComboBox *widget, struct ppref *priv);
174 G_MODULE_EXPORT void nsgtk_preferences_comboButtonType_realize(GtkWidget *widget, struct ppref *priv);
175 G_MODULE_EXPORT void nsgtk_preferences_setCurrentPage_clicked(GtkButton *button, struct ppref *priv);
176 G_MODULE_EXPORT void nsgtk_preferences_setDefaultPage_clicked(GtkButton *button, struct ppref *priv);
177 G_MODULE_EXPORT void nsgtk_preferences_comboSearch_changed(GtkComboBox *widget, struct ppref *priv);
178 G_MODULE_EXPORT void nsgtk_preferences_comboSearch_realize(GtkWidget *widget, struct ppref *priv);
179 G_MODULE_EXPORT void nsgtk_preferences_fileChooserDownloads_selectionchanged(GtkFileChooser *chooser, struct ppref *priv);
180 G_MODULE_EXPORT void nsgtk_preferences_fileChooserDownloads_realize(GtkWidget *widget, struct ppref *priv);
181 G_MODULE_EXPORT void nsgtk_preferences_dialogPreferences_response(GtkDialog *dlg, gint resid);
182 G_MODULE_EXPORT gboolean nsgtk_preferences_dialogPreferences_deleteevent(GtkDialog *dlg, struct ppref *priv);
183 G_MODULE_EXPORT void nsgtk_preferences_dialogPreferences_destroy(GtkDialog *dlg, struct ppref *priv);
184
185
186 /********* PDF **********/
187
188 /* Appearance */
189
190 /* no images in output */
TOGGLEBUTTON_SIGNALS(checkSuppressImages,suppress_images)191 TOGGLEBUTTON_SIGNALS(checkSuppressImages, suppress_images)
192
193 /* no background images */
194 TOGGLEBUTTON_SIGNALS(checkRemoveBackgrounds, remove_backgrounds)
195
196 /* scale to fit page */
197 TOGGLEBUTTON_SIGNALS(checkFitPage, enable_loosening)
198
199 /* port */
200 SPINBUTTON_SIGNALS(spinExportScale, export_scale, 1.0)
201
202 /* Margins */
203 SPINBUTTON_SIGNALS(spinMarginTop, margin_top, 1.0)
204 SPINBUTTON_SIGNALS(spinMarginBottom, margin_bottom, 1.0)
205 SPINBUTTON_SIGNALS(spinMarginLeft, margin_left, 1.0)
206 SPINBUTTON_SIGNALS(spinMarginRight, margin_right, 1.0)
207
208
209 /* Generation */
210
211 /* output is compressed */
212 TOGGLEBUTTON_SIGNALS(checkCompressPDF, enable_PDF_compression)
213
214 /* output has a password */
215 TOGGLEBUTTON_SIGNALS(checkPasswordPDF, enable_PDF_password)
216
217 /********* Network **********/
218
219 /* HTTP proxy */
220 static void set_proxy_widgets_sensitivity(int proxyval, struct ppref *priv)
221 {
222 gboolean host;
223 gboolean port;
224 gboolean user;
225 gboolean pass;
226 gboolean noproxy;
227
228 switch (proxyval) {
229 case 0: /* no proxy */
230 host = FALSE;
231 port = FALSE;
232 user = FALSE;
233 pass = FALSE;
234 noproxy = FALSE;
235 break;
236
237 case 1: /* proxy with no auth */
238 host = TRUE;
239 port = TRUE;
240 user = FALSE;
241 pass = FALSE;
242 noproxy = TRUE;
243 break;
244
245 case 2: /* proxy with basic auth */
246 host = TRUE;
247 port = TRUE;
248 user = TRUE;
249 pass = TRUE;
250 noproxy = TRUE;
251 break;
252
253 case 3: /* proxy with ntlm auth */
254 host = TRUE;
255 port = TRUE;
256 user = TRUE;
257 pass = TRUE;
258 noproxy = TRUE;
259 break;
260
261 case 4: /* system proxy */
262 host = FALSE;
263 port = FALSE;
264 user = FALSE;
265 pass = FALSE;
266 noproxy = FALSE;
267 break;
268
269 default:
270 return;
271 }
272
273 gtk_widget_set_sensitive(GTK_WIDGET(priv->entryProxyHost), host);
274 gtk_widget_set_sensitive(GTK_WIDGET(priv->spinProxyPort), port);
275 gtk_widget_set_sensitive(GTK_WIDGET(priv->entryProxyUser), user);
276 gtk_widget_set_sensitive(GTK_WIDGET(priv->entryProxyPassword), pass);
277 gtk_widget_set_sensitive(GTK_WIDGET(priv->entryProxyNoproxy), noproxy);
278
279 }
280
281 G_MODULE_EXPORT void
nsgtk_preferences_comboProxyType_changed(GtkComboBox * combo,struct ppref * priv)282 nsgtk_preferences_comboProxyType_changed(GtkComboBox *combo, struct ppref *priv)
283 {
284 int proxy_sel;
285 proxy_sel = gtk_combo_box_get_active(combo);
286
287 switch (proxy_sel) {
288 case 0: /* no proxy */
289 nsoption_set_bool(http_proxy, false);
290 break;
291
292 case 1: /* proxy with no auth */
293 nsoption_set_bool(http_proxy, true);
294 nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_NONE);
295 break;
296
297 case 2: /* proxy with basic auth */
298 nsoption_set_bool(http_proxy, true);
299 nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_BASIC);
300 break;
301
302 case 3: /* proxy with ntlm auth */
303 nsoption_set_bool(http_proxy, true);
304 nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_NTLM);
305 break;
306
307 case 4: /* system proxy */
308 nsoption_set_bool(http_proxy, true);
309 nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_NONE);
310 break;
311 }
312
313 set_proxy_widgets_sensitivity(proxy_sel, priv);
314 }
315
316 G_MODULE_EXPORT void
nsgtk_preferences_comboProxyType_realize(GtkWidget * widget,struct ppref * priv)317 nsgtk_preferences_comboProxyType_realize(GtkWidget *widget, struct ppref *priv)
318 {
319 int proxytype = 0; /* no proxy by default */
320
321 if (nsoption_bool(http_proxy) == true) {
322 /* proxy type combo box starts with disabled, to allow
323 * for this the http_proxy option needs combining with
324 * the http_proxy_auth option
325 */
326 proxytype = nsoption_int(http_proxy_auth) + 1;
327 if (nsoption_charp(http_proxy_host) == NULL) {
328 /* set to use a proxy without a host, turn proxy off */
329 proxytype = 0;
330 } else if (((proxytype == 2) ||
331 (proxytype == 3)) &&
332 ((nsoption_charp(http_proxy_auth_user) == NULL) ||
333 (nsoption_charp(http_proxy_auth_pass) == NULL))) {
334 /* authentication selected with empty credentials, turn proxy off */
335 proxytype = 0;
336 }
337 }
338 gtk_combo_box_set_active(GTK_COMBO_BOX(widget), proxytype);
339
340 set_proxy_widgets_sensitivity(proxytype, priv);
341 }
342
343 /* host */
ENTRY_SIGNALS(entryProxyHost,http_proxy_host)344 ENTRY_SIGNALS(entryProxyHost, http_proxy_host)
345
346 /* port */
347 SPINBUTTON_SIGNALS(spinProxyPort, http_proxy_port, 1.0)
348
349 /* user */
350 ENTRY_SIGNALS(entryProxyUser, http_proxy_auth_user)
351
352 /* password */
353 ENTRY_SIGNALS(entryProxyPassword, http_proxy_auth_pass)
354
355 /* no proxy */
356 ENTRY_SIGNALS(entryProxyNoproxy, http_proxy_noproxy)
357
358 /* Fetching */
359
360 /* maximum fetchers */
361 SPINBUTTON_SIGNALS(spinMaxFetchers, max_fetchers, 1.0)
362
363 /* fetches per host */
364 SPINBUTTON_SIGNALS(spinFetchesPerHost, max_fetchers_per_host, 1.0)
365
366 /* cached connections */
367 SPINBUTTON_SIGNALS(spinCachedConnections, max_cached_fetch_handles, 1.0)
368
369
370 /********* Privacy **********/
371
372 /* General */
373
374 /* enable referral submission */
375 TOGGLEBUTTON_SIGNALS(checkSendReferer, send_referer)
376
377 /* send do not track */
378 TOGGLEBUTTON_SIGNALS(checkSendDNT, do_not_track)
379
380 /* History */
381
382 /* local history shows url tooltips */
383 TOGGLEBUTTON_SIGNALS(checkHoverURLs, hover_urls)
384
385 /* remember browsing history */
386 SPINBUTTON_SIGNALS(spinHistoryAge, history_age, 1.0)
387
388 /* Cache */
389
390 /* memory cache size */
391 SPINBUTTON_SIGNALS(spinMemoryCacheSize, memory_cache_size, (1024*1024))
392
393 /* disc cache size */
394 SPINBUTTON_UINT_SIGNALS(spinDiscCacheSize, disc_cache_size, (1024*1024))
395
396
397 /* disc cache age */
398 SPINBUTTON_SIGNALS(spinDiscCacheAge, disc_cache_age, 1.0)
399
400
401 /********* Content **********/
402
403 /* Control */
404
405
406 /* prevent popups */
407 TOGGLEBUTTON_SIGNALS(checkDisablePopups, disable_popups)
408
409 /* hide adverts */
410 TOGGLEBUTTON_SIGNALS(checkHideAdverts, block_advertisements)
411
412 /* enable javascript */
413 TOGGLEBUTTON_SIGNALS(checkEnableJavascript, enable_javascript)
414
415 /* load and display of images */
416 G_MODULE_EXPORT void
417 nsgtk_preferences_comboboxLoadImages_changed(GtkComboBox *combo,
418 struct ppref *priv)
419 {
420 int img_sel;
421 /* get the row number for the selection */
422 img_sel = gtk_combo_box_get_active(combo);
423 switch (img_sel) {
424 case 0:
425 /* background and foreground */
426 nsoption_set_bool(foreground_images, true);
427 nsoption_set_bool(background_images, true);
428 break;
429
430 case 1:
431 /* foreground only */
432 nsoption_set_bool(foreground_images, true);
433 nsoption_set_bool(background_images, false);
434 break;
435
436 case 2:
437 /* background only */
438 nsoption_set_bool(foreground_images, false);
439 nsoption_set_bool(background_images, true);
440 break;
441
442 case 3:
443 /* no images */
444 nsoption_set_bool(foreground_images, false);
445 nsoption_set_bool(background_images, false);
446 break;
447 }
448 }
449
450 G_MODULE_EXPORT void
nsgtk_preferences_comboboxLoadImages_realize(GtkWidget * widget,struct ppref * priv)451 nsgtk_preferences_comboboxLoadImages_realize(GtkWidget *widget,
452 struct ppref *priv)
453 {
454 if (nsoption_bool(foreground_images)) {
455 if (nsoption_bool(background_images)) {
456 /* background and foreground */
457 gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 0);
458 } else {
459 /* foreground only */
460 gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 1);
461 }
462 } else {
463 if (nsoption_bool(background_images)) {
464 /* background only */
465 gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 2);
466 } else {
467 /* no images */
468 gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 3);
469 }
470 }
471 }
472
473 /* Animation */
474
475 /* enable animation */
TOGGLEBUTTON_SIGNALS(checkEnableAnimations,animate_images)476 TOGGLEBUTTON_SIGNALS(checkEnableAnimations, animate_images)
477
478 /* Fonts */
479
480 /* default font */
481 G_MODULE_EXPORT void
482 nsgtk_preferences_comboDefault_changed(GtkComboBox *combo, struct ppref *priv)
483 {
484 int font_sel;
485 /* get the row number for the selection */
486 font_sel = gtk_combo_box_get_active(combo);
487 if ((font_sel >= 0) && (font_sel <= 4)) {
488 nsoption_set_int(font_default, font_sel);
489 }
490 }
491
492 G_MODULE_EXPORT void
nsgtk_preferences_comboDefault_realize(GtkWidget * widget,struct ppref * priv)493 nsgtk_preferences_comboDefault_realize(GtkWidget *widget, struct ppref *priv)
494 {
495 gtk_combo_box_set_active(GTK_COMBO_BOX(widget),
496 nsoption_int(font_default));
497 }
498
499 /* default font size */
500 SPINBUTTON_SIGNALS(spinDefaultSize, font_size, 10.0)
501
502 /* preview - actually reflow all views */
503 G_MODULE_EXPORT void
nsgtk_preferences_fontPreview_clicked(GtkButton * button,struct ppref * priv)504 nsgtk_preferences_fontPreview_clicked(GtkButton *button, struct ppref *priv)
505 {
506 nsgtk_window_update_all();
507 }
508
509
510 /* Language */
511
512 /* accept language */
513 G_MODULE_EXPORT void
nsgtk_preferences_comboboxLanguage_changed(GtkComboBox * combo,struct ppref * priv)514 nsgtk_preferences_comboboxLanguage_changed(GtkComboBox *combo,
515 struct ppref *priv)
516 {
517 gchar *lang = NULL;
518 GtkTreeIter iter;
519 GtkTreeModel *model;
520
521 /* Obtain currently selected item from combo box.
522 * If nothing is selected, do nothing.
523 */
524 if (gtk_combo_box_get_active_iter(combo, &iter)) {
525 /* Obtain data model from combo box. */
526 model = gtk_combo_box_get_model(combo);
527
528 /* Obtain string from model. */
529 gtk_tree_model_get(model, &iter, 0, &lang, -1);
530 }
531
532 if (lang != NULL) {
533 nsoption_set_charp(accept_language, strdup(lang));
534 g_free(lang);
535 }
536 }
537
538 /**
539 * populate language combo from data
540 */
541 static nserror
comboboxLanguage_add_from_data(GtkListStore * liststore,GtkComboBox * combobox,const char * accept_language,const uint8_t * data,size_t data_size)542 comboboxLanguage_add_from_data(GtkListStore *liststore,
543 GtkComboBox *combobox,
544 const char *accept_language,
545 const uint8_t *data,
546 size_t data_size)
547 {
548 int active_language = -1;
549 GtkTreeIter iter;
550 int combo_row_count = 0;
551 const uint8_t *s;
552 const uint8_t *nl;
553 char buf[50];
554 int bufi = 0;
555
556 gtk_list_store_clear(liststore);
557 s = data;
558
559 while (s < (data + data_size)) {
560 /* find nl and copy buffer */
561 for (nl = s; nl < data + data_size; nl++) {
562 if ((*nl == '\n') || (bufi == (sizeof(buf) - 2))) {
563 buf[bufi] = 0; /* null terminate */
564 break;
565 }
566 buf[bufi++] = *nl;
567 }
568 if (bufi > 0) {
569 gtk_list_store_append(liststore, &iter);
570 gtk_list_store_set(liststore, &iter, 0, buf, -1 );
571
572 if (strcmp(buf, accept_language) == 0) {
573 active_language = combo_row_count;
574 }
575
576 combo_row_count++;
577 }
578 bufi = 0;
579 s = nl + 1; /* skip newline */
580 }
581
582 /* if configured language was not in list, add it */
583 if (active_language == -1) {
584 gtk_list_store_append(liststore, &iter);
585 gtk_list_store_set(liststore, &iter, 0, accept_language, -1);
586 active_language = combo_row_count;
587 }
588
589 gtk_combo_box_set_active(combobox, active_language);
590
591 return NSERROR_OK;
592 }
593
594 /**
595 * populate language combo from file
596 */
597 static nserror
comboboxLanguage_add_from_file(GtkListStore * liststore,GtkComboBox * combobox,const char * accept_language,const char * file_location)598 comboboxLanguage_add_from_file(GtkListStore *liststore,
599 GtkComboBox *combobox,
600 const char *accept_language,
601 const char *file_location)
602 {
603 int active_language = -1;
604 GtkTreeIter iter;
605 int combo_row_count = 0;
606 FILE *fp;
607 char buf[50];
608
609 fp = fopen(file_location, "r");
610 if (fp == NULL) {
611 return NSERROR_NOT_FOUND;
612 }
613
614 gtk_list_store_clear(liststore);
615
616 NSLOG(netsurf, INFO, "Used %s for languages", file_location);
617 while (fgets(buf, sizeof(buf), fp)) {
618 /* Ignore blank lines */
619 if (buf[0] == '\0')
620 continue;
621
622 /* Remove trailing \n */
623 buf[strlen(buf) - 1] = '\0';
624
625 gtk_list_store_append(liststore, &iter);
626 gtk_list_store_set(liststore, &iter, 0, buf, -1 );
627
628 if (strcmp(buf, accept_language) == 0) {
629 active_language = combo_row_count;
630 }
631
632 combo_row_count++;
633 }
634
635 /* if configured language was not in list, add it */
636 if (active_language == -1) {
637 gtk_list_store_append(liststore, &iter);
638 gtk_list_store_set(liststore, &iter, 0, accept_language, -1);
639 active_language = combo_row_count;
640 }
641
642 fclose(fp);
643
644 gtk_combo_box_set_active(combobox, active_language);
645
646 return NSERROR_OK;
647 }
648
649 /**
650 * Fill content language list store.
651 */
652 G_MODULE_EXPORT void
nsgtk_preferences_comboboxLanguage_realize(GtkWidget * widget,struct ppref * priv)653 nsgtk_preferences_comboboxLanguage_realize(GtkWidget *widget,
654 struct ppref *priv)
655 {
656 nserror res;
657 const uint8_t *data;
658 size_t data_size;
659 const char *languages_file;
660 const char *accept_language;
661
662 if (priv->content_language == NULL) {
663 NSLOG(netsurf, INFO, "content language list store unavailable");
664 return;
665 }
666
667 /* get current accept language */
668 accept_language = nsoption_charp(accept_language);
669 if (accept_language == NULL) {
670 accept_language = "en";
671 }
672
673 /* attempt to read languages from inline resource */
674 res = nsgtk_data_from_resname("languages", &data, &data_size);
675 if (res == NSERROR_OK) {
676 res = comboboxLanguage_add_from_data(priv->content_language,
677 GTK_COMBO_BOX(widget),
678 accept_language,
679 data,
680 data_size);
681 } else {
682 /* attempt to read languages from file */
683 res = nsgtk_path_from_resname("languages", &languages_file);
684 if (res == NSERROR_OK) {
685 res = comboboxLanguage_add_from_file(priv->content_language,
686 GTK_COMBO_BOX(widget),
687 accept_language,
688 languages_file);
689 }
690 }
691 if (res != NSERROR_OK) {
692 NSLOG(netsurf, INFO, "error populatiung languages combo");
693 }
694 }
695
696
697 /********* Apperance **********/
698
699 /* Tabs */
700
701 /* always show tab bar */
702 G_MODULE_EXPORT void
nsgtk_preferences_checkShowSingleTab_toggled(GtkToggleButton * togglebutton,struct ppref * priv)703 nsgtk_preferences_checkShowSingleTab_toggled(GtkToggleButton *togglebutton,
704 struct ppref *priv)
705 {
706 nsoption_set_bool(show_single_tab,
707 gtk_toggle_button_get_active(togglebutton));
708 nsgtk_window_update_all();
709 }
710
711 G_MODULE_EXPORT void
nsgtk_preferences_checkShowSingleTab_realize(GtkWidget * widget,struct ppref * priv)712 nsgtk_preferences_checkShowSingleTab_realize(GtkWidget *widget,
713 struct ppref *priv)
714 {
715 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
716 nsoption_bool(show_single_tab));
717 }
718
719 /* switch to newly opened tabs immediately */
TOGGLEBUTTON_SIGNALS(checkFocusNew,focus_new)720 TOGGLEBUTTON_SIGNALS(checkFocusNew, focus_new)
721
722 /* newly opened tabs are blank */
723 TOGGLEBUTTON_SIGNALS(checkNewBlank, new_blank)
724
725 /* tab position */
726 G_MODULE_EXPORT void
727 nsgtk_preferences_comboTabPosition_changed(GtkComboBox *widget,
728 struct ppref *priv)
729 {
730 /* set the option */
731 nsoption_set_int(position_tab, gtk_combo_box_get_active(widget));
732
733 /* update all windows */
734 nsgtk_window_update_all();
735 }
736
737 G_MODULE_EXPORT void
nsgtk_preferences_comboTabPosition_realize(GtkWidget * widget,struct ppref * priv)738 nsgtk_preferences_comboTabPosition_realize(GtkWidget *widget,
739 struct ppref *priv)
740 {
741 gtk_combo_box_set_active(GTK_COMBO_BOX(widget),
742 nsoption_int(position_tab));
743 }
744
745 /* Tools */
746
747 /* developer view opening */
748 G_MODULE_EXPORT void
nsgtk_preferences_comboDeveloperView_changed(GtkComboBox * widget,struct ppref * priv)749 nsgtk_preferences_comboDeveloperView_changed(GtkComboBox *widget,
750 struct ppref *priv)
751 {
752 /* set the option */
753 nsoption_set_int(developer_view, gtk_combo_box_get_active(widget));
754 }
755
756 G_MODULE_EXPORT void
nsgtk_preferences_comboDeveloperView_realize(GtkWidget * widget,struct ppref * priv)757 nsgtk_preferences_comboDeveloperView_realize(GtkWidget *widget,
758 struct ppref *priv)
759 {
760 gtk_combo_box_set_active(GTK_COMBO_BOX(widget),
761 nsoption_int(developer_view));
762 }
763
764
765 /* URLbar */
766
767 /* show recently visited urls as you type */
TOGGLEBUTTON_SIGNALS(checkDisplayRecentURLs,url_suggestion)768 TOGGLEBUTTON_SIGNALS(checkDisplayRecentURLs, url_suggestion)
769
770 /* Toolbar */
771
772 /* button position */
773 G_MODULE_EXPORT void
774 nsgtk_preferences_comboButtonType_changed(GtkComboBox *widget,
775 struct ppref *priv)
776 {
777 nsoption_set_int(button_type, gtk_combo_box_get_active(widget) + 1);
778
779 /* update all windows to adopt change */
780 nsgtk_window_update_all();
781 }
782
783 G_MODULE_EXPORT void
nsgtk_preferences_comboButtonType_realize(GtkWidget * widget,struct ppref * priv)784 nsgtk_preferences_comboButtonType_realize(GtkWidget *widget,
785 struct ppref *priv)
786 {
787 gtk_combo_box_set_active(GTK_COMBO_BOX(widget),
788 nsoption_int(button_type) - 1);
789 }
790
791
792
793 /************ Main ************/
794
795 /* Startup */
796
797 /* entry HomePageURL widget */
ENTRY_SIGNALS(entryHomePageURL,homepage_url)798 ENTRY_SIGNALS(entryHomePageURL, homepage_url)
799
800 /* put current page into homepage url */
801 G_MODULE_EXPORT void
802 nsgtk_preferences_setCurrentPage_clicked(GtkButton *button, struct ppref *priv)
803 {
804 const gchar *url = nsurl_access(browser_window_access_url(priv->bw));
805
806 if (priv->entryHomePageURL != NULL) {
807 gtk_entry_set_text(GTK_ENTRY(priv->entryHomePageURL), url);
808 nsoption_set_charp(homepage_url, strdup(url));
809 }
810 }
811
812 /* put default page into homepage */
813 G_MODULE_EXPORT void
nsgtk_preferences_setDefaultPage_clicked(GtkButton * button,struct ppref * priv)814 nsgtk_preferences_setDefaultPage_clicked(GtkButton *button, struct ppref *priv)
815 {
816 const gchar *url = NETSURF_HOMEPAGE;
817
818 if (priv->entryHomePageURL != NULL) {
819 gtk_entry_set_text(GTK_ENTRY(priv->entryHomePageURL), url);
820 nsoption_set_charp(homepage_url, strdup(url));
821 }
822 }
823
824 /* Search */
825
826 /* Url Search widget */
TOGGLEBUTTON_SIGNALS(checkUrlSearch,search_url_bar)827 TOGGLEBUTTON_SIGNALS(checkUrlSearch, search_url_bar)
828
829 /* provider combo */
830 G_MODULE_EXPORT void
831 nsgtk_preferences_comboSearch_changed(GtkComboBox *widget, struct ppref *priv)
832 {
833 int provider;
834
835 provider = gtk_combo_box_get_active(widget);
836
837 /* set the option */
838 nsoption_set_int(search_provider, provider);
839
840 /* set search provider */
841 search_web_select_provider(provider);
842 }
843
844 G_MODULE_EXPORT void
nsgtk_preferences_comboSearch_realize(GtkWidget * widget,struct ppref * priv)845 nsgtk_preferences_comboSearch_realize(GtkWidget *widget, struct ppref *priv)
846 {
847 int iter;
848 const char *name;
849 int provider = nsoption_int(search_provider);
850
851 if (priv->search_providers != NULL) {
852 gtk_list_store_clear(priv->search_providers);
853 for (iter = search_web_iterate_providers(0, &name);
854 iter != -1;
855 iter = search_web_iterate_providers(iter, &name)) {
856 gtk_list_store_insert_with_values(priv->search_providers,
857 NULL, -1,
858 0, name, -1);
859 }
860 }
861
862 gtk_combo_box_set_active(GTK_COMBO_BOX(widget), provider);
863 }
864
865
866 /* Downloads */
867
868 /* clear downloads */
TOGGLEBUTTON_SIGNALS(checkClearDownloads,downloads_clear)869 TOGGLEBUTTON_SIGNALS(checkClearDownloads, downloads_clear)
870
871 /* request overwite */
872 TOGGLEBUTTON_SIGNALS(checkRequestOverwrite, request_overwrite)
873
874 /* download location
875 *
876 * note selection-changed is used instead of file-set as the returned
877 * filename when that signal are used is incorrect. Though this signal
878 * does update frequently often with the same data.
879 */
880 G_MODULE_EXPORT void
881 nsgtk_preferences_fileChooserDownloads_selectionchanged(GtkFileChooser *chooser,
882 struct ppref *priv)
883 {
884 gchar *dir;
885 dir = gtk_file_chooser_get_filename(chooser);
886 nsoption_set_charp(downloads_directory, strdup(dir));
887 g_free(dir);
888 }
889
890 G_MODULE_EXPORT void
nsgtk_preferences_fileChooserDownloads_realize(GtkWidget * widget,struct ppref * priv)891 nsgtk_preferences_fileChooserDownloads_realize(GtkWidget *widget,
892 struct ppref *priv)
893 {
894 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(widget),
895 nsoption_charp(downloads_directory));
896 }
897
898
899 /************* Dialog window ***********/
900
901 /* dialog close and destroy events */
902 G_MODULE_EXPORT void
nsgtk_preferences_dialogPreferences_response(GtkDialog * dlg,gint resid)903 nsgtk_preferences_dialogPreferences_response(GtkDialog *dlg, gint resid)
904 {
905 char *choices = NULL;
906
907 if (resid == GTK_RESPONSE_CLOSE) {
908 netsurf_mkpath(&choices, NULL, 2, nsgtk_config_home, "Choices");
909 if (choices != NULL) {
910 nsoption_write(choices, NULL, NULL);
911 free(choices);
912 }
913 gtk_widget_hide(GTK_WIDGET(dlg));
914 }
915 }
916
917 G_MODULE_EXPORT gboolean
nsgtk_preferences_dialogPreferences_deleteevent(GtkDialog * dlg,struct ppref * priv)918 nsgtk_preferences_dialogPreferences_deleteevent(GtkDialog *dlg,
919 struct ppref *priv)
920 {
921 char *choices = NULL;
922
923 netsurf_mkpath(&choices, NULL, 2, nsgtk_config_home, "Choices");
924 if (choices != NULL) {
925 nsoption_write(choices, NULL, NULL);
926 free(choices);
927 }
928
929 gtk_widget_hide(GTK_WIDGET(dlg));
930
931 /* Delt with it by hiding window, no need to destory widget by
932 * default.
933 */
934 return TRUE;
935 }
936
937 G_MODULE_EXPORT void
nsgtk_preferences_dialogPreferences_destroy(GtkDialog * dlg,struct ppref * priv)938 nsgtk_preferences_dialogPreferences_destroy(GtkDialog *dlg, struct ppref *priv)
939 {
940 char *choices = NULL;
941
942 netsurf_mkpath(&choices, NULL, 2, nsgtk_config_home, "Choices");
943 if (choices != NULL) {
944 nsoption_write(choices, NULL, NULL);
945 free(choices);
946 }
947 }
948
949
950 /* exported interface documented in gtk/preferences.h */
nsgtk_preferences(struct browser_window * bw,GtkWindow * parent)951 GtkWidget* nsgtk_preferences(struct browser_window *bw, GtkWindow *parent)
952 {
953 GtkBuilder *preferences_builder;
954 struct ppref *priv = &ppref;
955 nserror res;
956
957 priv->bw = bw; /* for setting "current" page */
958
959 /* memoised dialog creation */
960 if (priv->dialog != NULL) {
961 gtk_window_set_transient_for(GTK_WINDOW(priv->dialog), parent);
962 return GTK_WIDGET(priv->dialog);
963 }
964
965 const char *client_lang;
966 char ui_lang[10] = "en";
967 client_lang = getenv("LANG");
968
969 if (strlen(client_lang)>1)
970 {
971 strncpy(ui_lang,client_lang,2);
972 }
973
974 strcat(ui_lang,"options");
975
976 res = nsgtk_builder_new_from_resname(ui_lang, &preferences_builder);
977 if (res != NSERROR_OK) {
978 //LANG not found, so fall back to english i guess
979 res = nsgtk_builder_new_from_resname("enoption", &preferences_builder);
980 }
981 if (res != NSERROR_OK) {
982 NSLOG(netsurf, INFO, "Preferences UI builder init failed");
983 return NULL;
984 }
985
986 priv->dialog = gtk_builder_get_object(preferences_builder,
987 "dialogPreferences");
988 if (priv->dialog == NULL) {
989 NSLOG(netsurf, INFO,
990 "Unable to get object for preferences dialog");
991 /* release builder as were done with it */
992 g_object_unref(G_OBJECT(preferences_builder));
993 return NULL;
994 }
995
996 /* need to explicitly obtain handles for some widgets enabling
997 * updates by other widget events
998 */
999 #define GB(TYPE, NAME) GTK_##TYPE(gtk_builder_get_object(preferences_builder, #NAME))
1000 priv->entryHomePageURL = GB(ENTRY, entryHomePageURL);
1001 priv->content_language = GB(LIST_STORE, liststore_content_language);
1002 priv->search_providers = GB(LIST_STORE, liststore_search_provider);
1003 priv->entryProxyHost = GB(ENTRY, entryProxyHost);
1004 priv->spinProxyPort = GB(SPIN_BUTTON, spinProxyPort);
1005 priv->entryProxyUser = GB(ENTRY, entryProxyUser);
1006 priv->entryProxyPassword = GB(ENTRY, entryProxyPassword);
1007 priv->entryProxyNoproxy = GB(ENTRY, entryProxyNoproxy);
1008 #undef GB
1009
1010 /* connect all signals ready to use */
1011 gtk_builder_connect_signals(preferences_builder, priv);
1012
1013 /* release builder as were done with it */
1014 g_object_unref(G_OBJECT(preferences_builder));
1015
1016 /* mark dialog as transient on parent */
1017 gtk_window_set_transient_for(GTK_WINDOW(priv->dialog), parent);
1018
1019 return GTK_WIDGET(priv->dialog);
1020 }
1021
1022