1 /* config_dialog.c - GTK+ dialog for option files utilities
2  *
3  * Copyright (C) 2002 Patrice St-Gelais
4  * patrstg@users.sourceforge.net
5  *
6  * This program 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; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20 
21 #include <stdlib.h>
22 #include <string.h>
23 #include <math.h>
24 #include <glib.h>
25 #include "./x_alloc.h"
26 #include "./config_rc_file.h"
27 #include "./config_dialog.h"
28 #include "./utils.h"
29 #include "./filesel.h"
30 
31 #include "../icons/open.xpm"
32 
33 #define NB_CONFIG_EXT 2
34 omenu_list_type config_ext_list[NB_CONFIG_EXT] = {
35 	{ NULL,0,NULL,gettext_noop("<no extension>"),filetype_sel_callback_fn },
36 	{ NULL,0,NULL,gettext_noop("RC"),filetype_sel_callback_fn }
37 };
38 
39 #define NB_BLANK_EXT 1
40 omenu_list_type blank_ext_list[NB_BLANK_EXT] = {
41 	 { NULL,0,NULL,gettext_noop("<no extension>"),filetype_sel_callback_fn }
42 };
43 
44 static gchar *SEP="#";
45 
update_block_page(option_block_type * allowed_block,option_block_type * current_block)46 void update_block_page( option_block_type *allowed_block,
47 			option_block_type *current_block) {
48 	gint i,j, field_length;
49 	gchar *value;
50 	for (i=0; i<allowed_block->nb_options; i++) {
51 		// 1. Find value in current_opt
52 		// 2. Update Widget
53 		// 3. Reset the length of the widget when needed
54 
55 		for (j = 0; j<current_block->nb_options; j++) {
56 			if (!strcmp((current_block->option_list+j)->label,
57 				(allowed_block->option_list+i)->label))
58 				break;
59 		}
60 		if (j==current_block->nb_options) {  // Option not found
61 			value = (gchar *) x_malloc(1, "gchar (value in config file)");
62 			value[0] = '\0';
63 		}
64 		else
65 			value = 	(current_block->option_list+j)->value;
66 //	Required redundancy between allowed_options and current_options
67 		(allowed_block->option_list+i)->value = value;
68 		if (GTK_IS_ENTRY((allowed_block->option_list+i)->widget )) {
69 			field_length = MAX(15,3*strlen(value));
70 			gtk_entry_set_max_length(GTK_ENTRY((allowed_block->option_list+i)->widget ),
71 				field_length);
72 			gtk_widget_set_usize(GTK_WIDGET((allowed_block->option_list+i)->widget ),
73 				field_length*3,0);
74 			gtk_entry_set_text(GTK_ENTRY((allowed_block->option_list+i)->widget), value);
75 		}
76 
77 		else if (GTK_IS_ADJUSTMENT((allowed_block->option_list+i)->widget )) {
78 			gtk_adjustment_set_value((allowed_block->option_list+i)->widget , atof(value));
79 		}
80 		else if (GTK_IS_OPTION_MENU((allowed_block->option_list+i)->widget )) {
81 			set_option_menu_from_label ((allowed_block->option_list+i)->widget,
82 				value);
83 		}
84 		else {
85 			printf(_("No interface widget found for %s - contact the programmer!\n"),(allowed_block->option_list+i)->label);
86 			return;
87 		}
88 	}
89 }
90 
update_options(option_file_type * allowed_opt,option_file_type * input_opt)91 void update_options(option_file_type *allowed_opt,
92 		option_file_type *input_opt) {
93 //	Updates the widgets with options found in "input_opt"
94 //	Note for the future:  recreate the display if allowed_opt is NULL
95 	gint i,bi;
96 	for (i=0; i<allowed_opt->nb_blocks; i++) {
97 		bi = get_block_index((allowed_opt->option_block+i)->block_name,
98 			input_opt);
99 		update_block_page(allowed_opt->option_block+i,
100 			input_opt->option_block+i);
101 	}
102 
103 }
open_rc(GtkWidget * wdg,gpointer callb_data)104 void open_rc (GtkWidget *wdg, gpointer callb_data) {
105 	gchar *dname=NULL, *buffer=NULL, *msg=NULL, *title;
106 	rc_callback_data *data;
107 	option_file_type *tmp;
108 	data = (rc_callback_data *) callb_data;
109 	if (*data->path_n_file)
110 		split_dir_file(*data->path_n_file, &dname, &buffer, FILESEP);
111 //	We're supposed to free "buffer" here since it's a copy and it's not required,
112 //	but it gives a seg fault (???).
113 	buffer = 	GetFilename(	config_ext_list,
114 				NB_CONFIG_EXT,
115 				_("Open"),
116 				dname,
117 				EXISTING_FILE,
118 				_(config_ext_list[0].lbl));
119 	if (buffer)
120 		tmp = load_config_file(buffer,data->allowed_opt,&msg);
121 	else
122 		return;
123 	if (!tmp) {
124 		my_msg(msg,WARNING);
125 		return;
126 	}
127 	(*data->path_n_file) = buffer;
128 	free_options_except_values(*data->current_opt_ptr);
129 	(*data->current_opt_ptr) = tmp;
130 	title = (gchar *) x_malloc(strlen(*data->path_n_file)+1+strlen(_("Options file %s")), "gchar (Options file title)");
131 	sprintf(title,_("Options file %s"),*data->path_n_file);
132 	gtk_window_set_title(GTK_WINDOW(data->window),title);
133 	update_options(data->allowed_opt,*data->current_opt_ptr);
134 //	Reinitialize the current options for the application ("transmit" them)
135 //	Up-to-date options are in data->allowed_opt
136 	(*data->process_options) (data->allowed_opt);
137 	data->if_modified = FALSE;
138 	return;
139 }
140 
save_options(rc_callback_data * data)141 gint save_options (rc_callback_data *data) {
142 	gchar *msg=NULL;
143 	save_all_options(*data->path_n_file,
144 		data->allowed_opt,
145 		data->save_comments,
146 		&msg);
147 	if (msg) {
148 		my_msg(msg,WARNING);
149 		return FALSE;
150 	}
151 	else {
152 		(*data->process_options) (data->allowed_opt);
153 		data->if_modified = FALSE;
154 		return TRUE;
155 	}
156 }
157 
save_rc_as(GtkWidget * wdg,gpointer callb_data)158 void save_rc_as (GtkWidget *wdg, gpointer callb_data) {
159 	gchar *dname=NULL, *new_file=NULL, *previous_file=NULL, *title;
160 	rc_callback_data *data;
161 	data = (rc_callback_data *) callb_data;
162 	if (*data->path_n_file)
163 		split_dir_file(*data->path_n_file, &dname, &new_file, FILESEP);
164 	new_file = GetFilename(	config_ext_list,
165 				NB_CONFIG_EXT,
166 				_("Save as..."),
167 				dname,
168 				NEW_FILE,
169 				_(config_ext_list[0].lbl));
170 	if (!new_file)
171 		return;
172 	previous_file = (*data->path_n_file) ;
173 	(*data->path_n_file) = new_file;
174 	if (!save_options(data))
175 		 (*data->path_n_file) = previous_file;
176 	title = x_malloc(strlen(*data->path_n_file)+1+strlen(_("Options file %s")),"gchar (Options file title)");
177 	sprintf(title,_("Options file %s"),*data->path_n_file);
178 	gtk_window_set_title(GTK_WINDOW(data->window),title);
179 	return;
180 }
181 
save_rc(GtkWidget * wdg,gpointer callb_data)182 void save_rc (GtkWidget *wdg, gpointer callb_data) {
183 	rc_callback_data *data;
184 	data = (rc_callback_data *) callb_data;
185 	if (*data->path_n_file)
186 		save_options(data);
187 	else
188 		save_rc_as(wdg,callb_data);
189 	return;
190 }
191 
set_save_comments(GtkWidget * wdg,gpointer callb_data)192 void set_save_comments (GtkWidget *wdg, gpointer callb_data) {
193 	rc_callback_data *data;
194 	data = (rc_callback_data *) callb_data;
195 	data->save_comments = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(wdg));
196 }
197 
process_entry(GtkWidget * wdg,gpointer callb_data)198 void process_entry (GtkWidget *wdg, gpointer callb_data) {
199 	option_type *data;
200 	data = (option_type *)callb_data;
201 	if (data->value)
202 		x_free(data->value);
203 	data->value = gtk_editable_get_chars(GTK_EDITABLE(wdg),0,-1);
204 }
205 
switch_modified(GtkWidget * wdg,gpointer callb_data)206 void switch_modified (GtkWidget *wdg, gpointer callb_data) {
207 	*((gboolean *) callb_data) = TRUE;
208 }
209 
choose_file(GtkWidget * wdg,gpointer callb_data)210 void choose_file (GtkWidget *wdg, gpointer callb_data) {
211 	gchar *buffer, *buf_msg;
212 	const gchar *buf_utf8;
213 	option_type *data;
214 	data = (option_type *)callb_data;
215 
216 	buffer = GetFilename(blank_ext_list,
217 		NB_BLANK_EXT,
218 		_("Open"),
219 		NULL,
220 		EXISTING_FILE,
221 		blank_ext_list[0].lbl);
222 	if (!buffer)
223 		return;
224 	if (directory_exists(buffer)) { // If it's a directory, it's not a file!
225 		buf_msg = (gchar *) x_malloc(strlen(_("%s is not a file"))+1+strlen(buffer), "const gchar (buf_msg - is not a file in choose config file)");
226 		sprintf(buf_msg,_("%s is not a file"),buffer);
227 		my_msg(buf_msg,WARNING);
228 		return;
229 	}
230 
231 	buf_utf8 = g_filename_to_utf8(buffer,-1,NULL,NULL,NULL);
232 	data->value = (gchar *) x_realloc(data->value, strlen(buffer)+1, "gchar - (data->value in config - choose file)");
233 	strcpy(data->value,buffer);
234 	gtk_entry_set_text(GTK_ENTRY(data->widget),buf_utf8);
235 	gtk_widget_set_usize(GTK_WIDGET(data->widget),
236 		2.5*MAX(15,3*strlen(buf_utf8)),0);
237 }
238 
choose_dir(GtkWidget * wdg,gpointer callb_data)239 void choose_dir (GtkWidget *wdg, gpointer callb_data) {
240 	gchar *buffer, *new_buf=NULL;
241 	const gchar *buf_utf8;
242 	option_type *data;
243 	data = (option_type *)callb_data;
244 	buffer = GetDirname(blank_ext_list,
245 		NB_BLANK_EXT,
246 		_("Open"),
247 		NULL,
248 		EXISTING_FILE,
249 		blank_ext_list[0].lbl);
250 	if (!buffer)
251 		return;
252 	// Directory existence is tested in GetDirname
253 	new_buf = add_filesep(buffer);
254 	buf_utf8 = g_filename_to_utf8(new_buf,-1,NULL,NULL,NULL);
255 	data->value = (gchar *) x_realloc(data->value, strlen(new_buf)+1,"gchar - (data->value in config - choose dir)" );
256 	strcpy(data->value,new_buf);
257 	gtk_entry_set_text(GTK_ENTRY(data->widget),buf_utf8);
258 	gtk_widget_set_usize(GTK_WIDGET(data->widget),
259 		2.5*MAX(15,3*strlen(buf_utf8)),0);
260 
261 	if (new_buf && (new_buf!=buffer) )
262 		x_free(new_buf);
263 }
264 
get_decimals(option_type * opt)265 gint get_decimals (option_type *opt) {
266 	// Try to guess decimals from domain range or current value
267 	gchar *reference_string, *dec;
268 	if (strcmp(opt->field_type,"float"))
269 		return 0;
270 	if (opt->field_domain)
271 		reference_string = opt->field_domain;
272 	else
273 		if (opt->value)
274 			reference_string = opt->value;
275 		else
276 			return 2; // default
277 	dec = strrchr(reference_string,'.');
278 	return MAX(0,strlen(1+dec));
279 }
280 
scale_integer(GtkWidget * wdg,gpointer callb_data)281 void scale_integer (GtkWidget *wdg, gpointer callb_data) {
282 	//	Update the current variable value with the scale result
283 	opt_callb_data *data;
284 	data = (opt_callb_data *)callb_data;
285 	if (! ((data->opt->value) && (strlen(data->opt->value)>=data->lng)) )
286 		data->opt->value = (gchar *) x_realloc(data->opt->value,data->lng+1, "gchar (data->opt->value in config - scale_integer)");
287 	sprintf(data->opt->value,data->format,
288 		(gint) GTK_ADJUSTMENT(wdg)->value);
289 }
290 
scale_float(GtkWidget * wdg,gpointer callb_data)291 void scale_float (GtkWidget *wdg, gpointer callb_data) {
292 	//	Update the current variable value with the scale result
293 	opt_callb_data *data;
294 	data = (opt_callb_data *)callb_data;
295 	if (! ((data->opt->value) && (strlen(data->opt->value)>=data->lng)) )
296 		data->opt->value = x_realloc(data->opt->value,data->lng+1, "gchar (data->opt->value in config - scale_float)");
297 	sprintf(data->opt->value,data->format,
298 		(gfloat) GTK_ADJUSTMENT(wdg)->value);
299 }
300 
build_float_format(opt_callb_data * opt)301 void build_float_format (opt_callb_data *opt) {
302 	// Deduce format from field domain
303 	gfloat max;
304 	max = atof(1+strrchr(opt->opt->field_domain,'-'));
305 	if (!max)
306 		max = 1000000000;	// Default
307 	opt->lng = 1 + (gint) log10(max);
308 	opt->dec = get_decimals(opt->opt);
309 	opt->format = (gchar *) x_malloc(10, "gchar (opt->format (float) in config file)");
310 	sprintf(opt->format,"%%%d.%df",opt->lng,opt->dec);
311 }
312 
build_integer_format(opt_callb_data * opt)313 void build_integer_format (opt_callb_data *opt) {
314 	// Deduce format from field domain
315 	gint max;
316 	max = atoi(1+strrchr(opt->opt->field_domain,'-'));
317 	if (!max)
318 		max = 1000000000;	// Default
319 	opt->lng = 1 + (gint) log10(max);
320 	opt->dec = 0;
321 	opt->format = (gchar *) x_malloc(10, "gchar (opt->format (integer) in config file)");
322 	sprintf(opt->format,"%%%dd",opt->lng);
323 }
324 
build_adjustment(option_type * opt)325 GtkObject *build_adjustment (option_type *opt) {
326 	GtkObject *adj;
327 	gfloat val, min, max, step_increment, page_increment;
328 	if (opt->value)
329 		val = atof(opt->value);
330 	else
331 		val = 0.0;
332 	min = atof(opt->field_domain);
333 	max = atof(1+strrchr(opt->field_domain,'-'));
334 	if (!strcmp(opt->field_type,"float")) {
335 		step_increment = 1.0/MAX(1.0,pow(10.0,get_decimals(opt)));
336 		page_increment = 1.0;
337 	}
338 	else {	// integer
339 		step_increment = 1.0;
340 		page_increment = 1.0;
341 	}
342 	adj = gtk_adjustment_new (val,
343 		min, max,
344 		step_increment,
345 		step_increment, 0.0001);  // page_increment == step_increment, for now...
346 	return adj;
347 }
348 
get_element_index_in_domain(gchar * domain,gchar * element,gchar * sep,guint * index)349 gboolean get_element_index_in_domain (gchar *domain,
350 	gchar *element,
351 	gchar *sep,
352 	guint *index) {
353 	// Get index of "element" in "domain" given separator "sep" used in "domain"
354 	// Example:
355 	// Domain = "64#128#256#512"
356 	// Element = "256"
357 	// Sep = "#"
358 	// Index = 2
359 	gchar *domain_copy, *next;
360 	domain_copy = (gchar *) x_malloc (1+strlen(domain), "gchar (domain_copy in get_element_index_in_domain)");
361 	strcpy(domain_copy,domain);
362 	next = strtok(domain_copy, sep);
363 	*index = 0;
364 	while (next) {
365 		if (!next)
366 			continue;
367 		if (!strcmp(next,element)) {
368 			x_free(domain_copy);
369 			return TRUE;
370 		}
371 		(*index)++;
372 		next = strtok(NULL,sep);
373 	}
374 	x_free(domain_copy);
375 	return FALSE;
376 }
377 
get_string_from_element_index_in_domain(gchar * domain,gint index,gchar * sep)378 gchar * get_string_from_element_index_in_domain (gchar *domain,
379 	gint index, gchar *sep) {
380 	// Creates a new string corresponding to "index" in "domain", given "sep"
381 	// Example:
382 	// Domain = "64#128#256#512"
383 	// Index = 2
384 	// Sep = "#"
385 	// Output string = "256"
386 	gchar *domain_copy, *next, *output_string;
387 	gint i;
388 	domain_copy = (gchar *) x_malloc (1+strlen(domain), "gchar (domain_copy in get_string_from_element_index_in_domain)");
389 	strcpy(domain_copy,domain);
390 	next = strtok(domain_copy, sep);
391 	for (i=0; i<index; i++) {
392 		if (!next)
393 			break;
394 		next = strtok(NULL,sep);
395 	}
396 	if (next) {
397 		output_string = (gchar *) x_malloc(strlen(next)+1, "gchar (output_string in get_string_from_element_index_in_domain)");
398 		strcpy (output_string, next);
399 	}
400 	else {
401 		output_string = NULL;
402 	}
403 	x_free(domain_copy);
404 	return output_string;
405 }
406 
changed_omenu_callb(GtkWidget * wdg,gpointer data)407 void changed_omenu_callb(GtkWidget *wdg, gpointer data)
408 {
409 	//	Function connected to the "changed" signal of the option menu
410 
411 	gint index;
412 	gchar *option_string;
413 	option_type *opt;
414 	opt = (option_type *) data;
415 
416 	index = gtk_option_menu_get_history (GTK_OPTION_MENU(wdg));
417 	if (index==-1)
418 		return; // No change
419 
420 	if (!index)
421 		option_string = _("<no value>");
422 
423 	// We get the string for index+1 because we added the "no value" string at
424 	// the beginning
425 	option_string = get_string_from_element_index_in_domain (opt->field_domain, index-1, SEP);
426 
427 	if (!option_string) {
428 		opt->value[0] = '\0';  // Empty string
429 		return;
430 	}
431 	else {
432 /**************************************/
433 // We couldn't free value, because sometimes it's a static char
434 // but sometimes it is not... so there is a small memory leak to deal with here
435 //		if (opt->value)
436 //			x_free(opt->value);
437 		opt->value = option_string;
438 	}
439 }
440 
changed_switch_modified_omenu(GtkWidget * wdg,gpointer callb_data)441 void changed_switch_modified_omenu (GtkWidget *wdg, gpointer callb_data) {
442 //	"wdg" is an option menu
443 //	callb_data is a gboolean*
444 //	This function must be collected to the "changed" signal
445 	*((gboolean *) callb_data) = TRUE;
446 }
447 
build_option_menu(option_type * opt,gboolean * if_modified_ptr)448 void build_option_menu (option_type *opt, gboolean*if_modified_ptr) {
449 	//	Build an option menu from the field_domain list
450 	//	Allocate the value field with enough room for any option menu label
451 	guint lng = 0, index = 0;
452 	gchar *domain,*next, *txt;
453 	GSList *group = NULL;
454 	GtkWidget *menu, *menuitem;
455 
456 	opt->widget = gtk_option_menu_new();
457 	menu = gtk_menu_new();
458 	gtk_option_menu_set_menu (GTK_OPTION_MENU(opt->widget),menu);
459 	gtk_widget_show(opt->widget);
460 
461 	menuitem = gtk_radio_menu_item_new_with_label (group,_("<no value>"));
462 	group = gtk_radio_menu_item_group(GTK_RADIO_MENU_ITEM(menuitem));
463 	gtk_menu_append(GTK_MENU(menu),menuitem);
464 	gtk_widget_show(menuitem);
465 //	gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
466 //		GTK_SIGNAL_FUNC(omenu_callb), (gpointer) opt);
467 
468 //	gtk_signal_connect (GTK_OBJECT ( menuitem ),
469 //		"activate", GTK_SIGNAL_FUNC (switch_modified_opt_menu),
470 //			if_modified_ptr);
471 	domain = (gchar *) x_malloc(strlen(opt->field_domain)+1, "gchar (domain in build_option_menu)");
472 	strcpy(domain, opt->field_domain);
473 	next = strtok(domain, SEP);
474 	while (next) {
475 		lng = MIN(lng,strlen(next));
476 		menuitem = gtk_radio_menu_item_new_with_label (group,next);
477 		group = gtk_radio_menu_item_group(GTK_RADIO_MENU_ITEM(menuitem));
478 		gtk_menu_append(GTK_MENU(menu),menuitem);
479 		gtk_widget_show(menuitem);
480 //		gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
481 //			GTK_SIGNAL_FUNC(omenu_callb), (gpointer) opt);
482 //		gtk_signal_connect (GTK_OBJECT ( menuitem ),
483 //			"activate", GTK_SIGNAL_FUNC (switch_modified_opt_menu),
484 //				if_modified_ptr);
485 		next = strtok(NULL, SEP);
486 	}
487 	if (! ((opt->value) && (strlen(opt->value)>=lng)) )
488 		opt->value = x_realloc(opt->value,lng+1, "gchar (opt->value in build_option_menu)");
489 	x_free(domain);
490 
491 //	Activate the current item
492 
493 	if ( (opt->value) && strlen(opt->value)) {
494 		txt = alltrim(opt->value);
495 		if (get_element_index_in_domain (opt->field_domain, txt, SEP, &index))
496 			// We add 1 to index because of the "no value" entry,
497 			// which is not in the field domain
498 			gtk_option_menu_set_history (GTK_OPTION_MENU(opt->widget),index+1);
499 		else	// Invalid value = no value
500 			gtk_option_menu_set_history (GTK_OPTION_MENU(opt->widget),0);
501 //		**** Check why it seg faults
502 //		x_free(txt);
503 	}
504 	else // No value
505 		gtk_option_menu_set_history (GTK_OPTION_MENU(opt->widget),0);
506 	gtk_signal_connect (GTK_OBJECT(opt->widget), "changed", GTK_SIGNAL_FUNC (changed_omenu_callb), (gpointer) opt);
507 	gtk_signal_connect (GTK_OBJECT(opt->widget), "changed", GTK_SIGNAL_FUNC (changed_switch_modified_omenu), if_modified_ptr);
508 
509 }
510 
get_widget_type(option_type * opt)511 gint get_widget_type (option_type *opt) {
512 //	Deduce widget type (depends on field_type, field_domain and
513 //	current implementation choices ... or laziness)
514 	if (!strcmp(opt->field_type,"file"))
515 		return FILE_ENTRY;
516 	if (!strcmp(opt->field_type,"dir"))
517 		return DIR_ENTRY;
518 	if (!opt->field_domain)
519 		return GENERIC_ENTRY;
520 	if ( (!strcmp(opt->field_type,"integer") ) ||
521 	     (!strcmp(opt->field_type,"float") ) )
522 		if (strchr(opt->field_domain,'-'))
523 			return SCALE_ENTRY;
524 	if (strchr(opt->field_domain,'#'))
525 		return OPT_MENU;
526 	return GENERIC_ENTRY;
527 }
528 
create_block_page(option_block_type * allowed_block,GtkWidget * notebook,GtkWidget * window,gboolean * if_modified_ptr)529 void create_block_page (option_block_type *allowed_block,
530 			GtkWidget *notebook,
531 			GtkWidget *window,
532 			gboolean *if_modified_ptr) {
533 	//	Create a new page for the current block
534 	//	Descriptions and domain validations come from allowed_block
535 	gint i, field_length, widget_length, widget_type;
536 	GtkWidget *vbox, *table, *open_icon, *wdg, *scrolled_window;
537 	GtkObject *adj;
538 	opt_callb_data *callb_data;
539 
540 	vbox = gtk_vbox_new(FALSE,0);
541 	gtk_widget_show(GTK_WIDGET(vbox));
542 
543 	scrolled_window = gtk_scrolled_window_new (NULL, NULL);
544         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
545 	gtk_widget_show (scrolled_window);
546 	gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW(scrolled_window), vbox);
547 
548 //	define_label_in_box (allowed_block->block_name,
549 //		vbox, FALSE, TRUE, DEF_PAD);
550 	gtk_notebook_append_page (GTK_NOTEBOOK(notebook),
551 		scrolled_window, gtk_label_new(_(allowed_block->block_description)));
552 
553 	gtk_widget_set_size_request (scrolled_window, -1, gdk_screen_get_height(gdk_screen_get_default())/2);
554 
555 	table = gtk_table_new(allowed_block->nb_options, 3, FALSE);
556 	gtk_widget_show(GTK_WIDGET(table));
557 	gtk_container_add(GTK_CONTAINER(vbox),GTK_WIDGET(align_widget(table,0.5,0.0)));
558 
559 	for (i=0; i<allowed_block->nb_options; i++) {
560 		// Define label and define widget
561 		widget_type = get_widget_type (allowed_block->option_list+i);
562 		if ( (allowed_block->option_list+i)->value)
563 			field_length =
564 				MAX(20,3*strlen( (allowed_block->option_list+i)->value));
565 		else
566 			field_length = 20;
567 		widget_length = field_length*3;
568 		define_label_in_table_align((allowed_block->option_list+i)->field_description,
569 			table,0, 1, i, i+1, DEF_PAD, ALIGN_LEFT, ALIGN_CENTER);
570 
571 		if ((widget_type == DIR_ENTRY) || (widget_type == FILE_ENTRY)) {
572 			widget_length = field_length*2.5;
573 			field_length = 255;	// Magic number... should be enough for a path!
574 			open_icon = gtk_button_new();
575 			gtk_widget_show(GTK_WIDGET(open_icon));
576 			gtk_container_add(GTK_CONTAINER(open_icon),
577 				create_widget_from_xpm(window,(gchar **)open_xpm));
578 			gtk_table_attach (GTK_TABLE (table),
579 				open_icon, 2, 3, i, i+1, 0, 0, DEF_PAD, DEF_PAD);
580 			if (widget_type == DIR_ENTRY)
581 				gtk_signal_connect (GTK_OBJECT (open_icon), "clicked",
582 		       			GTK_SIGNAL_FUNC(choose_dir),
583 					allowed_block->option_list+i);
584 			else
585 				gtk_signal_connect (GTK_OBJECT (open_icon), "clicked",
586 		       			GTK_SIGNAL_FUNC(choose_file),
587 					allowed_block->option_list+i);
588 		}
589 		else if (widget_type == SCALE_ENTRY) {
590 			callb_data = (opt_callb_data *) x_malloc(sizeof(opt_callb_data), "opt_callb_data");
591 			callb_data->opt = allowed_block->option_list+i;
592 			adj = build_adjustment(allowed_block->option_list+i);
593 			if (adj) {
594 				(allowed_block->option_list+i)->widget = adj;
595 				if (!strcmp((allowed_block->option_list+i)->field_type,"float")) {
596 					build_float_format (callb_data);
597 					}
598 				else {
599 					build_integer_format (callb_data);
600 				}
601     				wdg = gtk_hscale_new (GTK_ADJUSTMENT (adj));
602 				gtk_scale_set_digits (GTK_SCALE (wdg), callb_data->dec);
603 				gtk_widget_set_usize(wdg,200,0);
604 				gtk_widget_show(GTK_WIDGET(wdg));
605 				gtk_table_attach(GTK_TABLE(table), wdg, 1, 2,
606 					i, i+1, GTK_FILL, GTK_FILL, DEF_PAD, DEF_PAD);
607 
608 				if (callb_data->dec)
609 					gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
610 						GTK_SIGNAL_FUNC(scale_float),
611 						(gpointer) callb_data);
612 				else
613 					gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
614 						GTK_SIGNAL_FUNC(scale_integer),
615 						(gpointer) callb_data);
616 				gtk_signal_connect (GTK_OBJECT ( adj ),
617 					"value_changed",
618 					GTK_SIGNAL_FUNC (switch_modified),
619 					if_modified_ptr);
620 				continue;
621 			}
622 		}
623 		else if (widget_type == OPT_MENU) {
624 			build_option_menu(allowed_block->option_list+i, if_modified_ptr);
625 			gtk_table_attach(GTK_TABLE(table),
626 					(allowed_block->option_list+i)->widget, 1, 2,
627 					i, i+1, GTK_FILL, GTK_FILL, DEF_PAD, DEF_PAD);
628 
629 			continue;
630 		}
631 		//	Defaults to GENERIC_ENTRY
632 
633 		(allowed_block->option_list+i)->widget =
634 			define_entry_in_table_align((allowed_block->option_list+i)->value,
635 				table, 1, 2, i, i+1, field_length, widget_length,
636 				DEF_PAD, ALIGN_LEFT, ALIGN_CENTER);
637 		gtk_signal_connect (GTK_OBJECT ( (allowed_block->option_list+i)->widget ),
638 			"changed", GTK_SIGNAL_FUNC (process_entry),
639 			allowed_block->option_list+i);
640 		gtk_signal_connect (GTK_OBJECT ( (allowed_block->option_list+i)->widget ),
641 			"changed", GTK_SIGNAL_FUNC (switch_modified),
642 			if_modified_ptr);
643 	}
644 }
645 
create_block_page_without_dictionary(option_block_type * current_block,GtkWidget * notebook,gboolean * if_modified_ptr)646 void create_block_page_without_dictionary (
647 			option_block_type *current_block,
648 			GtkWidget *notebook,
649 			gboolean *if_modified_ptr) {
650 	//	Create a new page for the current block
651 	//	Descriptions and domain validations come from allowed_block
652 	//	**** The options subsystem cannot be used without dictionary for now!
653 	//	**** Other functions are incomplete... consistency is not assured
654 	gint i, field_length;
655 	gchar *value;
656 	GtkWidget *vbox, *table, *scrolled_window;
657 	vbox = gtk_vbox_new(FALSE,0);
658 	gtk_widget_show(GTK_WIDGET(vbox));
659 
660 	scrolled_window = gtk_scrolled_window_new (NULL, NULL);
661         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
662 	gtk_widget_show (scrolled_window);
663 	gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW(scrolled_window), vbox);
664 
665 	if (current_block->block_name)
666 		gtk_notebook_append_page (GTK_NOTEBOOK(notebook),
667 			scrolled_window, gtk_label_new(current_block->block_name));
668 	else
669 		gtk_notebook_append_page (GTK_NOTEBOOK(notebook),
670 			scrolled_window, gtk_label_new("*"));
671 
672 	table = gtk_table_new(current_block->nb_options, 2, FALSE);
673 	gtk_widget_show(GTK_WIDGET(table));
674 	gtk_container_add(GTK_CONTAINER(vbox),GTK_WIDGET(align_widget(table,0.5,0.0)));
675 
676 	for (i=0; i<current_block->nb_options; i++) {
677 		value = 	(current_block->option_list+i)->value;
678 		define_label_in_table_align((current_block->option_list+i)->label,
679 			table,0, 1, i, i+1, DEF_PAD, ALIGN_LEFT, ALIGN_CENTER);
680 		field_length = MAX(15,3*strlen(value));
681 		(current_block->option_list+i)->widget =
682 			define_entry_in_table_align(value,table,
683 				1, 2, i, i+1, field_length, field_length*3,
684 				DEF_PAD, ALIGN_LEFT, ALIGN_CENTER);
685 		gtk_signal_connect (GTK_OBJECT ( (current_block->option_list+i)->widget ),
686 			"changed", GTK_SIGNAL_FUNC (process_entry),
687 			current_block->option_list+i);
688 		gtk_signal_connect (GTK_OBJECT ( (current_block->option_list+i)->widget ),
689 			"changed", GTK_SIGNAL_FUNC (switch_modified),
690 			if_modified_ptr);
691 	}
692 }
693 
ok_options_callb(GtkWidget * wdg,gpointer callb_data)694 gint ok_options_callb(GtkWidget *wdg, gpointer callb_data) {
695 	gchar *msg;
696 	rc_callback_data *data;
697 	data = (rc_callback_data *) callb_data;
698 	if (data->if_modified) {
699 		msg = (gchar *) x_malloc(strlen(*data->path_n_file)+strlen(_("<%s> has been modified. Save?"))+1, "const gchar (msg in ok_options_callb)");
700 		sprintf(msg,_("<%s> has been modified. Save?"),*data->path_n_file);
701 		if (yes_no(msg,TRUE)) {
702 //	Up-to-date options are in data->allowed_opt
703 			(*data->process_options) (data->allowed_opt);
704 			data->if_modified = FALSE;
705 			save_options(data);
706 		}
707 //		const gchar...
708 //		if (msg)
709 //			x_free(msg);
710 	}
711 	return FALSE;
712 }
713 
options_dialog_new(char ** path_n_file,option_file_type ** current_opt_ptr,option_file_type * allowed_opt,void (* process_options)(option_file_type *))714 GtkWidget * options_dialog_new( char **path_n_file,
715 			option_file_type **current_opt_ptr,
716 			option_file_type *allowed_opt,
717 			void (*process_options) (option_file_type *) ) {
718 
719 //	Builds a standard dialog for an option file, with the specs
720 //	given in "allowed_opt" and the data given in "current_opt"
721 //	2002.09.01 change:  data in current_opt is copied in allowed_opt at load time
722 //	The resulting window is permanent and should be
723 //	reactivated this way:
724 //		gtk_widget_show(GTK_WIDGET(returned_window));
725 //		gtk_grab_add(GTK_WIDGET(returned_window));
726 
727 //	If "allowed_opt" is null, we display all variables with entry fields,
728 //	without validation and without comments
729 //	Now the process...
730 //	1.	Create a window
731 //		This window will contain a modal dialog
732 //		It uses the default OK button of the modal "show-hide" utility
733 //	2.	Create a notebook
734 //	3.	Loop on blocks in allowed_opt (or current_opt)
735 //		3.1 For each block, create a notebook page
736 //		3.2 For each option in the block, create a widget
737 //		2005-01-24 generic entry tests not yet implemented
738 //		Using option menus or adjustment minimize this need
739 //		... integer -> is_integer test
740 //		... float -> is_float test (to improve)
741 //		... file -> filexists test
742 //		... dir -> direxists test (to build)
743 //		... char -> no particular test
744 //		... domain == list:  build an option menu
745 //		... domain == range / integer or float:  build a scale
746 //		... domain == range / char or domain == NULL:  simple entry
747 //		Use generic callbacks, with data == &option->value
748 //	4.	Insert the notebook in a vbox
749 //	5.	Append a "save" button, a "save with comments" button
750 //		and a "load" button to the vbox
751 //	6.	Embed the vbox in a modal dialog with "modal_dialog_showhide"
752 
753 	gint i;
754 	GtkWidget *notebook,*button, *hbox, *vbox;
755 	static rc_callback_data data;
756 	option_file_type *current_opt;
757 	gchar *title;
758 
759 //	Initialize the callback data
760 	data.path_n_file = path_n_file;
761 	data.current_opt_ptr = current_opt_ptr;
762 	data.allowed_opt = allowed_opt;
763 	data.save_comments = TRUE;
764 	data.if_modified = FALSE;
765 	data.process_options = process_options;
766 
767 	data.window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
768 //	We realize the window because we need to define pixmaps in it
769 	gtk_widget_realize(GTK_WIDGET(data.window));
770 	title = (gchar *) x_malloc(strlen(*path_n_file)+1+strlen(_("Options file %s")), "gchar (title in options_dialog_new)");
771 	sprintf(title,_("Options file %s"),*path_n_file);
772 
773 //	Vbox in which we insert the notebook, the 2 "save" and the "open" buttons
774 //	This widget would be embedded in the modal dialog
775 
776 	vbox = gtk_vbox_new(FALSE,0);
777 	gtk_widget_show(GTK_WIDGET(vbox));
778 
779 //	Create a new notebook, place the position of the tabs
780  	notebook = gtk_notebook_new ();
781     	gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
782 	gtk_box_pack_start_defaults(GTK_BOX(vbox),notebook);
783     	gtk_widget_show(notebook);
784 
785 //	Dereference the current options before processing them
786 	current_opt = (option_file_type *) *current_opt_ptr;
787 //	Loop on blocks and compute each block as a notebook page
788 
789 	if (allowed_opt)
790 		for (i=0; i<allowed_opt->nb_blocks; i++) {
791 			create_block_page(allowed_opt->option_block+i,
792 				notebook,
793 				data.window,
794 				&data.if_modified);
795 		}
796 	else
797 		//	Only display the fields as text entries
798 		for (i=0; i<current_opt->nb_blocks; i++) {
799 			create_block_page_without_dictionary(
800 				current_opt->option_block+i,
801 				notebook,
802 				&data.if_modified);
803 		}
804 	gtk_notebook_set_page(GTK_NOTEBOOK(notebook),0);
805 
806 	hbox = gtk_hbox_new(FALSE,DEF_PAD);
807 	gtk_widget_show(GTK_WIDGET(hbox));
808 
809 	button = gtk_button_new_with_label(_("Open"));
810 	gtk_widget_show(GTK_WIDGET(button));
811 	gtk_signal_connect (GTK_OBJECT (button), "clicked",
812 	       (GtkSignalFunc) open_rc, &data);
813 	gtk_box_pack_start (GTK_BOX(hbox), button, FALSE, FALSE, DEF_PAD);
814 
815 	button = gtk_button_new_with_label(_("Save"));
816 	gtk_widget_show(GTK_WIDGET(button));
817 	gtk_signal_connect (GTK_OBJECT (button), "clicked",
818 	       (GtkSignalFunc) save_rc, &data);
819 	gtk_box_pack_start (GTK_BOX(hbox), button, FALSE, FALSE, DEF_PAD);
820 
821 	button = gtk_button_new_with_label(_("Save as..."));
822 	gtk_widget_show(GTK_WIDGET(button));
823 	gtk_signal_connect (GTK_OBJECT (button), "clicked",
824 	       (GtkSignalFunc) save_rc_as, &data);
825 	gtk_box_pack_start (GTK_BOX(hbox), button, FALSE, FALSE, DEF_PAD);
826 
827 	if (allowed_opt) {
828 	//	If a dictionary is given, we offer to save descriptions
829 	//	coming from this dictionary as comments
830 
831 		button = gtk_check_button_new_with_label(_("Save with comments"));
832 		gtk_box_pack_start (GTK_BOX(hbox), button, FALSE, FALSE, DEF_PAD);
833 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),TRUE);
834 		gtk_widget_show(GTK_WIDGET(button));
835 		gtk_signal_connect (GTK_OBJECT(button), "toggled",
836 			(GtkSignalFunc) set_save_comments, &data);
837 	}
838 
839  	gtk_box_pack_start (GTK_BOX (vbox),
840 		align_widget(hbox,ALIGN_CENTER, ALIGN_CENTER),
841 		FALSE, FALSE, DEF_PAD);
842 
843 	modal_dialog_showhide(data.window, vbox, title, ok_options_callb, NULL, &data,
844 		GTK_WIN_POS_CENTER, TRUE);
845 
846 	// Required for GTK 2
847 	data.if_modified = FALSE;
848 
849 	return data.window;
850 }
851 
852 
853