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