1 /* X-Chat
2  * Copyright (C) 2004-2007 Peter Zelezny.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24 
25 #include "../common/hexchat.h"
26 #include "../common/cfgfiles.h"
27 #include "../common/fe.h"
28 #include "../common/text.h"
29 #include "../common/userlist.h"
30 #include "../common/util.h"
31 #include "../common/hexchatc.h"
32 #include "../common/outbound.h"
33 #include "fe-gtk.h"
34 #include "gtkutil.h"
35 #include "maingui.h"
36 #include "palette.h"
37 #include "pixmaps.h"
38 #include "menu.h"
39 #include "plugin-tray.h"
40 #include "notifications/notification-backend.h"
41 
42 #ifdef WIN32
43 #include "../common/fe.h"
44 #endif
45 #include "sexy-spell-entry.h"
46 
47 GtkStyle *create_input_style (GtkStyle *);
48 
49 #define LABEL_INDENT 12
50 
51 static int last_selected_page = 0;
52 static int last_selected_row = 0; /* sound row */
53 static gboolean color_change;
54 static struct hexchatprefs setup_prefs;
55 static GtkWidget *cancel_button;
56 static GtkWidget *font_dialog = NULL;
57 
58 enum
59 {
60 	ST_END,
61 	ST_TOGGLE,
62 	ST_TOGGLR,
63 	ST_3OGGLE,
64 	ST_ENTRY,
65 	ST_EFONT,
66 	ST_EFILE,
67 	ST_EFOLDER,
68 	ST_MENU,
69 	ST_RADIO,
70 	ST_NUMBER,
71 	ST_HSCALE,
72 	ST_HEADER,
73 	ST_LABEL,
74 	ST_ALERTHEAD
75 };
76 
77 typedef struct
78 {
79 	int type;
80 	char *label;
81 	int offset;
82 	char *tooltip;
83 	char const *const *list;
84 	int extra;
85 } setting;
86 
87 #ifdef WIN32
88 static const char *const langsmenu[] =
89 {
90 	N_("Afrikaans"),
91 	N_("Albanian"),
92 	N_("Amharic"),
93 	N_("Asturian"),
94 	N_("Azerbaijani"),
95 	N_("Basque"),
96 	N_("Belarusian"),
97 	N_("Bulgarian"),
98 	N_("Catalan"),
99 	N_("Chinese (Simplified)"),
100 	N_("Chinese (Traditional)"),
101 	N_("Czech"),
102 	N_("Danish"),
103 	N_("Dutch"),
104 	N_("English (British)"),
105 	N_("English"),
106 	N_("Estonian"),
107 	N_("Finnish"),
108 	N_("French"),
109 	N_("Galician"),
110 	N_("German"),
111 	N_("Greek"),
112 	N_("Gujarati"),
113 	N_("Hindi"),
114 	N_("Hungarian"),
115 	N_("Indonesian"),
116 	N_("Italian"),
117 	N_("Japanese"),
118 	N_("Kannada"),
119 	N_("Kinyarwanda"),
120 	N_("Korean"),
121 	N_("Latvian"),
122 	N_("Lithuanian"),
123 	N_("Macedonian"),
124 	N_("Malay"),
125 	N_("Malayalam"),
126 	N_("Norwegian (Bokmal)"),
127 	N_("Norwegian (Nynorsk)"),
128 	N_("Polish"),
129 	N_("Portuguese"),
130 	N_("Portuguese (Brazilian)"),
131 	N_("Punjabi"),
132 	N_("Russian"),
133 	N_("Serbian"),
134 	N_("Slovak"),
135 	N_("Slovenian"),
136 	N_("Spanish"),
137 	N_("Swedish"),
138 	N_("Thai"),
139 	N_("Turkish"),
140 	N_("Ukrainian"),
141 	N_("Vietnamese"),
142 	N_("Walloon"),
143 	NULL
144 };
145 #endif
146 
147 static const setting appearance_settings[] =
148 {
149 	{ST_HEADER,	N_("General"),0,0,0},
150 #ifdef WIN32
151 	{ST_MENU,   N_("Language:"), P_OFFINTNL(hex_gui_lang), 0, langsmenu, 0},
152 	{ST_EFONT,  N_("Main font:"), P_OFFSETNL(hex_text_font_main), 0, 0, sizeof prefs.hex_text_font_main},
153 #else
154 	{ST_EFONT,  N_("Font:"), P_OFFSETNL(hex_text_font), 0, 0, sizeof prefs.hex_text_font},
155 #endif
156 
157 	{ST_HEADER,	N_("Text Box"),0,0,0},
158 	{ST_TOGGLE, N_("Colored nick names"), P_OFFINTNL(hex_text_color_nicks), N_("Give each person on IRC a different color"),0,0},
159 	{ST_TOGGLR, N_("Indent nick names"), P_OFFINTNL(hex_text_indent), N_("Make nick names right-justified"),0,0},
160 	{ST_TOGGLE, N_ ("Show marker line"), P_OFFINTNL (hex_text_show_marker), N_ ("Insert a red line after the last read text."), 0, 0},
161 	{ST_EFILE, N_ ("Background image:"), P_OFFSETNL (hex_text_background), 0, 0, sizeof prefs.hex_text_background},
162 
163 	{ST_HEADER, N_("Transparency Settings"), 0,0,0},
164 	{ST_HSCALE, N_("Window opacity:"), P_OFFINTNL(hex_gui_transparency),0,0,0},
165 
166 	{ST_HEADER,	N_("Timestamps"),0,0,0},
167 	{ST_TOGGLE, N_("Enable timestamps"), P_OFFINTNL(hex_stamp_text),0,0,1},
168 	{ST_ENTRY,  N_("Timestamp format:"), P_OFFSETNL(hex_stamp_text_format),
169 #ifdef WIN32
170 					N_("See the strftime MSDN article for details."),0,sizeof prefs.hex_stamp_text_format},
171 #else
172 					N_("See the strftime manpage for details."),0,sizeof prefs.hex_stamp_text_format},
173 #endif
174 
175 	{ST_HEADER,	N_("Title Bar"),0,0,0},
176 	{ST_TOGGLE, N_("Show channel modes"), P_OFFINTNL(hex_gui_win_modes),0,0,0},
177 	{ST_TOGGLR, N_("Show number of users"), P_OFFINTNL(hex_gui_win_ucount),0,0,0},
178 
179 	{ST_END, 0, 0, 0, 0, 0}
180 };
181 
182 static const char *const tabcompmenu[] =
183 {
184 	N_("A-Z"),
185 	N_("Last-spoke order"),
186 	NULL
187 };
188 
189 static const setting inputbox_settings[] =
190 {
191 	{ST_HEADER, N_("Input Box"),0,0,0},
192 	{ST_TOGGLE, N_("Use the text box font and colors"), P_OFFINTNL(hex_gui_input_style),0,0,0},
193 	{ST_TOGGLE, N_("Render colors and attributes"), P_OFFINTNL (hex_gui_input_attr),0,0,0},
194 	{ST_TOGGLE, N_("Show nick box"), P_OFFINTNL(hex_gui_input_nick),0,0,1},
195 	{ST_TOGGLE, N_("Show user mode icon in nick box"), P_OFFINTNL(hex_gui_input_icon),0,0,0},
196 	{ST_TOGGLE, N_("Spell checking"), P_OFFINTNL(hex_gui_input_spell),0,0,1},
197 	{ST_ENTRY,	N_("Dictionaries to use:"), P_OFFSETNL(hex_text_spell_langs),0,0,sizeof prefs.hex_text_spell_langs},
198 #ifdef WIN32
199 	{ST_LABEL,	N_("Use language codes (as in \"%LOCALAPPDATA%\\enchant\\myspell\\dicts\").\nSeparate multiple entries with commas.")},
200 #else
201 	{ST_LABEL,	N_("Use language codes. Separate multiple entries with commas.")},
202 #endif
203 
204 	{ST_HEADER, N_("Nick Completion"),0,0,0},
205 	{ST_ENTRY,	N_("Nick completion suffix:"), P_OFFSETNL(hex_completion_suffix),0,0,sizeof prefs.hex_completion_suffix},
206 	{ST_MENU,	N_("Nick completion sorted:"), P_OFFINTNL(hex_completion_sort), 0, tabcompmenu, 0},
207 	{ST_NUMBER,	N_("Nick completion amount:"), P_OFFINTNL(hex_completion_amount), N_("Threshold of nicks to start listing instead of completing"), (const char **)N_("nicks."), 1000},
208 
209 	{ST_END, 0, 0, 0, 0, 0}
210 };
211 
212 static const char *const lagmenutext[] =
213 {
214 	N_("Off"),
215 	N_("Graphical"),
216 	N_("Text"),
217 	N_("Both"),
218 	NULL
219 };
220 
221 static const char *const ulmenutext[] =
222 {
223 	N_("A-Z, ops first"),
224 	N_("A-Z"),
225 	N_("Z-A, ops last"),
226 	N_("Z-A"),
227 	N_("Unsorted"),
228 	NULL
229 };
230 
231 static const char *const cspos[] =
232 {
233 	N_("Left (upper)"),
234 	N_("Left (lower)"),
235 	N_("Right (upper)"),
236 	N_("Right (lower)"),
237 	N_("Top"),
238 	N_("Bottom"),
239 	N_("Hidden"),
240 	NULL
241 };
242 
243 static const char *const ulpos[] =
244 {
245 	N_("Left (upper)"),
246 	N_("Left (lower)"),
247 	N_("Right (upper)"),
248 	N_("Right (lower)"),
249 	NULL
250 };
251 
252 static const setting userlist_settings[] =
253 {
254 	{ST_HEADER,	N_("User List"),0,0,0},
255 	{ST_TOGGLE, N_("Show hostnames in user list"), P_OFFINTNL(hex_gui_ulist_show_hosts), 0, 0, 0},
256 	{ST_TOGGLE, N_("Use the Text box font and colors"), P_OFFINTNL(hex_gui_ulist_style),0,0,0},
257 	{ST_TOGGLE, N_("Show icons for user modes"), P_OFFINTNL(hex_gui_ulist_icons), N_("Use graphical icons instead of text symbols in the user list."), 0, 0},
258 	{ST_TOGGLE, N_("Color nicknames in userlist"), P_OFFINTNL(hex_gui_ulist_color), N_("Will color nicknames the same as in chat."), 0, 0},
259 	{ST_TOGGLE, N_("Show user count in channels"), P_OFFINTNL(hex_gui_ulist_count), 0, 0, 0},
260 	{ST_MENU,	N_("User list sorted by:"), P_OFFINTNL(hex_gui_ulist_sort), 0, ulmenutext, 0},
261 	{ST_MENU,	N_("Show user list at:"), P_OFFINTNL(hex_gui_ulist_pos), 0, ulpos, 1},
262 
263 	{ST_HEADER,	N_("Away Tracking"),0,0,0},
264 	{ST_TOGGLE,	N_("Track the away status of users and mark them in a different color"), P_OFFINTNL(hex_away_track),0,0,1},
265 	{ST_NUMBER, N_("On channels smaller than:"), P_OFFINTNL(hex_away_size_max),0,0,10000},
266 
267 	{ST_HEADER,	N_("Action Upon Double Click"),0,0,0},
268 	{ST_ENTRY,	N_("Execute command:"), P_OFFSETNL(hex_gui_ulist_doubleclick), 0, 0, sizeof prefs.hex_gui_ulist_doubleclick},
269 
270 	{ST_HEADER,	N_("Extra Gadgets"),0,0,0},
271 	{ST_MENU,	N_("Lag meter:"), P_OFFINTNL(hex_gui_lagometer), 0, lagmenutext, 0},
272 	{ST_MENU,	N_("Throttle meter:"), P_OFFINTNL(hex_gui_throttlemeter), 0, lagmenutext, 0},
273 
274 	{ST_END, 0, 0, 0, 0, 0}
275 };
276 
277 static const char *const tabwin[] =
278 {
279 	N_("Windows"),
280 	N_("Tabs"),
281 	NULL
282 };
283 
284 static const char *const focusnewtabsmenu[] =
285 {
286 	N_("Never"),
287 	N_("Always"),
288 	N_("Only requested tabs"),
289 	NULL
290 };
291 
292 static const char *const noticeposmenu[] =
293 {
294 	N_("Automatic"),
295 	N_("In an extra tab"),
296 	N_("In the front tab"),
297 	NULL
298 };
299 
300 static const char *const swtype[] =
301 {
302 	N_("Tabs"),	/* 0 tabs */
303 	"",			/* 1 reserved */
304 	N_("Tree"),	/* 2 tree */
305 	NULL
306 };
307 
308 static const setting tabs_settings[] =
309 {
310 	/*{ST_HEADER,	N_("Channel Switcher"),0,0,0},*/
311 	{ST_RADIO,  N_("Switcher type:"),P_OFFINTNL(hex_gui_tab_layout), 0, swtype, 0},
312 	{ST_TOGGLE, N_("Open an extra tab for server messages"), P_OFFINTNL(hex_gui_tab_server), 0, 0, 0},
313 	{ST_TOGGLE, N_("Open a new tab when you receive a private message"), P_OFFINTNL(hex_gui_autoopen_dialog), 0, 0, 0},
314 	{ST_TOGGLE, N_("Sort tabs in alphabetical order"), P_OFFINTNL(hex_gui_tab_sort), 0, 0, 0},
315 	{ST_TOGGLE, N_("Show icons in the channel tree"), P_OFFINTNL(hex_gui_tab_icons), 0, 0, 0},
316 	{ST_TOGGLE, N_("Show dotted lines in the channel tree"), P_OFFINTNL(hex_gui_tab_dots), 0, 0, 0},
317 	{ST_TOGGLE, N_("Scroll mouse-wheel to change tabs"), P_OFFINTNL (hex_gui_tab_scrollchans), 0, 0, 0},
318 	{ST_TOGGLE, N_("Middle click to close tab"), P_OFFINTNL(hex_gui_tab_middleclose), 0, 0, 0},
319 	{ST_TOGGLE, N_("Smaller text"), P_OFFINTNL(hex_gui_tab_small), 0, 0, 0},
320 	{ST_MENU,	N_("Focus new tabs:"), P_OFFINTNL(hex_gui_tab_newtofront), 0, focusnewtabsmenu, 0},
321 	{ST_MENU,	N_("Placement of notices:"), P_OFFINTNL(hex_irc_notice_pos), 0, noticeposmenu, 0},
322 	{ST_MENU,	N_("Show channel switcher at:"), P_OFFINTNL(hex_gui_tab_pos), 0, cspos, 1},
323 	{ST_NUMBER,	N_("Shorten tab labels to:"), P_OFFINTNL(hex_gui_tab_trunc), 0, (const char **)N_("letters."), 99},
324 
325 	{ST_HEADER,	N_("Tabs or Windows"),0,0,0},
326 	{ST_MENU,	N_("Open channels in:"), P_OFFINTNL(hex_gui_tab_chans), 0, tabwin, 0},
327 	{ST_MENU,	N_("Open dialogs in:"), P_OFFINTNL(hex_gui_tab_dialogs), 0, tabwin, 0},
328 	{ST_MENU,	N_("Open utilities in:"), P_OFFINTNL(hex_gui_tab_utils), N_("Open DCC, Ignore, Notify etc, in tabs or windows?"), tabwin, 0},
329 
330 	{ST_END, 0, 0, 0, 0, 0}
331 };
332 
333 static const setting color_settings[] =
334 {
335 	{ST_TOGGLE, N_("Messages"), P_OFFINTNL(hex_text_stripcolor_msg), 0, 0, 0},
336 	{ST_TOGGLE, N_("Scrollback"), P_OFFINTNL(hex_text_stripcolor_replay), 0, 0, 0},
337 	{ST_TOGGLE, N_("Topic"), P_OFFINTNL(hex_text_stripcolor_topic), 0, 0, 0},
338 
339 	{ST_END, 0, 0, 0, 0, 0}
340 };
341 
342 static const char *const dccaccept[] =
343 {
344 	N_("Ask for confirmation"),
345 	N_("Ask for download folder"),
346 	N_("Save without interaction"),
347 	NULL
348 };
349 
350 static const setting filexfer_settings[] =
351 {
352 	{ST_HEADER, N_("Files and Directories"), 0, 0, 0},
353 	{ST_MENU,	N_("Auto accept file offers:"), P_OFFINTNL(hex_dcc_auto_recv), 0, dccaccept, 0},
354 	{ST_EFOLDER,N_("Download files to:"), P_OFFSETNL(hex_dcc_dir), 0, 0, sizeof prefs.hex_dcc_dir},
355 	{ST_EFOLDER,N_("Move completed files to:"), P_OFFSETNL(hex_dcc_completed_dir), 0, 0, sizeof prefs.hex_dcc_completed_dir},
356 	{ST_TOGGLE, N_("Save nick name in filenames"), P_OFFINTNL(hex_dcc_save_nick), 0, 0, 0},
357 
358 	{ST_HEADER,	N_("Auto Open DCC Windows"),0,0,0},
359 	{ST_TOGGLE, N_("Send window"), P_OFFINTNL(hex_gui_autoopen_send), 0, 0, 0},
360 	{ST_TOGGLE, N_("Receive window"), P_OFFINTNL(hex_gui_autoopen_recv), 0, 0, 0},
361 	{ST_TOGGLE, N_("Chat window"), P_OFFINTNL(hex_gui_autoopen_chat), 0, 0, 0},
362 
363 	{ST_HEADER, N_("Maximum File Transfer Speeds (Byte per Second)"), 0, 0, 0},
364 	{ST_NUMBER,	N_("One upload:"), P_OFFINTNL(hex_dcc_max_send_cps),
365 					N_("Maximum speed for one transfer"), 0, 10000000},
366 	{ST_NUMBER,	N_("One download:"), P_OFFINTNL(hex_dcc_max_get_cps),
367 					N_("Maximum speed for one transfer"), 0, 10000000},
368 	{ST_NUMBER,	N_("All uploads combined:"), P_OFFINTNL(hex_dcc_global_max_send_cps),
369 					N_("Maximum speed for all files"), 0, 10000000},
370 	{ST_NUMBER,	N_("All downloads combined:"), P_OFFINTNL(hex_dcc_global_max_get_cps),
371 					N_("Maximum speed for all files"), 0, 10000000},
372 
373 	{ST_END, 0, 0, 0, 0, 0}
374 };
375 
376 static const int balloonlist[3] =
377 {
378 	P_OFFINTNL(hex_input_balloon_chans), P_OFFINTNL(hex_input_balloon_priv), P_OFFINTNL(hex_input_balloon_hilight)
379 };
380 
381 static const int trayblinklist[3] =
382 {
383 	P_OFFINTNL(hex_input_tray_chans), P_OFFINTNL(hex_input_tray_priv), P_OFFINTNL(hex_input_tray_hilight)
384 };
385 
386 static const int taskbarlist[3] =
387 {
388 	P_OFFINTNL(hex_input_flash_chans), P_OFFINTNL(hex_input_flash_priv), P_OFFINTNL(hex_input_flash_hilight)
389 };
390 
391 static const int beeplist[3] =
392 {
393 	P_OFFINTNL(hex_input_beep_chans), P_OFFINTNL(hex_input_beep_priv), P_OFFINTNL(hex_input_beep_hilight)
394 };
395 
396 static const setting alert_settings[] =
397 {
398 	{ST_HEADER,	N_("Alerts"),0,0,0},
399 
400 	{ST_ALERTHEAD},
401 
402 
403 	{ST_3OGGLE, N_("Show notifications on:"), 0, 0, (void *)balloonlist, 0},
404 	{ST_3OGGLE, N_("Blink tray icon on:"), 0, 0, (void *)trayblinklist, 0},
405 	{ST_3OGGLE, N_("Blink task bar on:"), 0, 0, (void *)taskbarlist, 0},
406 #ifdef WIN32
407 	{ST_3OGGLE, N_("Make a beep sound on:"), 0, N_("Play the \"Instant Message Notification\" system sound upon the selected events"), (void *)beeplist, 0},
408 #else
409 #ifdef USE_LIBCANBERRA
410 	{ST_3OGGLE, N_("Make a beep sound on:"), 0, N_("Play \"message-new-instant\" from the freedesktop.org sound theme upon the selected events"), (void *)beeplist, 0},
411 #else
412 	{ST_3OGGLE, N_("Make a beep sound on:"), 0, N_("Play a GTK beep upon the selected events"), (void *)beeplist, 0},
413 #endif
414 #endif
415 
416 	{ST_TOGGLE,	N_("Omit alerts when marked as being away"), P_OFFINTNL(hex_away_omit_alerts), 0, 0, 0},
417 	{ST_TOGGLE,	N_("Omit alerts while the window is focused"), P_OFFINTNL(hex_gui_focus_omitalerts), 0, 0, 0},
418 
419 	{ST_HEADER,	N_("Tray Behavior"), 0, 0, 0},
420 	{ST_TOGGLE,	N_("Enable system tray icon"), P_OFFINTNL(hex_gui_tray), 0, 0, 4},
421 	{ST_TOGGLE,	N_("Minimize to tray"), P_OFFINTNL(hex_gui_tray_minimize), 0, 0, 0},
422 	{ST_TOGGLE,	N_("Close to tray"), P_OFFINTNL(hex_gui_tray_close), 0, 0, 0},
423 	{ST_TOGGLE,	N_("Automatically mark away/back"), P_OFFINTNL(hex_gui_tray_away), N_("Automatically change status when hiding to tray."), 0, 0},
424 	{ST_TOGGLE,	N_("Only show notifications when hidden or iconified"), P_OFFINTNL(hex_gui_tray_quiet), 0, 0, 0},
425 
426 	{ST_HEADER,	N_("Highlighted Messages"),0,0,0},
427 	{ST_LABEL,	N_("Highlighted messages are ones where your nickname is mentioned, but also:"), 0, 0, 0, 1},
428 
429 	{ST_ENTRY,	N_("Extra words to highlight:"), P_OFFSETNL(hex_irc_extra_hilight), 0, 0, sizeof prefs.hex_irc_extra_hilight},
430 	{ST_ENTRY,	N_("Nick names not to highlight:"), P_OFFSETNL(hex_irc_no_hilight), 0, 0, sizeof prefs.hex_irc_no_hilight},
431 	{ST_ENTRY,	N_("Nick names to always highlight:"), P_OFFSETNL(hex_irc_nick_hilight), 0, 0, sizeof prefs.hex_irc_nick_hilight},
432 	{ST_LABEL,	N_("Separate multiple words with commas.\nWildcards are accepted.")},
433 
434 	{ST_END, 0, 0, 0, 0, 0}
435 };
436 
437 static const setting alert_settings_nonotifications[] =
438 {
439 	{ST_HEADER,	N_("Alerts"),0,0,0},
440 
441 	{ST_ALERTHEAD},
442 	{ST_3OGGLE, N_("Blink tray icon on:"), 0, 0, (void *)trayblinklist, 0},
443 #ifdef HAVE_GTK_MAC
444 	{ST_3OGGLE, N_("Bounce dock icon on:"), 0, 0, (void *)taskbarlist, 0},
445 #else
446 #ifndef __APPLE__
447 	{ST_3OGGLE, N_("Blink task bar on:"), 0, 0, (void *)taskbarlist, 0},
448 #endif
449 #endif
450 #ifdef WIN32
451 	{ST_3OGGLE, N_("Make a beep sound on:"), 0, N_("Play the \"Instant Message Notification\" system sound upon the selected events"), (void *)beeplist, 0},
452 #else
453 #ifdef USE_LIBCANBERRA
454 	{ST_3OGGLE, N_("Make a beep sound on:"), 0, N_("Play \"message-new-instant\" from the freedesktop.org sound theme upon the selected events"), (void *)beeplist, 0},
455 #else
456 	{ST_3OGGLE, N_("Make a beep sound on:"), 0, N_("Play a GTK beep upon the selected events"), (void *)beeplist, 0},
457 #endif
458 #endif
459 
460 	{ST_TOGGLE,	N_("Omit alerts when marked as being away"), P_OFFINTNL(hex_away_omit_alerts), 0, 0, 0},
461 	{ST_TOGGLE,	N_("Omit alerts while the window is focused"), P_OFFINTNL(hex_gui_focus_omitalerts), 0, 0, 0},
462 
463 	{ST_HEADER,	N_("Tray Behavior"), 0, 0, 0},
464 	{ST_TOGGLE,	N_("Enable system tray icon"), P_OFFINTNL(hex_gui_tray), 0, 0, 4},
465 	{ST_TOGGLE,	N_("Minimize to tray"), P_OFFINTNL(hex_gui_tray_minimize), 0, 0, 0},
466 	{ST_TOGGLE,	N_("Close to tray"), P_OFFINTNL(hex_gui_tray_close), 0, 0, 0},
467 	{ST_TOGGLE,	N_("Automatically mark away/back"), P_OFFINTNL(hex_gui_tray_away), N_("Automatically change status when hiding to tray."), 0, 0},
468 
469 	{ST_HEADER,	N_("Highlighted Messages"),0,0,0},
470 	{ST_LABEL,	N_("Highlighted messages are ones where your nickname is mentioned, but also:"), 0, 0, 0, 1},
471 
472 	{ST_ENTRY,	N_("Extra words to highlight:"), P_OFFSETNL(hex_irc_extra_hilight), 0, 0, sizeof prefs.hex_irc_extra_hilight},
473 	{ST_ENTRY,	N_("Nick names not to highlight:"), P_OFFSETNL(hex_irc_no_hilight), 0, 0, sizeof prefs.hex_irc_no_hilight},
474 	{ST_ENTRY,	N_("Nick names to always highlight:"), P_OFFSETNL(hex_irc_nick_hilight), 0, 0, sizeof prefs.hex_irc_nick_hilight},
475 	{ST_LABEL,	N_("Separate multiple words with commas.\nWildcards are accepted.")},
476 
477 	{ST_END, 0, 0, 0, 0, 0}
478 };
479 
480 static const setting alert_settings_unity[] =
481 {
482 	{ST_HEADER,	N_("Alerts"),0,0,0},
483 
484 	{ST_ALERTHEAD},
485 	{ST_3OGGLE, N_("Show notifications on:"), 0, 0, (void *)balloonlist, 0},
486 	{ST_3OGGLE, N_("Blink task bar on:"), 0, 0, (void *)taskbarlist, 0},
487 	{ST_3OGGLE, N_("Make a beep sound on:"), 0, 0, (void *)beeplist, 0},
488 
489 	{ST_TOGGLE,	N_("Omit alerts when marked as being away"), P_OFFINTNL(hex_away_omit_alerts), 0, 0, 0},
490 	{ST_TOGGLE,	N_("Omit alerts while the window is focused"), P_OFFINTNL(hex_gui_focus_omitalerts), 0, 0, 0},
491 
492 	{ST_HEADER,	N_("Highlighted Messages"),0,0,0},
493 	{ST_LABEL,	N_("Highlighted messages are ones where your nickname is mentioned, but also:"), 0, 0, 0, 1},
494 
495 	{ST_ENTRY,	N_("Extra words to highlight:"), P_OFFSETNL(hex_irc_extra_hilight), 0, 0, sizeof prefs.hex_irc_extra_hilight},
496 	{ST_ENTRY,	N_("Nick names not to highlight:"), P_OFFSETNL(hex_irc_no_hilight), 0, 0, sizeof prefs.hex_irc_no_hilight},
497 	{ST_ENTRY,	N_("Nick names to always highlight:"), P_OFFSETNL(hex_irc_nick_hilight), 0, 0, sizeof prefs.hex_irc_nick_hilight},
498 	{ST_LABEL,	N_("Separate multiple words with commas.\nWildcards are accepted.")},
499 
500 	{ST_END, 0, 0, 0, 0, 0}
501 };
502 
503 static const setting alert_settings_unityandnonotifications[] =
504 {
505 	{ST_HEADER, N_("Alerts"), 0, 0, 0},
506 
507 	{ST_ALERTHEAD},
508 	{ST_3OGGLE, N_("Blink task bar on:"), 0, 0, (void *)taskbarlist, 0},
509 	{ST_3OGGLE, N_("Make a beep sound on:"), 0, 0, (void *)beeplist, 0},
510 
511 	{ST_TOGGLE, N_("Omit alerts when marked as being away"), P_OFFINTNL (hex_away_omit_alerts), 0, 0, 0},
512 	{ST_TOGGLE, N_("Omit alerts while the window is focused"), P_OFFINTNL (hex_gui_focus_omitalerts), 0, 0, 0},
513 
514 	{ST_HEADER, N_("Highlighted Messages"), 0, 0, 0},
515 	{ST_LABEL, N_("Highlighted messages are ones where your nickname is mentioned, but also:"), 0, 0, 0, 1},
516 
517 	{ST_ENTRY, N_("Extra words to highlight:"), P_OFFSETNL (hex_irc_extra_hilight), 0, 0, sizeof prefs.hex_irc_extra_hilight},
518 	{ST_ENTRY, N_("Nick names not to highlight:"), P_OFFSETNL (hex_irc_no_hilight), 0, 0, sizeof prefs.hex_irc_no_hilight},
519 	{ST_ENTRY, N_("Nick names to always highlight:"), P_OFFSETNL (hex_irc_nick_hilight), 0, 0, sizeof prefs.hex_irc_nick_hilight},
520 	{ST_LABEL, N_("Separate multiple words with commas.\nWildcards are accepted.")},
521 
522 	{ST_END, 0, 0, 0, 0, 0}
523 };
524 
525 static const setting general_settings[] =
526 {
527 	{ST_HEADER,	N_("Default Messages"),0,0,0},
528 	{ST_ENTRY,	N_("Quit:"), P_OFFSETNL(hex_irc_quit_reason), 0, 0, sizeof prefs.hex_irc_quit_reason},
529 	{ST_ENTRY,	N_("Leave channel:"), P_OFFSETNL(hex_irc_part_reason), 0, 0, sizeof prefs.hex_irc_part_reason},
530 	{ST_ENTRY,	N_("Away:"), P_OFFSETNL(hex_away_reason), 0, 0, sizeof prefs.hex_away_reason},
531 
532 	{ST_HEADER,	N_("Away"),0,0,0},
533 	{ST_TOGGLE,	N_("Show away once"), P_OFFINTNL(hex_away_show_once), N_("Show identical away messages only once."), 0, 0},
534 	{ST_TOGGLE,	N_("Automatically unmark away"), P_OFFINTNL(hex_away_auto_unmark), N_("Unmark yourself as away before sending messages."), 0, 0},
535 
536 	{ST_HEADER,	N_("Miscellaneous"),0,0,0},
537 	{ST_TOGGLE,	N_("Display MODEs in raw form"), P_OFFINTNL(hex_irc_raw_modes), 0, 0, 0},
538 	{ST_TOGGLE,	N_("WHOIS on notify"), P_OFFINTNL(hex_notify_whois_online), N_("Sends a /WHOIS when a user comes online in your notify list."), 0, 0},
539 	{ST_TOGGLE,	N_("Hide join and part messages"), P_OFFINTNL(hex_irc_conf_mode), N_("Hide channel join/part messages by default."), 0, 0},
540 	{ST_TOGGLE,	N_("Hide nick change messages"), P_OFFINTNL(hex_irc_hide_nickchange), 0, 0, 0},
541 
542 	{ST_END, 0, 0, 0, 0, 0}
543 };
544 
545 static const char *const bantypemenu[] =
546 {
547 	N_("*!*@*.host"),
548 	N_("*!*@domain"),
549 	N_("*!*user@*.host"),
550 	N_("*!*user@domain"),
551 	NULL
552 };
553 
554 static const setting advanced_settings[] =
555 {
556 	{ST_HEADER,	N_("Auto Copy Behavior"),0,0,0},
557 	{ST_TOGGLE, N_("Automatically copy selected text"), P_OFFINTNL(hex_text_autocopy_text),
558 					N_("Copy selected text to clipboard when left mouse button is released. "
559 						"Otherwise, Ctrl+Shift+C will copy the "
560 						"selected text to the clipboard."), 0, 0},
561 	{ST_TOGGLE, N_("Automatically include timestamps"), P_OFFINTNL(hex_text_autocopy_stamp),
562 					N_("Automatically include timestamps in copied lines of text. Otherwise, "
563 						"include timestamps if the Shift key is held down while selecting."), 0, 0},
564 	{ST_TOGGLE, N_("Automatically include color information"), P_OFFINTNL(hex_text_autocopy_color),
565 					N_("Automatically include color information in copied lines of text.  "
566 						"Otherwise, include color information if the Ctrl key is held down "
567 						"while selecting."), 0, 0},
568 
569 	{ST_HEADER,	N_("Miscellaneous"), 0, 0, 0},
570 	{ST_ENTRY,  N_("Real name:"), P_OFFSETNL(hex_irc_real_name), 0, 0, sizeof prefs.hex_irc_real_name},
571 #ifdef WIN32
572 	{ST_ENTRY,  N_("Alternative fonts:"), P_OFFSETNL(hex_text_font_alternative), N_("Separate multiple entries with commas without spaces before or after."), 0, sizeof prefs.hex_text_font_alternative},
573 #endif
574 	{ST_TOGGLE,	N_("Display lists in compact mode"), P_OFFINTNL(hex_gui_compact), N_("Use less spacing between user list/channel tree rows."), 0, 0},
575 	{ST_TOGGLE,	N_("Use server time if supported"), P_OFFINTNL(hex_irc_cap_server_time), N_("Display timestamps obtained from server if it supports the time-server extension."), 0, 0},
576 	{ST_TOGGLE,	N_("Automatically reconnect to servers on disconnect"), P_OFFINTNL(hex_net_auto_reconnect), 0, 0, 1},
577 	{ST_NUMBER,	N_("Auto reconnect delay:"), P_OFFINTNL(hex_net_reconnect_delay), 0, 0, 9999},
578 	{ST_NUMBER,	N_("Auto join delay:"), P_OFFINTNL(hex_irc_join_delay), 0, 0, 9999},
579 	{ST_MENU,	N_("Ban Type:"), P_OFFINTNL(hex_irc_ban_type), N_("Attempt to use this banmask when banning or quieting. (requires irc_who_join)"), bantypemenu, 0},
580 
581 	{ST_END, 0, 0, 0, 0, 0}
582 };
583 
584 static const setting logging_settings[] =
585 {
586 	{ST_HEADER,	N_("Logging"),0,0,0},
587 	{ST_TOGGLE,	N_("Display scrollback from previous session"), P_OFFINTNL(hex_text_replay), 0, 0, 0},
588 	{ST_NUMBER,	N_("Scrollback lines:"), P_OFFINTNL(hex_text_max_lines),0,0,100000},
589 	{ST_TOGGLE,	N_("Enable logging of conversations to disk"), P_OFFINTNL(hex_irc_logging), 0, 0, 0},
590 	{ST_ENTRY,	N_("Log filename:"), P_OFFSETNL(hex_irc_logmask), 0, 0, sizeof prefs.hex_irc_logmask},
591 	{ST_LABEL,	N_("%s=Server %c=Channel %n=Network.")},
592 
593 	{ST_HEADER,	N_("Timestamps"),0,0,0},
594 	{ST_TOGGLE,	N_("Insert timestamps in logs"), P_OFFINTNL(hex_stamp_log), 0, 0, 1},
595 	{ST_ENTRY,	N_("Log timestamp format:"), P_OFFSETNL(hex_stamp_log_format), 0, 0, sizeof prefs.hex_stamp_log_format},
596 #ifdef WIN32
597 	{ST_LABEL,	N_("See the strftime MSDN article for details.")},
598 #else
599 	{ST_LABEL,	N_("See the strftime manpage for details.")},
600 #endif
601 
602 	{ST_HEADER,	N_("URLs"),0,0,0},
603 	{ST_TOGGLE,	N_("Enable logging of URLs to disk"), P_OFFINTNL(hex_url_logging), 0, 0, 0},
604 	{ST_TOGGLE,	N_("Enable URL grabber"), P_OFFINTNL(hex_url_grabber), 0, 0, 1},
605 	{ST_NUMBER,	N_("Maximum number of URLs to grab:"), P_OFFINTNL(hex_url_grabber_limit), 0, 0, 9999},
606 
607 	{ST_END, 0, 0, 0, 0, 0}
608 };
609 
610 static const char *const proxytypes[] =
611 {
612 	N_("(Disabled)"),
613 	N_("Wingate"),
614 	N_("SOCKS4"),
615 	N_("SOCKS5"),
616 	N_("HTTP"),
617 	N_("Auto"),
618 	NULL
619 };
620 
621 static const char *const proxyuse[] =
622 {
623 	N_("All connections"),
624 	N_("IRC server only"),
625 	N_("DCC only"),
626 	NULL
627 };
628 
629 static const setting network_settings[] =
630 {
631 	{ST_HEADER,	N_("Your Address"), 0, 0, 0, 0},
632 	{ST_ENTRY,	N_("Bind to:"), P_OFFSETNL(hex_net_bind_host), 0, 0, sizeof prefs.hex_net_bind_host},
633 	{ST_LABEL,	N_("Only useful for computers with multiple addresses.")},
634 
635 	{ST_HEADER, N_("File Transfers"), 0, 0, 0},
636 	{ST_TOGGLE, N_("Get my address from the IRC server"), P_OFFINTNL(hex_dcc_ip_from_server),
637 					N_("Asks the IRC server for your real address. Use this if you have a 192.168.*.* address!"), 0, 0},
638 	{ST_ENTRY,	N_("DCC IP address:"), P_OFFSETNL(hex_dcc_ip),
639 					N_("Claim you are at this address when offering files."), 0, sizeof prefs.hex_dcc_ip},
640 	{ST_NUMBER,	N_("First DCC listen port:"), P_OFFINTNL(hex_dcc_port_first), 0, 0, 65535},
641 	{ST_NUMBER,	N_("Last DCC listen port:"), P_OFFINTNL(hex_dcc_port_last), 0,
642 		(const char **)N_("!Leave ports at zero for full range."), 65535},
643 
644 	{ST_HEADER,	N_("Proxy Server"), 0, 0, 0, 0},
645 	{ST_ENTRY,	N_("Hostname:"), P_OFFSETNL(hex_net_proxy_host), 0, 0, sizeof prefs.hex_net_proxy_host},
646 	{ST_NUMBER,	N_("Port:"), P_OFFINTNL(hex_net_proxy_port), 0, 0, 65535},
647 	{ST_MENU,	N_("Type:"), P_OFFINTNL(hex_net_proxy_type), 0, proxytypes, 0},
648 	{ST_MENU,	N_("Use proxy for:"), P_OFFINTNL(hex_net_proxy_use), 0, proxyuse, 0},
649 
650 	{ST_HEADER,	N_("Proxy Authentication"), 0, 0, 0, 0},
651 	{ST_TOGGLE,	N_("Use authentication (HTTP or SOCKS5 only)"), P_OFFINTNL(hex_net_proxy_auth), 0, 0, 0},
652 	{ST_ENTRY,	N_("Username:"), P_OFFSETNL(hex_net_proxy_user), 0, 0, sizeof prefs.hex_net_proxy_user},
653 	{ST_ENTRY,	N_("Password:"), P_OFFSETNL(hex_net_proxy_pass), 0, GINT_TO_POINTER(1), sizeof prefs.hex_net_proxy_pass},
654 
655 	{ST_END, 0, 0, 0, 0, 0}
656 };
657 
658 static const setting identd_settings[] =
659 {
660 	{ST_HEADER, N_("Identd Server"), 0, 0, 0, 0},
661 	{ST_TOGGLE, N_("Enabled"), P_OFFINTNL(hex_identd_server), N_("Server will respond with the networks username"), 0, 1},
662 	{ST_NUMBER,	N_("Port:"), P_OFFINTNL(hex_identd_port), N_("You must have permissions to listen on this port. "
663 												   "If not 113 (0 defaults to this) then you must configure port-forwarding."), 0, 65535},
664 
665 	{ST_END, 0, 0, 0, 0, 0}
666 };
667 
668 #define setup_get_str(pr,set) (((char *)pr)+set->offset)
669 #define setup_get_int(pr,set) *(((int *)pr)+set->offset)
670 #define setup_get_int3(pr,off) *(((int *)pr)+off)
671 
672 #define setup_set_int(pr,set,num) *((int *)pr+set->offset)=num
673 #define setup_set_str(pr,set,str) strcpy(((char *)pr)+set->offset,str)
674 
675 
676 static void
setup_3oggle_cb(GtkToggleButton * but,unsigned int * setting)677 setup_3oggle_cb (GtkToggleButton *but, unsigned int *setting)
678 {
679 	*setting = gtk_toggle_button_get_active (but);
680 }
681 
682 static void
setup_headlabel(GtkWidget * tab,int row,int col,char * text)683 setup_headlabel (GtkWidget *tab, int row, int col, char *text)
684 {
685 	GtkWidget *label;
686 	char buf[128];
687 	char *sp;
688 
689 	g_snprintf (buf, sizeof (buf), "<b><span size=\"smaller\">%s</span></b>", text);
690 	sp = strchr (buf + 17, ' ');
691 	if (sp)
692 		*sp = '\n';
693 
694 	label = gtk_label_new (NULL);
695 	gtk_label_set_markup (GTK_LABEL (label), buf);
696 	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
697 	gtk_table_attach (GTK_TABLE (tab), label, col, col + 1, row, row + 1, 0, 0, 4, 0);
698 }
699 
700 static void
setup_create_alert_header(GtkWidget * tab,int row,const setting * set)701 setup_create_alert_header (GtkWidget *tab, int row, const setting *set)
702 {
703 	setup_headlabel (tab, row, 3, _("Channel Message"));
704 	setup_headlabel (tab, row, 4, _("Private Message"));
705 	setup_headlabel (tab, row, 5, _("Highlighted Message"));
706 }
707 
708 /* makes 3 toggles side-by-side */
709 
710 static void
setup_create_3oggle(GtkWidget * tab,int row,const setting * set)711 setup_create_3oggle (GtkWidget *tab, int row, const setting *set)
712 {
713 	GtkWidget *label, *wid;
714 	int *offsets = (int *)set->list;
715 
716 	label = gtk_label_new (_(set->label));
717 	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
718 	if (set->tooltip)
719 	{
720 		gtk_widget_set_tooltip_text (label, _(set->tooltip));
721 	}
722 	gtk_table_attach (GTK_TABLE (tab), label, 2, 3, row, row + 1,
723 							GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, LABEL_INDENT, 0);
724 
725 	wid = gtk_check_button_new ();
726 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wid),
727 											setup_get_int3 (&setup_prefs, offsets[0]));
728 	g_signal_connect (G_OBJECT (wid), "toggled",
729 							G_CALLBACK (setup_3oggle_cb), ((int *)&setup_prefs) + offsets[0]);
730 	gtk_table_attach (GTK_TABLE (tab), wid, 3, 4, row, row + 1, 0, 0, 0, 0);
731 
732 	wid = gtk_check_button_new ();
733 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wid),
734 											setup_get_int3 (&setup_prefs, offsets[1]));
735 	g_signal_connect (G_OBJECT (wid), "toggled",
736 							G_CALLBACK (setup_3oggle_cb), ((int *)&setup_prefs) + offsets[1]);
737 	gtk_table_attach (GTK_TABLE (tab), wid, 4, 5, row, row + 1, 0, 0, 0, 0);
738 
739 	wid = gtk_check_button_new ();
740 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wid),
741 											setup_get_int3 (&setup_prefs, offsets[2]));
742 	g_signal_connect (G_OBJECT (wid), "toggled",
743 							G_CALLBACK (setup_3oggle_cb), ((int *)&setup_prefs) + offsets[2]);
744 	gtk_table_attach (GTK_TABLE (tab), wid, 5, 6, row, row + 1, 0, 0, 0, 0);
745 }
746 
747 static void
setup_toggle_cb(GtkToggleButton * but,const setting * set)748 setup_toggle_cb (GtkToggleButton *but, const setting *set)
749 {
750 	GtkWidget *label, *disable_wid;
751 
752 	setup_set_int (&setup_prefs, set, gtk_toggle_button_get_active (but));
753 
754 	/* does this toggle also enable/disable another widget? */
755 	disable_wid = g_object_get_data (G_OBJECT (but), "nxt");
756 	if (disable_wid)
757 	{
758 		gtk_widget_set_sensitive (disable_wid, gtk_toggle_button_get_active (but));
759 		label = g_object_get_data (G_OBJECT (disable_wid), "lbl");
760 		gtk_widget_set_sensitive (label, gtk_toggle_button_get_active (but));
761 	}
762 }
763 
764 static void
setup_toggle_sensitive_cb(GtkToggleButton * but,GtkWidget * wid)765 setup_toggle_sensitive_cb (GtkToggleButton *but, GtkWidget *wid)
766 {
767 	gtk_widget_set_sensitive (wid, gtk_toggle_button_get_active (but));
768 }
769 
770 static void
setup_create_toggleR(GtkWidget * tab,int row,const setting * set)771 setup_create_toggleR (GtkWidget *tab, int row, const setting *set)
772 {
773 	GtkWidget *wid;
774 
775 	wid = gtk_check_button_new_with_label (_(set->label));
776 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wid),
777 											setup_get_int (&setup_prefs, set));
778 	g_signal_connect (G_OBJECT (wid), "toggled",
779 							G_CALLBACK (setup_toggle_cb), (gpointer)set);
780 	if (set->tooltip)
781 		gtk_widget_set_tooltip_text (wid, _(set->tooltip));
782 	gtk_table_attach (GTK_TABLE (tab), wid, 4, 5, row, row + 1,
783 							GTK_EXPAND | GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
784 }
785 
786 static GtkWidget *
setup_create_toggleL(GtkWidget * tab,int row,const setting * set)787 setup_create_toggleL (GtkWidget *tab, int row, const setting *set)
788 {
789 	GtkWidget *wid;
790 
791 	wid = gtk_check_button_new_with_label (_(set->label));
792 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wid),
793 											setup_get_int (&setup_prefs, set));
794 	g_signal_connect (G_OBJECT (wid), "toggled",
795 							G_CALLBACK (setup_toggle_cb), (gpointer)set);
796 	if (set->tooltip)
797 		gtk_widget_set_tooltip_text (wid, _(set->tooltip));
798 	gtk_table_attach (GTK_TABLE (tab), wid, 2, row==6 ? 6 : 4, row, row + 1,
799 							GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, LABEL_INDENT, 0);
800 
801 	return wid;
802 }
803 
804 static GtkWidget *
setup_create_italic_label(char * text)805 setup_create_italic_label (char *text)
806 {
807 	GtkWidget *label;
808 	char buf[256];
809 
810 	label = gtk_label_new (NULL);
811 	g_snprintf (buf, sizeof (buf), "<i><span size=\"smaller\">%s</span></i>", text);
812 	gtk_label_set_markup (GTK_LABEL (label), buf);
813 	gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
814 
815 	return label;
816 }
817 
818 static void
setup_spin_cb(GtkSpinButton * spin,const setting * set)819 setup_spin_cb (GtkSpinButton *spin, const setting *set)
820 {
821 	setup_set_int (&setup_prefs, set, gtk_spin_button_get_value_as_int (spin));
822 }
823 
824 static GtkWidget *
setup_create_spin(GtkWidget * table,int row,const setting * set)825 setup_create_spin (GtkWidget *table, int row, const setting *set)
826 {
827 	GtkWidget *label, *wid, *rbox, *align;
828 	char *text;
829 
830 	label = gtk_label_new (_(set->label));
831 	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
832 	gtk_table_attach (GTK_TABLE (table), label, 2, 3, row, row + 1,
833 							GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, LABEL_INDENT, 0);
834 
835 	align = gtk_alignment_new (0.0, 0.5, 0.0, 0.0);
836 	gtk_table_attach (GTK_TABLE (table), align, 3, 4, row, row + 1,
837 							GTK_EXPAND | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
838 
839 	rbox = gtk_hbox_new (0, 0);
840 	gtk_container_add (GTK_CONTAINER (align), rbox);
841 
842 	wid = gtk_spin_button_new_with_range (0, set->extra, 1);
843 	g_object_set_data (G_OBJECT (wid), "lbl", label);
844 	if (set->tooltip)
845 		gtk_widget_set_tooltip_text (wid, _(set->tooltip));
846 	gtk_spin_button_set_value (GTK_SPIN_BUTTON (wid),
847 										setup_get_int (&setup_prefs, set));
848 	g_signal_connect (G_OBJECT (wid), "value_changed",
849 							G_CALLBACK (setup_spin_cb), (gpointer)set);
850 	gtk_box_pack_start (GTK_BOX (rbox), wid, 0, 0, 0);
851 
852 	if (set->list)
853 	{
854 		text = _((char *)set->list);
855 		if (text[0] == '!')
856 			label = setup_create_italic_label (text + 1);
857 		else
858 			label = gtk_label_new (text);
859 		gtk_box_pack_start (GTK_BOX (rbox), label, 0, 0, 6);
860 	}
861 
862 	return wid;
863 }
864 
865 static gint
setup_apply_trans(int * tag)866 setup_apply_trans (int *tag)
867 {
868 	prefs.hex_gui_transparency = setup_prefs.hex_gui_transparency;
869 	gtk_window_set_opacity (GTK_WINDOW (current_sess->gui->window),
870 							(prefs.hex_gui_transparency / 255.));
871 
872 	/* mg_update_xtext (current_sess->gui->xtext); */
873 	*tag = 0;
874 	return 0;
875 }
876 
877 static void
setup_hscale_cb(GtkHScale * wid,const setting * set)878 setup_hscale_cb (GtkHScale *wid, const setting *set)
879 {
880 	static int tag = 0;
881 
882 	setup_set_int (&setup_prefs, set, (int) gtk_range_get_value (GTK_RANGE (wid)));
883 
884 	if (tag == 0)
885 	{
886 		tag = g_idle_add ((GSourceFunc) setup_apply_trans, &tag);
887 	}
888 }
889 
890 static void
setup_create_hscale(GtkWidget * table,int row,const setting * set)891 setup_create_hscale (GtkWidget *table, int row, const setting *set)
892 {
893 	GtkWidget *wid;
894 
895 	wid = gtk_label_new (_(set->label));
896 	gtk_misc_set_alignment (GTK_MISC (wid), 0.0, 0.5);
897 	gtk_table_attach (GTK_TABLE (table), wid, 2, 3, row, row + 1,
898 							GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, LABEL_INDENT, 0);
899 
900 	wid = gtk_hscale_new_with_range (0., 255., 1.);
901 	gtk_scale_set_value_pos (GTK_SCALE (wid), GTK_POS_RIGHT);
902 	gtk_range_set_value (GTK_RANGE (wid), setup_get_int (&setup_prefs, set));
903 	g_signal_connect (G_OBJECT(wid), "value_changed",
904 							G_CALLBACK (setup_hscale_cb), (gpointer)set);
905 	gtk_table_attach (GTK_TABLE (table), wid, 3, 6, row, row + 1,
906 							GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
907 
908 #ifndef WIN32 /* Windows always supports this */
909 	/* Only used for transparency currently */
910 	if (!gtk_widget_is_composited (current_sess->gui->window))
911 		gtk_widget_set_sensitive (wid, FALSE);
912 #endif
913 }
914 
915 
916 static GtkWidget *proxy_user; 	/* username GtkEntry */
917 static GtkWidget *proxy_pass; 	/* password GtkEntry */
918 
919 static void
setup_menu_cb(GtkWidget * cbox,const setting * set)920 setup_menu_cb (GtkWidget *cbox, const setting *set)
921 {
922 	int n = gtk_combo_box_get_active (GTK_COMBO_BOX (cbox));
923 
924 	/* set the prefs.<field> */
925 	setup_set_int (&setup_prefs, set, n + set->extra);
926 
927 	if (set->list == proxytypes)
928 	{
929 		/* only HTTP and SOCKS5 can use a username/pass */
930 		gtk_widget_set_sensitive (proxy_user, (n == 3 || n == 4 || n == 5));
931 		gtk_widget_set_sensitive (proxy_pass, (n == 3 || n == 4 || n == 5));
932 	}
933 }
934 
935 static void
setup_radio_cb(GtkWidget * item,const setting * set)936 setup_radio_cb (GtkWidget *item, const setting *set)
937 {
938 	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (item)))
939 	{
940 		int n = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "n"));
941 		/* set the prefs.<field> */
942 		setup_set_int (&setup_prefs, set, n);
943 	}
944 }
945 
946 static int
setup_create_radio(GtkWidget * table,int row,const setting * set)947 setup_create_radio (GtkWidget *table, int row, const setting *set)
948 {
949 	GtkWidget *wid, *hbox;
950 	int i;
951 	const char **text = (const char **)set->list;
952 	GSList *group;
953 
954 	wid = gtk_label_new (_(set->label));
955 	gtk_misc_set_alignment (GTK_MISC (wid), 0.0, 0.5);
956 	gtk_table_attach (GTK_TABLE (table), wid, 2, 3, row, row + 1,
957 							GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, LABEL_INDENT, 0);
958 
959 	hbox = gtk_hbox_new (0, 0);
960 	gtk_table_attach (GTK_TABLE (table), hbox, 3, 4, row, row + 1,
961 							GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
962 
963 	i = 0;
964 	group = NULL;
965 	while (text[i])
966 	{
967 		if (text[i][0] != 0)
968 		{
969 			wid = gtk_radio_button_new_with_mnemonic (group, _(text[i]));
970 			/*if (set->tooltip)
971 				gtk_widget_set_tooltip_text (wid, _(set->tooltip));*/
972 			group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (wid));
973 			gtk_container_add (GTK_CONTAINER (hbox), wid);
974 			if (i == setup_get_int (&setup_prefs, set))
975 				gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wid), TRUE);
976 			g_object_set_data (G_OBJECT (wid), "n", GINT_TO_POINTER (i));
977 			g_signal_connect (G_OBJECT (wid), "toggled",
978 									G_CALLBACK (setup_radio_cb), (gpointer)set);
979 		}
980 		i++;
981 		row++;
982 	}
983 
984 	return i;
985 }
986 
987 /*
988 static const char *id_strings[] =
989 {
990 	"",
991 	"*",
992 	"%C4*%C18%B%B",
993 	"%U"
994 };
995 
996 static void
997 setup_id_menu_cb (GtkWidget *item, char *dest)
998 {
999 	int n = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "n"));
1000 
1001 	strcpy (dest, id_strings[n]);
1002 }
1003 
1004 static void
1005 setup_create_id_menu (GtkWidget *table, char *label, int row, char *dest)
1006 {
1007 	GtkWidget *wid, *menu, *item;
1008 	int i, def = 0;
1009 	static const char *text[] =
1010 	{
1011 		("(disabled)"),
1012 		("A star (*)"),
1013 		("A red star (*)"),
1014 		("Underlined")
1015 	};
1016 
1017 	wid = gtk_label_new (label);
1018 	gtk_misc_set_alignment (GTK_MISC (wid), 0.0, 0.5);
1019 	gtk_table_attach (GTK_TABLE (table), wid, 2, 3, row, row + 1,
1020 							GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, LABEL_INDENT, 0);
1021 
1022 	wid = gtk_option_menu_new ();
1023 	menu = gtk_menu_new ();
1024 
1025 	for (i = 0; i < 4; i++)
1026 	{
1027 		if (strcmp (id_strings[i], dest) == 0)
1028 		{
1029 			def = i;
1030 			break;
1031 		}
1032 	}
1033 
1034 	i = 0;
1035 	while (text[i])
1036 	{
1037 		item = gtk_menu_item_new_with_label (_(text[i]));
1038 		g_object_set_data (G_OBJECT (item), "n", GINT_TO_POINTER (i));
1039 
1040 		gtk_widget_show (item);
1041 		gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1042 		g_signal_connect (G_OBJECT (item), "activate",
1043 								G_CALLBACK (setup_id_menu_cb), dest);
1044 		i++;
1045 	}
1046 
1047 	gtk_option_menu_set_menu (GTK_OPTION_MENU (wid), menu);
1048 	gtk_option_menu_set_history (GTK_OPTION_MENU (wid), def);
1049 
1050 	gtk_table_attach (GTK_TABLE (table), wid, 3, 4, row, row + 1,
1051 							GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
1052 }
1053 
1054 */
1055 
1056 static void
setup_create_menu(GtkWidget * table,int row,const setting * set)1057 setup_create_menu (GtkWidget *table, int row, const setting *set)
1058 {
1059 	GtkWidget *wid, *cbox, *box;
1060 	const char **text = (const char **)set->list;
1061 	int i;
1062 
1063 	wid = gtk_label_new (_(set->label));
1064 	gtk_misc_set_alignment (GTK_MISC (wid), 0.0, 0.5);
1065 	gtk_table_attach (GTK_TABLE (table), wid, 2, 3, row, row + 1,
1066 							GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, LABEL_INDENT, 0);
1067 
1068 	cbox = gtk_combo_box_text_new ();
1069 
1070 	for (i = 0; text[i]; i++)
1071 		gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (cbox), _(text[i]));
1072 
1073 	gtk_combo_box_set_active (GTK_COMBO_BOX (cbox),
1074 									  setup_get_int (&setup_prefs, set) - set->extra);
1075 	g_signal_connect (G_OBJECT (cbox), "changed",
1076 							G_CALLBACK (setup_menu_cb), (gpointer)set);
1077 
1078 	box = gtk_hbox_new (0, 0);
1079 	gtk_box_pack_start (GTK_BOX (box), cbox, 0, 0, 0);
1080 	gtk_table_attach (GTK_TABLE (table), box, 3, 4, row, row + 1,
1081 							GTK_EXPAND | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
1082 }
1083 
1084 static void
setup_filereq_cb(GtkWidget * entry,char * file)1085 setup_filereq_cb (GtkWidget *entry, char *file)
1086 {
1087 	if (file)
1088 	{
1089 		if (file[0])
1090 			gtk_entry_set_text (GTK_ENTRY (entry), file);
1091 	}
1092 }
1093 
1094 static void
setup_browsefile_cb(GtkWidget * button,GtkWidget * entry)1095 setup_browsefile_cb (GtkWidget *button, GtkWidget *entry)
1096 {
1097 	/* used for background image only */
1098 	char *filter;
1099 	int filter_type;
1100 
1101 #ifdef WIN32
1102 	filter = "*png;*.tiff;*.gif;*.jpeg;*.jpg";
1103 	filter_type = FRF_EXTENSIONS;
1104 #else
1105 	filter = "image/*";
1106 	filter_type = FRF_MIMETYPES;
1107 #endif
1108 	gtkutil_file_req (_("Select an Image File"), setup_filereq_cb,
1109 					entry, NULL, filter, filter_type|FRF_RECENTLYUSED);
1110 }
1111 
1112 static void
setup_fontsel_destroy(GtkWidget * button,GtkFontSelectionDialog * dialog)1113 setup_fontsel_destroy (GtkWidget *button, GtkFontSelectionDialog *dialog)
1114 {
1115 	font_dialog = NULL;
1116 }
1117 
1118 static void
setup_fontsel_cb(GtkWidget * button,GtkFontSelectionDialog * dialog)1119 setup_fontsel_cb (GtkWidget *button, GtkFontSelectionDialog *dialog)
1120 {
1121 	GtkWidget *entry;
1122 	char *font_name;
1123 
1124 	entry = g_object_get_data (G_OBJECT (button), "e");
1125 	font_name = gtk_font_selection_dialog_get_font_name (dialog);
1126 
1127 	gtk_entry_set_text (GTK_ENTRY (entry), font_name);
1128 
1129 	g_free (font_name);
1130 	gtk_widget_destroy (GTK_WIDGET (dialog));
1131 	font_dialog = NULL;
1132 }
1133 
1134 static void
setup_fontsel_cancel(GtkWidget * button,GtkFontSelectionDialog * dialog)1135 setup_fontsel_cancel (GtkWidget *button, GtkFontSelectionDialog *dialog)
1136 {
1137 	gtk_widget_destroy (GTK_WIDGET (dialog));
1138 	font_dialog = NULL;
1139 }
1140 
1141 static void
setup_browsefolder_cb(GtkWidget * button,GtkEntry * entry)1142 setup_browsefolder_cb (GtkWidget *button, GtkEntry *entry)
1143 {
1144 	gtkutil_file_req (_("Select Download Folder"), setup_filereq_cb, entry, (char*)gtk_entry_get_text (entry), NULL, FRF_CHOOSEFOLDER);
1145 }
1146 
1147 static void
setup_browsefont_cb(GtkWidget * button,GtkWidget * entry)1148 setup_browsefont_cb (GtkWidget *button, GtkWidget *entry)
1149 {
1150 	GtkFontSelection *sel;
1151 	GtkFontSelectionDialog *dialog;
1152 	GtkWidget *ok_button;
1153 
1154 	dialog = (GtkFontSelectionDialog *) gtk_font_selection_dialog_new (_("Select font"));
1155 	font_dialog = (GtkWidget *)dialog;	/* global var */
1156 
1157 	sel = (GtkFontSelection *) gtk_font_selection_dialog_get_font_selection (dialog);
1158 
1159 	if (gtk_entry_get_text (GTK_ENTRY (entry))[0])
1160 		gtk_font_selection_set_font_name (sel, gtk_entry_get_text (GTK_ENTRY (entry)));
1161 
1162 	ok_button = gtk_font_selection_dialog_get_ok_button (dialog);
1163 	g_object_set_data (G_OBJECT (ok_button), "e", entry);
1164 
1165 	g_signal_connect (G_OBJECT (dialog), "destroy",
1166 							G_CALLBACK (setup_fontsel_destroy), dialog);
1167 	g_signal_connect (G_OBJECT (ok_button), "clicked",
1168 							G_CALLBACK (setup_fontsel_cb), dialog);
1169 	g_signal_connect (G_OBJECT (gtk_font_selection_dialog_get_cancel_button (dialog)), "clicked",
1170 							G_CALLBACK (setup_fontsel_cancel), dialog);
1171 
1172 	gtk_widget_show (GTK_WIDGET (dialog));
1173 }
1174 
1175 static void
setup_entry_cb(GtkEntry * entry,setting * set)1176 setup_entry_cb (GtkEntry *entry, setting *set)
1177 {
1178 	int size;
1179 	int pos;
1180 	unsigned char *p = (unsigned char*)gtk_entry_get_text (entry);
1181 	int len = strlen (p);
1182 
1183 	/* need to truncate? */
1184 	if (len >= set->extra)
1185 	{
1186 		len = pos = 0;
1187 		while (1)
1188 		{
1189 			size = g_utf8_skip [*p];
1190 			len += size;
1191 			p += size;
1192 			/* truncate to "set->extra" BYTES */
1193 			if (len >= set->extra)
1194 			{
1195 				gtk_editable_delete_text (GTK_EDITABLE (entry), pos, -1);
1196 				break;
1197 			}
1198 			pos++;
1199 		}
1200 	}
1201 	else
1202 	{
1203 		setup_set_str (&setup_prefs, set, gtk_entry_get_text (entry));
1204 	}
1205 }
1206 
1207 static void
setup_create_label(GtkWidget * table,int row,const setting * set)1208 setup_create_label (GtkWidget *table, int row, const setting *set)
1209 {
1210 	gtk_table_attach (GTK_TABLE (table), setup_create_italic_label (_(set->label)),
1211 							set->extra ? 1 : 3, 5, row, row + 1, GTK_FILL,
1212 							GTK_SHRINK | GTK_FILL, 0, 0);
1213 }
1214 
1215 static GtkWidget *
setup_create_entry(GtkWidget * table,int row,const setting * set)1216 setup_create_entry (GtkWidget *table, int row, const setting *set)
1217 {
1218 	GtkWidget *label;
1219 	GtkWidget *wid, *bwid;
1220 
1221 	label = gtk_label_new (_(set->label));
1222 	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
1223 	gtk_table_attach (GTK_TABLE (table), label, 2, 3, row, row + 1,
1224 							GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, LABEL_INDENT, 0);
1225 
1226 	wid = gtk_entry_new ();
1227 	g_object_set_data (G_OBJECT (wid), "lbl", label);
1228 	if (set->list)
1229 		gtk_entry_set_visibility (GTK_ENTRY (wid), FALSE);
1230 	if (set->tooltip)
1231 		gtk_widget_set_tooltip_text (wid, _(set->tooltip));
1232 	gtk_entry_set_max_length (GTK_ENTRY (wid), set->extra - 1);
1233 	gtk_entry_set_text (GTK_ENTRY (wid), setup_get_str (&setup_prefs, set));
1234 	g_signal_connect (G_OBJECT (wid), "changed",
1235 							G_CALLBACK (setup_entry_cb), (gpointer)set);
1236 
1237 	if (set->offset == P_OFFSETNL(hex_net_proxy_user))
1238 		proxy_user = wid;
1239 	if (set->offset == P_OFFSETNL(hex_net_proxy_pass))
1240 		proxy_pass = wid;
1241 
1242 	/* only http and Socks5 can auth */
1243 	if ( (set->offset == P_OFFSETNL(hex_net_proxy_pass) ||
1244 			set->offset == P_OFFSETNL(hex_net_proxy_user)) &&
1245 	     (setup_prefs.hex_net_proxy_type != 4 && setup_prefs.hex_net_proxy_type != 3 && setup_prefs.hex_net_proxy_type != 5) )
1246 		gtk_widget_set_sensitive (wid, FALSE);
1247 
1248 	if (set->type == ST_ENTRY)
1249 		gtk_table_attach (GTK_TABLE (table), wid, 3, 6, row, row + 1,
1250 								GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
1251 	else
1252 	{
1253 		gtk_table_attach (GTK_TABLE (table), wid, 3, 5, row, row + 1,
1254 								GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
1255 		bwid = gtk_button_new_with_label (_("Browse..."));
1256 		gtk_table_attach (GTK_TABLE (table), bwid, 5, 6, row, row + 1,
1257 								GTK_SHRINK | GTK_FILL, GTK_FILL, 0, 0);
1258 		if (set->type == ST_EFILE)
1259 			g_signal_connect (G_OBJECT (bwid), "clicked",
1260 									G_CALLBACK (setup_browsefile_cb), wid);
1261 		if (set->type == ST_EFONT)
1262 			g_signal_connect (G_OBJECT (bwid), "clicked",
1263 									G_CALLBACK (setup_browsefont_cb), wid);
1264 		if (set->type == ST_EFOLDER)
1265 			g_signal_connect (G_OBJECT (bwid), "clicked",
1266 									G_CALLBACK (setup_browsefolder_cb), wid);
1267 	}
1268 
1269 	return wid;
1270 }
1271 
1272 static void
setup_create_header(GtkWidget * table,int row,char * labeltext)1273 setup_create_header (GtkWidget *table, int row, char *labeltext)
1274 {
1275 	GtkWidget *label;
1276 	char buf[128];
1277 
1278 	if (row == 0)
1279 		g_snprintf (buf, sizeof (buf), "<b>%s</b>", _(labeltext));
1280 	else
1281 		g_snprintf (buf, sizeof (buf), "\n<b>%s</b>", _(labeltext));
1282 
1283 	label = gtk_label_new (NULL);
1284 	gtk_label_set_markup (GTK_LABEL (label), buf);
1285 	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
1286 	gtk_table_attach (GTK_TABLE (table), label, 0, 4, row, row + 1,
1287 							GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 5);
1288 }
1289 
1290 static void
setup_create_button(GtkWidget * table,int row,char * label,GCallback callback)1291 setup_create_button (GtkWidget *table, int row, char *label, GCallback callback)
1292 {
1293 	GtkWidget *but = gtk_button_new_with_label (label);
1294 	gtk_table_attach (GTK_TABLE (table), but, 2, 3, row, row + 1,
1295 					GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 5);
1296 	g_signal_connect (G_OBJECT (but), "clicked", callback, NULL);
1297 }
1298 
1299 static GtkWidget *
setup_create_frame(void)1300 setup_create_frame (void)
1301 {
1302 	GtkWidget *tab;
1303 
1304 	tab = gtk_table_new (3, 2, FALSE);
1305 	gtk_container_set_border_width (GTK_CONTAINER (tab), 6);
1306 	gtk_table_set_row_spacings (GTK_TABLE (tab), 2);
1307 	gtk_table_set_col_spacings (GTK_TABLE (tab), 3);
1308 
1309 	return tab;
1310 }
1311 
1312 static void
open_data_cb(GtkWidget * button,gpointer data)1313 open_data_cb (GtkWidget *button, gpointer data)
1314 {
1315 	fe_open_url (get_xdir ());
1316 }
1317 
1318 static GtkWidget *
setup_create_page(const setting * set)1319 setup_create_page (const setting *set)
1320 {
1321 	int i, row, do_disable;
1322 	GtkWidget *tab;
1323 	GtkWidget *wid = NULL, *parentwid = NULL;
1324 
1325 	tab = setup_create_frame ();
1326 	gtk_container_set_border_width (GTK_CONTAINER (tab), 6);
1327 
1328 	i = row = do_disable = 0;
1329 	while (set[i].type != ST_END)
1330 	{
1331 		switch (set[i].type)
1332 		{
1333 		case ST_HEADER:
1334 			setup_create_header (tab, row, set[i].label);
1335 			break;
1336 		case ST_EFONT:
1337 		case ST_ENTRY:
1338 		case ST_EFILE:
1339 		case ST_EFOLDER:
1340 			wid = setup_create_entry (tab, row, &set[i]);
1341 			break;
1342 		case ST_TOGGLR:
1343 			row--;
1344 			setup_create_toggleR (tab, row, &set[i]);
1345 			break;
1346 		case ST_TOGGLE:
1347 			wid = setup_create_toggleL (tab, row, &set[i]);
1348 			if (set[i].extra)
1349 				do_disable = set[i].extra;
1350 			break;
1351 		case ST_3OGGLE:
1352 			setup_create_3oggle (tab, row, &set[i]);
1353 			break;
1354 		case ST_MENU:
1355 			setup_create_menu (tab, row, &set[i]);
1356 			break;
1357 		case ST_RADIO:
1358 			row += setup_create_radio (tab, row, &set[i]);
1359 			break;
1360 		case ST_NUMBER:
1361 			wid = setup_create_spin (tab, row, &set[i]);
1362 			break;
1363 		case ST_HSCALE:
1364 			setup_create_hscale (tab, row, &set[i]);
1365 			break;
1366 		case ST_LABEL:
1367 			setup_create_label (tab, row, &set[i]);
1368 			break;
1369 		case ST_ALERTHEAD:
1370 			setup_create_alert_header (tab, row, &set[i]);
1371 		}
1372 
1373 		if (do_disable)
1374 		{
1375 			if (GTK_IS_WIDGET (parentwid))
1376 			{
1377 				g_signal_connect (G_OBJECT (parentwid), "toggled", G_CALLBACK(setup_toggle_sensitive_cb), wid);
1378 				gtk_widget_set_sensitive (wid, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (parentwid)));
1379 				do_disable--;
1380 				if (!do_disable)
1381 					parentwid = NULL;
1382 			}
1383 			else
1384 				parentwid = wid;
1385 		}
1386 
1387 		i++;
1388 		row++;
1389 	}
1390 
1391 	if (set == logging_settings)
1392 	{
1393 		setup_create_button (tab, row, _("Open Data Folder"), G_CALLBACK(open_data_cb));
1394 	}
1395 
1396 	return tab;
1397 }
1398 
1399 static void
setup_color_ok_cb(GtkWidget * button,GtkWidget * dialog)1400 setup_color_ok_cb (GtkWidget *button, GtkWidget *dialog)
1401 {
1402 	GtkColorSelectionDialog *cdialog = GTK_COLOR_SELECTION_DIALOG (dialog);
1403 	GdkColor *col;
1404 	GdkColor old_color;
1405 	GtkStyle *style;
1406 
1407 	col = g_object_get_data (G_OBJECT (button), "c");
1408 	old_color = *col;
1409 
1410 	button = g_object_get_data (G_OBJECT (button), "b");
1411 
1412 	if (!GTK_IS_WIDGET (button))
1413 	{
1414 		gtk_widget_destroy (dialog);
1415 		return;
1416 	}
1417 
1418 	color_change = TRUE;
1419 
1420 	gtk_color_selection_get_current_color (GTK_COLOR_SELECTION (gtk_color_selection_dialog_get_color_selection (cdialog)), col);
1421 
1422 	gdk_colormap_alloc_color (gtk_widget_get_colormap (button), col, TRUE, TRUE);
1423 
1424 	style = gtk_style_new ();
1425 	style->bg[0] = *col;
1426 	gtk_widget_set_style (button, style);
1427 	g_object_unref (style);
1428 
1429 	/* is this line correct?? */
1430 	gdk_colormap_free_colors (gtk_widget_get_colormap (button), &old_color, 1);
1431 
1432 	gtk_widget_destroy (dialog);
1433 }
1434 
1435 static void
setup_color_cb(GtkWidget * button,gpointer userdata)1436 setup_color_cb (GtkWidget *button, gpointer userdata)
1437 {
1438 	GtkWidget *dialog, *cancel_button, *ok_button, *help_button;
1439 	GtkColorSelectionDialog *cdialog;
1440 	GdkColor *color;
1441 
1442 	color = &colors[GPOINTER_TO_INT (userdata)];
1443 
1444 	dialog = gtk_color_selection_dialog_new (_("Select color"));
1445 	cdialog = GTK_COLOR_SELECTION_DIALOG (dialog);
1446 
1447 	g_object_get (G_OBJECT(cdialog), "cancel-button", &cancel_button,
1448 									"ok-button", &ok_button,
1449 									"help-button", &help_button, NULL);
1450 
1451 	gtk_widget_hide (help_button);
1452 	g_signal_connect (G_OBJECT (ok_button), "clicked",
1453 							G_CALLBACK (setup_color_ok_cb), dialog);
1454 	g_signal_connect (G_OBJECT (cancel_button), "clicked",
1455 							G_CALLBACK (gtkutil_destroy), dialog);
1456 	g_object_set_data (G_OBJECT (ok_button), "c", color);
1457 	g_object_set_data (G_OBJECT (ok_button), "b", button);
1458 	gtk_widget_set_sensitive (help_button, FALSE);
1459 	gtk_color_selection_set_current_color (GTK_COLOR_SELECTION (gtk_color_selection_dialog_get_color_selection (cdialog)), color);
1460 	gtk_widget_show (dialog);
1461 
1462 	g_object_unref (cancel_button);
1463 	g_object_unref (ok_button);
1464 	g_object_unref (help_button);
1465 }
1466 
1467 static void
setup_create_color_button(GtkWidget * table,int num,int row,int col)1468 setup_create_color_button (GtkWidget *table, int num, int row, int col)
1469 {
1470 	GtkWidget *but;
1471 	GtkStyle *style;
1472 	char buf[64];
1473 
1474 	if (num > 31)
1475 		strcpy (buf, "<span size=\"x-small\"> </span>");
1476 	else
1477 						/* 12345678901 23456789 01  23456789 */
1478 		sprintf (buf, "<span size=\"x-small\">%d</span>", num);
1479 	but = gtk_button_new_with_label (" ");
1480 	gtk_label_set_markup (GTK_LABEL (gtk_bin_get_child (GTK_BIN (but))), buf);
1481 	/* win32 build uses this to turn off themeing */
1482 	g_object_set_data (G_OBJECT (but), "hexchat-color", (gpointer)1);
1483 	gtk_table_attach (GTK_TABLE (table), but, col, col+1, row, row+1,
1484 							GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
1485 	g_signal_connect (G_OBJECT (but), "clicked",
1486 							G_CALLBACK (setup_color_cb), GINT_TO_POINTER (num));
1487 	style = gtk_style_new ();
1488 	style->bg[GTK_STATE_NORMAL] = colors[num];
1489 	gtk_widget_set_style (but, style);
1490 	g_object_unref (style);
1491 }
1492 
1493 static void
setup_create_other_colorR(char * text,int num,int row,GtkWidget * tab)1494 setup_create_other_colorR (char *text, int num, int row, GtkWidget *tab)
1495 {
1496 	GtkWidget *label;
1497 
1498 	label = gtk_label_new (text);
1499 	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
1500 	gtk_table_attach (GTK_TABLE (tab), label, 5, 9, row, row + 1,
1501 							GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, LABEL_INDENT, 0);
1502 	setup_create_color_button (tab, num, row, 9);
1503 }
1504 
1505 static void
setup_create_other_color(char * text,int num,int row,GtkWidget * tab)1506 setup_create_other_color (char *text, int num, int row, GtkWidget *tab)
1507 {
1508 	GtkWidget *label;
1509 
1510 	label = gtk_label_new (text);
1511 	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
1512 	gtk_table_attach (GTK_TABLE (tab), label, 2, 3, row, row + 1,
1513 							GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, LABEL_INDENT, 0);
1514 	setup_create_color_button (tab, num, row, 3);
1515 }
1516 
1517 static GtkWidget *
setup_create_color_page(void)1518 setup_create_color_page (void)
1519 {
1520 	GtkWidget *tab, *box, *label;
1521 	int i;
1522 
1523 	box = gtk_vbox_new (FALSE, 0);
1524 	gtk_container_set_border_width (GTK_CONTAINER (box), 6);
1525 
1526 	tab = gtk_table_new (9, 2, FALSE);
1527 	gtk_container_set_border_width (GTK_CONTAINER (tab), 6);
1528 	gtk_table_set_row_spacings (GTK_TABLE (tab), 2);
1529 	gtk_table_set_col_spacings (GTK_TABLE (tab), 3);
1530 	gtk_container_add (GTK_CONTAINER (box), tab);
1531 
1532 	setup_create_header (tab, 0, N_("Text Colors"));
1533 
1534 	label = gtk_label_new (_("mIRC colors:"));
1535 	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
1536 	gtk_table_attach (GTK_TABLE (tab), label, 2, 3, 1, 2,
1537 							GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, LABEL_INDENT, 0);
1538 
1539 	for (i = 0; i < 16; i++)
1540 		setup_create_color_button (tab, i, 1, i+3);
1541 
1542 	label = gtk_label_new (_("Local colors:"));
1543 	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
1544 	gtk_table_attach (GTK_TABLE (tab), label, 2, 3, 2, 3,
1545 							GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, LABEL_INDENT, 0);
1546 
1547 	for (i = 16; i < 32; i++)
1548 		setup_create_color_button (tab, i, 2, (i+3) - 16);
1549 
1550 	setup_create_other_color (_("Foreground:"), COL_FG, 3, tab);
1551 	setup_create_other_colorR (_("Background:"), COL_BG, 3, tab);
1552 
1553 	setup_create_header (tab, 5, N_("Selected Text"));
1554 
1555 	setup_create_other_color (_("Foreground:"), COL_MARK_FG, 6, tab);
1556 	setup_create_other_colorR (_("Background:"), COL_MARK_BG, 6, tab);
1557 
1558 	setup_create_header (tab, 8, N_("Interface Colors"));
1559 
1560 	setup_create_other_color (_("New data:"), COL_NEW_DATA, 9, tab);
1561 	setup_create_other_colorR (_("Marker line:"), COL_MARKER, 9, tab);
1562 	setup_create_other_color (_("New message:"), COL_NEW_MSG, 10, tab);
1563 	setup_create_other_colorR (_("Away user:"), COL_AWAY, 10, tab);
1564 	setup_create_other_color (_("Highlight:"), COL_HILIGHT, 11, tab);
1565 	setup_create_other_colorR (_("Spell checker:"), COL_SPELL, 11, tab);
1566 
1567 	setup_create_header (tab, 15, N_("Color Stripping"));
1568 
1569 	/* label = gtk_label_new (_("Strip colors from:"));
1570 	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
1571 	gtk_table_attach (GTK_TABLE (tab), label, 2, 3, 16, 17,
1572 							GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, LABEL_INDENT, 0); */
1573 
1574 	for (i = 0; i < 3; i++)
1575 	{
1576 		setup_create_toggleL (tab, i + 16, &color_settings[i]);
1577 	}
1578 
1579 	return box;
1580 }
1581 
1582 /* === GLOBALS for sound GUI === */
1583 
1584 static GtkWidget *sndfile_entry;
1585 static int ignore_changed = FALSE;
1586 
1587 extern struct text_event te[]; /* text.c */
1588 extern char *sound_files[];
1589 
1590 static void
setup_snd_populate(GtkTreeView * treeview)1591 setup_snd_populate (GtkTreeView * treeview)
1592 {
1593 	GtkListStore *store;
1594 	GtkTreeIter iter;
1595 	GtkTreeSelection *sel;
1596 	GtkTreePath *path;
1597 	int i;
1598 
1599 	sel = gtk_tree_view_get_selection (treeview);
1600 	store = (GtkListStore *)gtk_tree_view_get_model (treeview);
1601 
1602 	for (i = NUM_XP-1; i >= 0; i--)
1603 	{
1604 		gtk_list_store_prepend (store, &iter);
1605 		if (sound_files[i])
1606 			gtk_list_store_set (store, &iter, 0, te[i].name, 1, sound_files[i], 2, i, -1);
1607 		else
1608 			gtk_list_store_set (store, &iter, 0, te[i].name, 1, "", 2, i, -1);
1609 		if (i == last_selected_row)
1610 		{
1611 			gtk_tree_selection_select_iter (sel, &iter);
1612 			path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
1613 			if (path)
1614 			{
1615 				gtk_tree_view_scroll_to_cell (treeview, path, NULL, TRUE, 0.5, 0.5);
1616 				gtk_tree_view_set_cursor (treeview, path, NULL, FALSE);
1617 				gtk_tree_path_free (path);
1618 			}
1619 		}
1620 	}
1621 }
1622 
1623 static int
setup_snd_get_selected(GtkTreeSelection * sel,GtkTreeIter * iter)1624 setup_snd_get_selected (GtkTreeSelection *sel, GtkTreeIter *iter)
1625 {
1626 	int n;
1627 	GtkTreeModel *model;
1628 
1629 	if (!gtk_tree_selection_get_selected (sel, &model, iter))
1630 		return -1;
1631 
1632 	gtk_tree_model_get (model, iter, 2, &n, -1);
1633 	return n;
1634 }
1635 
1636 static void
setup_snd_row_cb(GtkTreeSelection * sel,gpointer user_data)1637 setup_snd_row_cb (GtkTreeSelection *sel, gpointer user_data)
1638 {
1639 	int n;
1640 	GtkTreeIter iter;
1641 
1642 	n = setup_snd_get_selected (sel, &iter);
1643 	if (n == -1)
1644 		return;
1645 	last_selected_row = n;
1646 
1647 	ignore_changed = TRUE;
1648 	if (sound_files[n])
1649 		gtk_entry_set_text (GTK_ENTRY (sndfile_entry), sound_files[n]);
1650 	else
1651 		gtk_entry_set_text (GTK_ENTRY (sndfile_entry), "");
1652 	ignore_changed = FALSE;
1653 }
1654 
1655 static void
setup_snd_add_columns(GtkTreeView * treeview)1656 setup_snd_add_columns (GtkTreeView * treeview)
1657 {
1658 	GtkCellRenderer *renderer;
1659 	GtkTreeModel *model;
1660 
1661 	/* event column */
1662 	renderer = gtk_cell_renderer_text_new ();
1663 	gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
1664 																-1, _("Event"), renderer,
1665 																"text", 0, NULL);
1666 
1667 	/* file column */
1668 	renderer = gtk_cell_renderer_text_new ();
1669 	gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
1670 																-1, _("Sound file"), renderer,
1671 																"text", 1, NULL);
1672 
1673 	model = GTK_TREE_MODEL (gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT));
1674 	gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), model);
1675 	g_object_unref (model);
1676 }
1677 
1678 static void
setup_snd_filereq_cb(GtkWidget * entry,char * file)1679 setup_snd_filereq_cb (GtkWidget *entry, char *file)
1680 {
1681 	if (file)
1682 	{
1683 		if (file[0])
1684 		{
1685 			/* Use just the filename if the given sound file is in the default <config>/sounds directory.
1686 			 * We're comparing absolute paths so this won't work in portable mode which uses a relative path.
1687 			 */
1688 			if (!strcmp (g_path_get_dirname (file), g_build_filename (get_xdir (), HEXCHAT_SOUND_DIR, NULL)))
1689 			{
1690 				gtk_entry_set_text (GTK_ENTRY (entry), g_path_get_basename (file));
1691 			}
1692 			else
1693 			{
1694 				gtk_entry_set_text (GTK_ENTRY (entry), file);
1695 			}
1696 		}
1697 	}
1698 }
1699 
1700 static void
setup_snd_browse_cb(GtkWidget * button,GtkEntry * entry)1701 setup_snd_browse_cb (GtkWidget *button, GtkEntry *entry)
1702 {
1703 	char *sounds_dir = g_build_filename (get_xdir (), HEXCHAT_SOUND_DIR, NULL);
1704 	char *filter = NULL;
1705 	int filter_type;
1706 #ifdef WIN32 /* win32 only supports wav, others could support anything */
1707 	filter = "*.wav";
1708 	filter_type = FRF_EXTENSIONS;
1709 #else
1710 	filter = "audio/*";
1711 	filter_type = FRF_MIMETYPES;
1712 #endif
1713 
1714 	gtkutil_file_req (_("Select a sound file"), setup_snd_filereq_cb, entry,
1715 						sounds_dir, filter, FRF_FILTERISINITIAL|filter_type);
1716 	g_free (sounds_dir);
1717 }
1718 
1719 static void
setup_snd_play_cb(GtkWidget * button,GtkEntry * entry)1720 setup_snd_play_cb (GtkWidget *button, GtkEntry *entry)
1721 {
1722 	sound_play (gtk_entry_get_text (entry), FALSE);
1723 }
1724 
1725 static void
setup_snd_changed_cb(GtkEntry * ent,GtkTreeView * tree)1726 setup_snd_changed_cb (GtkEntry *ent, GtkTreeView *tree)
1727 {
1728 	int n;
1729 	GtkTreeIter iter;
1730 	GtkListStore *store;
1731 	GtkTreeSelection *sel;
1732 
1733 	if (ignore_changed)
1734 		return;
1735 
1736 	sel = gtk_tree_view_get_selection (tree);
1737 	n = setup_snd_get_selected (sel, &iter);
1738 	if (n == -1)
1739 		return;
1740 
1741 	/* get the new sound file */
1742 	g_free (sound_files[n]);
1743 	sound_files[n] = g_strdup (gtk_entry_get_text (GTK_ENTRY (ent)));
1744 
1745 	/* update the TreeView list */
1746 	store = (GtkListStore *)gtk_tree_view_get_model (tree);
1747 	gtk_list_store_set (store, &iter, 1, sound_files[n], -1);
1748 
1749 	gtk_widget_set_sensitive (cancel_button, FALSE);
1750 }
1751 
1752 static GtkWidget *
setup_create_sound_page(void)1753 setup_create_sound_page (void)
1754 {
1755 	GtkWidget *vbox1;
1756 	GtkWidget *vbox2;
1757 	GtkWidget *scrolledwindow1;
1758 	GtkWidget *sound_tree;
1759 	GtkWidget *table1;
1760 	GtkWidget *sound_label;
1761 	GtkWidget *sound_browse;
1762 	GtkWidget *sound_play;
1763 	GtkTreeSelection *sel;
1764 
1765 	vbox1 = gtk_vbox_new (FALSE, 0);
1766 	gtk_container_set_border_width (GTK_CONTAINER (vbox1), 6);
1767 	gtk_widget_show (vbox1);
1768 
1769 	vbox2 = gtk_vbox_new (FALSE, 0);
1770 	gtk_widget_show (vbox2);
1771 	gtk_container_add (GTK_CONTAINER (vbox1), vbox2);
1772 
1773 	scrolledwindow1 = gtk_scrolled_window_new (NULL, NULL);
1774 	gtk_widget_show (scrolledwindow1);
1775 	gtk_container_add (GTK_CONTAINER (vbox2), scrolledwindow1);
1776 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow1),
1777 											  GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
1778 	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow1),
1779 													 GTK_SHADOW_IN);
1780 
1781 	sound_tree = gtk_tree_view_new ();
1782 	sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (sound_tree));
1783 	gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1784 	setup_snd_add_columns (GTK_TREE_VIEW (sound_tree));
1785 	setup_snd_populate (GTK_TREE_VIEW (sound_tree));
1786 	g_signal_connect (G_OBJECT (sel), "changed",
1787 							G_CALLBACK (setup_snd_row_cb), NULL);
1788 	gtk_widget_show (sound_tree);
1789 	gtk_container_add (GTK_CONTAINER (scrolledwindow1), sound_tree);
1790 	gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (sound_tree), TRUE);
1791 
1792 	table1 = gtk_table_new (2, 3, FALSE);
1793 	gtk_widget_show (table1);
1794 	gtk_box_pack_start (GTK_BOX (vbox2), table1, FALSE, TRUE, 8);
1795 	gtk_table_set_row_spacings (GTK_TABLE (table1), 2);
1796 	gtk_table_set_col_spacings (GTK_TABLE (table1), 4);
1797 
1798 	sound_label = gtk_label_new_with_mnemonic (_("Sound file:"));
1799 	gtk_widget_show (sound_label);
1800 	gtk_table_attach (GTK_TABLE (table1), sound_label, 0, 1, 0, 1,
1801 							(GtkAttachOptions) (GTK_FILL),
1802 							(GtkAttachOptions) (0), 0, 0);
1803 	gtk_misc_set_alignment (GTK_MISC (sound_label), 0, 0.5);
1804 
1805 	sndfile_entry = gtk_entry_new ();
1806 	g_signal_connect (G_OBJECT (sndfile_entry), "changed",
1807 							G_CALLBACK (setup_snd_changed_cb), sound_tree);
1808 	gtk_widget_show (sndfile_entry);
1809 	gtk_table_attach (GTK_TABLE (table1), sndfile_entry, 0, 1, 1, 2,
1810 							(GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1811 							(GtkAttachOptions) (0), 0, 0);
1812 
1813 	sound_browse = gtk_button_new_with_mnemonic (_("_Browse..."));
1814 	g_signal_connect (G_OBJECT (sound_browse), "clicked",
1815 							G_CALLBACK (setup_snd_browse_cb), sndfile_entry);
1816 	gtk_widget_show (sound_browse);
1817 	gtk_table_attach (GTK_TABLE (table1), sound_browse, 1, 2, 1, 2,
1818 							(GtkAttachOptions) (GTK_FILL),
1819 							(GtkAttachOptions) (0), 0, 0);
1820 
1821 #ifdef GTK_STOCK_MEDIA_PLAY
1822 	sound_play = gtk_button_new_from_stock (GTK_STOCK_MEDIA_PLAY);
1823 #else
1824 	sound_play = gtk_button_new_with_mnemonic (_("_Play"));
1825 #endif
1826 	g_signal_connect (G_OBJECT (sound_play), "clicked",
1827 							G_CALLBACK (setup_snd_play_cb), sndfile_entry);
1828 	gtk_widget_show (sound_play);
1829 	gtk_table_attach (GTK_TABLE (table1), sound_play, 2, 3, 1, 2,
1830 							(GtkAttachOptions) (GTK_FILL),
1831 							(GtkAttachOptions) (0), 0, 0);
1832 
1833 	setup_snd_row_cb (sel, NULL);
1834 
1835 	return vbox1;
1836 }
1837 
1838 static void
setup_add_page(const char * title,GtkWidget * book,GtkWidget * tab)1839 setup_add_page (const char *title, GtkWidget *book, GtkWidget *tab)
1840 {
1841 	GtkWidget *label, *vvbox, *viewport;
1842 	GtkScrolledWindow *sw;
1843 	char buf[128];
1844 
1845 	vvbox = gtk_vbox_new (FALSE, 0);
1846 
1847 	/* label */
1848 	label = gtk_label_new (NULL);
1849 	g_snprintf (buf, sizeof (buf), "<b><big>%s</big></b>", _(title));
1850 	gtk_label_set_markup (GTK_LABEL (label), buf);
1851 	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
1852 	gtk_misc_set_padding (GTK_MISC (label), 2, 1);
1853 	gtk_box_pack_start (GTK_BOX (vvbox), label, FALSE, FALSE, 2);
1854 
1855 	gtk_container_add (GTK_CONTAINER (vvbox), tab);
1856 
1857 	sw = GTK_SCROLLED_WINDOW(gtk_scrolled_window_new (NULL, NULL));
1858 	gtk_scrolled_window_set_shadow_type (sw, GTK_SHADOW_IN);
1859 	gtk_scrolled_window_set_policy (sw, GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1860 	gtk_scrolled_window_add_with_viewport (sw, vvbox);
1861 
1862 	viewport = gtk_bin_get_child (GTK_BIN(sw));
1863 	gtk_viewport_set_shadow_type (GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
1864 
1865 	gtk_notebook_append_page (GTK_NOTEBOOK (book), GTK_WIDGET(sw), NULL);
1866 }
1867 
1868 static const char *const cata[] =
1869 {
1870 	N_("Interface"),
1871 		N_("Appearance"),
1872 		N_("Input box"),
1873 		N_("User list"),
1874 		N_("Channel switcher"),
1875 		N_("Colors"),
1876 		NULL,
1877 	N_("Chatting"),
1878 		N_("General"),
1879 		N_("Alerts"),
1880 		N_("Sounds"),
1881 		N_("Logging"),
1882 		N_("Advanced"),
1883 		NULL,
1884 	N_("Network"),
1885 		N_("Network setup"),
1886 		N_("File transfers"),
1887 		N_("Identd"),
1888 		NULL,
1889 	NULL
1890 };
1891 
1892 static GtkWidget *
setup_create_pages(GtkWidget * box)1893 setup_create_pages (GtkWidget *box)
1894 {
1895 	GtkWidget *book;
1896 	GtkWindow *win = GTK_WINDOW(gtk_widget_get_toplevel (box));
1897 
1898 	book = gtk_notebook_new ();
1899 
1900 	setup_add_page (cata[1], book, setup_create_page (appearance_settings));
1901 	setup_add_page (cata[2], book, setup_create_page (inputbox_settings));
1902 	setup_add_page (cata[3], book, setup_create_page (userlist_settings));
1903 	setup_add_page (cata[4], book, setup_create_page (tabs_settings));
1904 	setup_add_page (cata[5], book, setup_create_color_page ());
1905 
1906 	setup_add_page (cata[8], book, setup_create_page (general_settings));
1907 
1908 	if (!gtkutil_tray_icon_supported (win) && !notification_backend_supported ())
1909 	{
1910 		setup_add_page (cata[9], book, setup_create_page (alert_settings_unityandnonotifications));
1911 	}
1912 	else if (!gtkutil_tray_icon_supported (win))
1913 	{
1914 		setup_add_page (cata[9], book, setup_create_page (alert_settings_unity));
1915 	}
1916 	else if (!notification_backend_supported ())
1917 	{
1918 		setup_add_page (cata[9], book, setup_create_page (alert_settings_nonotifications));
1919 	}
1920 	else
1921 	{
1922 		setup_add_page (cata[9], book, setup_create_page (alert_settings));
1923 	}
1924 
1925 	setup_add_page (cata[10], book, setup_create_sound_page ());
1926 	setup_add_page (cata[11], book, setup_create_page (logging_settings));
1927 	setup_add_page (cata[12], book, setup_create_page (advanced_settings));
1928 
1929 	setup_add_page (cata[15], book, setup_create_page (network_settings));
1930 	setup_add_page (cata[16], book, setup_create_page (filexfer_settings));
1931 	setup_add_page (cata[17], book, setup_create_page (identd_settings));
1932 
1933 	gtk_notebook_set_show_tabs (GTK_NOTEBOOK (book), FALSE);
1934 	gtk_notebook_set_show_border (GTK_NOTEBOOK (book), FALSE);
1935 	gtk_container_add (GTK_CONTAINER (box), book);
1936 
1937 	return book;
1938 }
1939 
1940 static void
setup_tree_cb(GtkTreeView * treeview,GtkWidget * book)1941 setup_tree_cb (GtkTreeView *treeview, GtkWidget *book)
1942 {
1943 	GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
1944 	GtkTreeIter iter;
1945 	GtkTreeModel *model;
1946 	int page;
1947 
1948 	if (gtk_tree_selection_get_selected (selection, &model, &iter))
1949 	{
1950 		gtk_tree_model_get (model, &iter, 1, &page, -1);
1951 		if (page != -1)
1952 		{
1953 			gtk_notebook_set_current_page (GTK_NOTEBOOK (book), page);
1954 			last_selected_page = page;
1955 		}
1956 	}
1957 }
1958 
1959 static gboolean
setup_tree_select_filter(GtkTreeSelection * selection,GtkTreeModel * model,GtkTreePath * path,gboolean path_selected,gpointer data)1960 setup_tree_select_filter (GtkTreeSelection *selection, GtkTreeModel *model,
1961 								  GtkTreePath *path, gboolean path_selected,
1962 								  gpointer data)
1963 {
1964 	if (gtk_tree_path_get_depth (path) > 1)
1965 		return TRUE;
1966 	return FALSE;
1967 }
1968 
1969 static void
setup_create_tree(GtkWidget * box,GtkWidget * book)1970 setup_create_tree (GtkWidget *box, GtkWidget *book)
1971 {
1972 	GtkWidget *tree;
1973 	GtkWidget *frame;
1974 	GtkTreeStore *model;
1975 	GtkTreeIter iter;
1976 	GtkTreeIter child_iter;
1977 	GtkTreeIter *sel_iter = NULL;
1978 	GtkCellRenderer *renderer;
1979 	GtkTreeSelection *sel;
1980 	int i, page;
1981 
1982 	model = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_INT);
1983 
1984 	i = 0;
1985 	page = 0;
1986 	do
1987 	{
1988 		gtk_tree_store_append (model, &iter, NULL);
1989 		gtk_tree_store_set (model, &iter, 0, _(cata[i]), 1, -1, -1);
1990 		i++;
1991 
1992 		do
1993 		{
1994 			gtk_tree_store_append (model, &child_iter, &iter);
1995 			gtk_tree_store_set (model, &child_iter, 0, _(cata[i]), 1, page, -1);
1996 			if (page == last_selected_page)
1997 				sel_iter = gtk_tree_iter_copy (&child_iter);
1998 			page++;
1999 			i++;
2000 		} while (cata[i]);
2001 
2002 		i++;
2003 
2004 	} while (cata[i]);
2005 
2006 	tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
2007 	g_object_unref (G_OBJECT (model));
2008 	sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree));
2009 	gtk_tree_selection_set_mode (sel, GTK_SELECTION_BROWSE);
2010 	gtk_tree_selection_set_select_function (sel, setup_tree_select_filter,
2011 														 NULL, NULL);
2012 	g_signal_connect (G_OBJECT (tree), "cursor_changed",
2013 							G_CALLBACK (setup_tree_cb), book);
2014 
2015 	renderer = gtk_cell_renderer_text_new ();
2016 	gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tree),
2017 							    -1, _("Categories"), renderer, "text", 0, NULL);
2018 	gtk_tree_view_expand_all (GTK_TREE_VIEW (tree));
2019 
2020 	frame = gtk_frame_new (NULL);
2021 	gtk_container_add (GTK_CONTAINER (frame), tree);
2022 	gtk_box_pack_start (GTK_BOX (box), frame, 0, 0, 0);
2023 	gtk_box_reorder_child (GTK_BOX (box), frame, 0);
2024 
2025 	if (sel_iter)
2026 	{
2027 		gtk_tree_selection_select_iter (sel, sel_iter);
2028 		gtk_tree_iter_free (sel_iter);
2029 	}
2030 }
2031 
2032 static void
setup_apply_entry_style(GtkWidget * entry)2033 setup_apply_entry_style (GtkWidget *entry)
2034 {
2035 	gtk_widget_modify_base (entry, GTK_STATE_NORMAL, &colors[COL_BG]);
2036 	gtk_widget_modify_text (entry, GTK_STATE_NORMAL, &colors[COL_FG]);
2037 	gtk_widget_modify_font (entry, input_style->font_desc);
2038 }
2039 
2040 static void
setup_apply_to_sess(session_gui * gui)2041 setup_apply_to_sess (session_gui *gui)
2042 {
2043 	mg_update_xtext (gui->xtext);
2044 
2045 	if (prefs.hex_gui_ulist_style)
2046 		gtk_widget_set_style (gui->user_tree, input_style);
2047 
2048 	if (prefs.hex_gui_input_style)
2049 	{
2050 		extern char cursor_color_rc[];
2051 		char buf[256];
2052 		sprintf (buf, cursor_color_rc,
2053 				(colors[COL_FG].red >> 8),
2054 				(colors[COL_FG].green >> 8),
2055 				(colors[COL_FG].blue >> 8));
2056 		gtk_rc_parse_string (buf);
2057 
2058 		setup_apply_entry_style (gui->input_box);
2059 		setup_apply_entry_style (gui->limit_entry);
2060 		setup_apply_entry_style (gui->key_entry);
2061 		setup_apply_entry_style (gui->topic_entry);
2062 	}
2063 
2064 	if (prefs.hex_gui_ulist_buttons)
2065 		gtk_widget_show (gui->button_box);
2066 	else
2067 		gtk_widget_hide (gui->button_box);
2068 
2069 	/* update active languages */
2070 	sexy_spell_entry_deactivate_language((SexySpellEntry *)gui->input_box,NULL);
2071 	sexy_spell_entry_activate_default_languages((SexySpellEntry *)gui->input_box);
2072 
2073 	sexy_spell_entry_set_checked ((SexySpellEntry *)gui->input_box, prefs.hex_gui_input_spell);
2074 	sexy_spell_entry_set_parse_attributes ((SexySpellEntry *)gui->input_box, prefs.hex_gui_input_attr);
2075 }
2076 
2077 static void
unslash(char * dir)2078 unslash (char *dir)
2079 {
2080 	if (dir[0])
2081 	{
2082 		int len = strlen (dir) - 1;
2083 #ifdef WIN32
2084 		if (dir[len] == '/' || dir[len] == '\\')
2085 #else
2086 		if (dir[len] == '/')
2087 #endif
2088 			dir[len] = 0;
2089 	}
2090 }
2091 
2092 void
setup_apply_real(int new_pix,int do_ulist,int do_layout,int do_identd)2093 setup_apply_real (int new_pix, int do_ulist, int do_layout, int do_identd)
2094 {
2095 	GSList *list;
2096 	session *sess;
2097 	int done_main = FALSE;
2098 
2099 	/* remove trailing slashes */
2100 	unslash (prefs.hex_dcc_dir);
2101 	unslash (prefs.hex_dcc_completed_dir);
2102 
2103 	g_mkdir (prefs.hex_dcc_dir, 0700);
2104 	g_mkdir (prefs.hex_dcc_completed_dir, 0700);
2105 
2106 	if (new_pix)
2107 	{
2108 		if (channelwin_pix)
2109 			g_object_unref (channelwin_pix);
2110 		channelwin_pix = pixmap_load_from_file (prefs.hex_text_background);
2111 	}
2112 
2113 	input_style = create_input_style (input_style);
2114 
2115 	list = sess_list;
2116 	while (list)
2117 	{
2118 		sess = list->data;
2119 		if (sess->gui->is_tab)
2120 		{
2121 			/* only apply to main tabwindow once */
2122 			if (!done_main)
2123 			{
2124 				done_main = TRUE;
2125 				setup_apply_to_sess (sess->gui);
2126 			}
2127 		} else
2128 		{
2129 			setup_apply_to_sess (sess->gui);
2130 		}
2131 
2132 		log_open_or_close (sess);
2133 
2134 		if (do_ulist)
2135 			userlist_rehash (sess);
2136 
2137 		list = list->next;
2138 	}
2139 
2140 	mg_apply_setup ();
2141 	tray_apply_setup ();
2142 	hexchat_reinit_timers ();
2143 
2144 	if (do_layout)
2145 		menu_change_layout ();
2146 
2147 	if (do_identd)
2148 		handle_command (current_sess, "IDENTD reload", FALSE);
2149 }
2150 
2151 static void
setup_apply(struct hexchatprefs * pr)2152 setup_apply (struct hexchatprefs *pr)
2153 {
2154 #ifdef WIN32
2155 	PangoFontDescription *old_desc;
2156 	PangoFontDescription *new_desc;
2157 	char buffer[4 * FONTNAMELEN + 1];
2158 #endif
2159 	int new_pix = FALSE;
2160 	int noapply = FALSE;
2161 	int do_ulist = FALSE;
2162 	int do_layout = FALSE;
2163 	int do_identd = FALSE;
2164 
2165 	if (strcmp (pr->hex_text_background, prefs.hex_text_background) != 0)
2166 		new_pix = TRUE;
2167 
2168 #define DIFF(a) (pr->a != prefs.a)
2169 
2170 #ifdef WIN32
2171 	if (DIFF (hex_gui_lang))
2172 		noapply = TRUE;
2173 #endif
2174 	if (DIFF (hex_gui_compact))
2175 		noapply = TRUE;
2176 	if (DIFF (hex_gui_input_icon))
2177 		noapply = TRUE;
2178 	if (DIFF (hex_gui_input_nick))
2179 		noapply = TRUE;
2180 	if (DIFF (hex_gui_lagometer))
2181 		noapply = TRUE;
2182 	if (DIFF (hex_gui_tab_icons))
2183 		noapply = TRUE;
2184 	if (DIFF (hex_gui_tab_server))
2185 		noapply = TRUE;
2186 	if (DIFF (hex_gui_tab_small))
2187 		noapply = TRUE;
2188 	if (DIFF (hex_gui_tab_sort))
2189 		noapply = TRUE;
2190 	if (DIFF (hex_gui_tab_trunc))
2191 		noapply = TRUE;
2192 	if (DIFF (hex_gui_throttlemeter))
2193 		noapply = TRUE;
2194 	if (DIFF (hex_gui_ulist_count))
2195 		noapply = TRUE;
2196 	if (DIFF (hex_gui_ulist_icons))
2197 		noapply = TRUE;
2198 	if (DIFF (hex_gui_ulist_show_hosts))
2199 		noapply = TRUE;
2200 	if (DIFF (hex_gui_ulist_style))
2201 		noapply = TRUE;
2202 	if (DIFF (hex_gui_ulist_sort))
2203 		noapply = TRUE;
2204 	if (DIFF (hex_gui_input_style) && prefs.hex_gui_input_style == TRUE)
2205 		noapply = TRUE; /* Requires restart to *disable* */
2206 
2207 	if (DIFF (hex_gui_tab_dots))
2208 		do_layout = TRUE;
2209 	if (DIFF (hex_gui_tab_layout))
2210 		do_layout = TRUE;
2211 
2212 	if (DIFF (hex_identd_server) || DIFF (hex_identd_port))
2213 		do_identd = TRUE;
2214 
2215 	if (color_change || (DIFF (hex_gui_ulist_color)) || (DIFF (hex_away_size_max)) || (DIFF (hex_away_track)))
2216 		do_ulist = TRUE;
2217 
2218 	if ((pr->hex_gui_tab_pos == 5 || pr->hex_gui_tab_pos == 6) &&
2219 		 pr->hex_gui_tab_layout == 2 && pr->hex_gui_tab_pos != prefs.hex_gui_tab_pos)
2220 		fe_message (_("You cannot place the tree on the top or bottom!\n"
2221 						"Please change to the <b>Tabs</b> layout in the <b>View</b>"
2222 						" menu first."),
2223 						FE_MSG_WARN | FE_MSG_MARKUP);
2224 
2225 	/* format cannot be blank, there is already a setting for this */
2226 	if (pr->hex_stamp_text_format[0] == 0)
2227 	{
2228 		pr->hex_stamp_text = 0;
2229 		strcpy (pr->hex_stamp_text_format, prefs.hex_stamp_text_format);
2230 	}
2231 
2232 	memcpy (&prefs, pr, sizeof (prefs));
2233 
2234 #ifdef WIN32
2235 	/* merge hex_font_main and hex_font_alternative into hex_font_normal */
2236 	old_desc = pango_font_description_from_string (prefs.hex_text_font_main);
2237 	sprintf (buffer, "%s,%s", pango_font_description_get_family (old_desc), prefs.hex_text_font_alternative);
2238 	new_desc = pango_font_description_from_string (buffer);
2239 	pango_font_description_set_weight (new_desc, pango_font_description_get_weight (old_desc));
2240 	pango_font_description_set_style (new_desc, pango_font_description_get_style (old_desc));
2241 	pango_font_description_set_size (new_desc, pango_font_description_get_size (old_desc));
2242 	sprintf (prefs.hex_text_font, "%s", pango_font_description_to_string (new_desc));
2243 
2244 	/* FIXME this is not required after pango_font_description_from_string()
2245 	g_free (old_desc);
2246 	g_free (new_desc);
2247 	*/
2248 #endif
2249 
2250 	if (prefs.hex_irc_real_name[0] == 0)
2251 	{
2252 		fe_message (_("The Real name option cannot be left blank. Falling back to \"realname\"."), FE_MSG_WARN);
2253 		strcpy (prefs.hex_irc_real_name, "realname");
2254 	}
2255 
2256 	setup_apply_real (new_pix, do_ulist, do_layout, do_identd);
2257 
2258 	if (noapply)
2259 		fe_message (_("Some settings were changed that require a"
2260 						" restart to take full effect."), FE_MSG_WARN);
2261 
2262 #ifndef WIN32
2263 	if (prefs.hex_dcc_auto_recv == 2) /* Auto */
2264 	{
2265 		if (!strcmp ((char *)g_get_home_dir (), prefs.hex_dcc_dir))
2266 		{
2267 			fe_message (_("*WARNING*\n"
2268 							 "Auto accepting DCC to your home directory\n"
2269 							 "can be dangerous and is exploitable. Eg:\n"
2270 							 "Someone could send you a .bash_profile"), FE_MSG_WARN);
2271 		}
2272 	}
2273 #endif
2274 }
2275 
2276 static void
setup_ok_cb(GtkWidget * but,GtkWidget * win)2277 setup_ok_cb (GtkWidget *but, GtkWidget *win)
2278 {
2279 	gtk_widget_destroy (win);
2280 	setup_apply (&setup_prefs);
2281 	save_config ();
2282 	palette_save ();
2283 }
2284 
2285 static GtkWidget *
setup_window_open(void)2286 setup_window_open (void)
2287 {
2288 	GtkWidget *wid, *win, *vbox, *hbox, *hbbox;
2289 	char buf[128];
2290 
2291 	g_snprintf(buf, sizeof(buf), _("Preferences - %s"), _(DISPLAY_NAME));
2292 	win = gtkutil_window_new (buf, "prefs", 0, 600, 2);
2293 
2294 	vbox = gtk_vbox_new (FALSE, 5);
2295 	gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
2296 	gtk_container_add (GTK_CONTAINER (win), vbox);
2297 
2298 	hbox = gtk_hbox_new (FALSE, 4);
2299 	gtk_container_add (GTK_CONTAINER (vbox), hbox);
2300 
2301 	setup_create_tree (hbox, setup_create_pages (hbox));
2302 
2303 	/* prepare the button box */
2304 	hbbox = gtk_hbutton_box_new ();
2305 	gtk_button_box_set_layout (GTK_BUTTON_BOX (hbbox), GTK_BUTTONBOX_END);
2306 	gtk_box_set_spacing (GTK_BOX (hbbox), 4);
2307 	gtk_box_pack_end (GTK_BOX (vbox), hbbox, FALSE, FALSE, 0);
2308 
2309 	cancel_button = wid = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
2310 	g_signal_connect (G_OBJECT (wid), "clicked",
2311 							G_CALLBACK (gtkutil_destroy), win);
2312 	gtk_box_pack_start (GTK_BOX (hbbox), wid, FALSE, FALSE, 0);
2313 
2314 	wid = gtk_button_new_from_stock (GTK_STOCK_OK);
2315 	g_signal_connect (G_OBJECT (wid), "clicked",
2316 							G_CALLBACK (setup_ok_cb), win);
2317 	gtk_box_pack_start (GTK_BOX (hbbox), wid, FALSE, FALSE, 0);
2318 
2319 	gtk_widget_show_all (win);
2320 
2321 	return win;
2322 }
2323 
2324 static void
setup_close_cb(GtkWidget * win,GtkWidget ** swin)2325 setup_close_cb (GtkWidget *win, GtkWidget **swin)
2326 {
2327 	*swin = NULL;
2328 
2329 	if (font_dialog)
2330 	{
2331 		gtk_widget_destroy (font_dialog);
2332 		font_dialog = NULL;
2333 	}
2334 }
2335 
2336 void
setup_open(void)2337 setup_open (void)
2338 {
2339 	static GtkWidget *setup_window = NULL;
2340 
2341 	if (setup_window)
2342 	{
2343 		gtk_window_present (GTK_WINDOW (setup_window));
2344 		return;
2345 	}
2346 
2347 	memcpy (&setup_prefs, &prefs, sizeof (prefs));
2348 
2349 	color_change = FALSE;
2350 	setup_window = setup_window_open ();
2351 
2352 	g_signal_connect (G_OBJECT (setup_window), "destroy",
2353 							G_CALLBACK (setup_close_cb), &setup_window);
2354 }
2355