1 /*
2 ** 1998-05-31 - After a long wait, here it finally is - the gdtool GUI config
3 ** module! This will be *big*, I can feel it.
4 ** 1998-06-16 - Smartened up the window handling. Now the config window is only ever
5 ** built (created) once. It is then reused! Environmentally safe.
6 ** It also allows me to use the config GUI to keep all the state,
7 ** although I'm not exactly convinced that's what I want to do...
8 ** 1998-06-22 - Redesigned. Cut away all old notebook-page-creation code (~140
9 ** lines) and implemented a new, more modular way of doing it.
10 ** 1998-07-26 - Added a global cache of page descriptors, avoiding having to
11 ** repeat the describe-calls all the time.
12 ** 1998-08-25 - Implemented a slim way of having the individual page modules
13 ** notify this main module of program-wide things they want done when
14 ** config closes. As an example, it is important to rescan the directories
15 ** if the styles or types change.
16 ** 1998-08-30 - Fixed version handling in config file, at least somewhat. Also added
17 ** a system-wide config, which is loaded if the user doesn't have one.
18 ** 1998-10-16 - System-wide config path now configurable via a symbol.
19 ** 1999-12-24 - Now uses the window utility module to handle the root config window,
20 ** thus making its size and position configurable and savable.
21 ** 2000-07-02 - Translated.
22 ** 2002-07-19 - Renamed, and recast the interface to use a tree. Cleaner-looking, and
23 ** handles nested stuff better.
24 */
25
26 #include "gentoo.h"
27
28 #include <stdlib.h>
29
30 #include "cmdseq.h"
31 #include "dialog.h"
32 #include "dirpane.h"
33 #include "fileutil.h"
34 #include "iconutil.h"
35 #include "nag_dialog.h"
36 #include "window.h"
37 #include "xmlutil.h"
38
39 #include "configure.h"
40
41 #include "cfg_module.h"
42
43 #include "cfg_buttonlayout.h"
44 #include "cfg_buttons.h"
45 #include "cfg_cmdcfg.h"
46 #include "cfg_cmdseq.h"
47 #include "cfg_controls.h"
48 #include "cfg_dialogs.h"
49 #include "cfg_dirpane.h"
50 #include "cfg_errors.h"
51 #include "cfg_menus.h"
52 #include "cfg_nag.h"
53 #include "cfg_paths.h"
54 #include "cfg_styles.h"
55 #include "cfg_types.h"
56 #include "cfg_windows.h"
57
58 /* This should be set in the Makefile, and passed along using the -D
59 ** compiler option, If not, let's default to nice old Slackware style.
60 */
61 #if !defined PATH_CFG
62 #define PATH_CFG "/usr/local/etc/"
63 #endif
64
65 /* ----------------------------------------------------------------------------------------- */
66
67 typedef struct {
68 MainInfo *min;
69 GtkWidget *dlg;
70 GtkWidget *view;
71 GtkTreeStore *store;
72 GtkTreeIter treeiter[4];
73 guint level; /* Current depth in tree, used when building. */
74 GtkWidget *first; /* Keeps track of first leaf node during build, for select. */
75 GtkWidget *nbook; /* A notebook widget holding all the config pages. */
76 GtkWidget *ok, *save, *cancel;
77 gint page; /* Index of last selected page. */
78
79 guint32 flags; /* Flags for stuff that need to be done when window closes. */
80 } CfgGui;
81
82 /* A global (yuck!) vector of page descriptor functions. This is where pointers to new pages go. */
83 static const CMDescribeFunc describe_page[] = { cdp_describe,
84 ccs_describe, ccc_describe,
85 cst_describe, ctp_describe,
86 cbt_describe, cbl_describe,
87 cpt_describe,
88 cwn_describe, cdl_describe,
89 cct_describe, cer_describe,
90 cng_describe,
91 };
92
93 #define CFG_PAGES (sizeof describe_page / sizeof describe_page[0])
94 /* A global vector of the resulting page descriptors. */
95 static const CfgModule *cfg_page[CFG_PAGES];
96
97
98 static CfgGui the_cfggui = { NULL };
99
100 /* ----------------------------------------------------------------------------------------- */
101
102 /* 2011-07-24 - Returns the base directory where the proper configuration file is to be found. */
get_config_dirname(void)103 static const gchar * get_config_dirname(void)
104 {
105 static gchar buf[1024] = "";
106
107 if(buf[0] == '\0')
108 {
109 const gchar *confdir;
110
111 if((confdir = g_get_user_config_dir()) != NULL)
112 g_snprintf(buf, sizeof buf, "%s" G_DIR_SEPARATOR_S PACKAGE, confdir);
113 }
114 return buf;
115 }
116
117 /* 2011-07-24 - Returns pointer to the proper filename for our configuration file. */
get_config_filename(const gchar * filename)118 static const gchar * get_config_filename(const gchar *filename)
119 {
120 static gchar buf[1024] = "";
121
122 if(buf[0] == '\0')
123 {
124 const gchar *confdir = get_config_dirname();
125
126 g_snprintf(buf, sizeof buf, "%s" G_DIR_SEPARATOR_S "%s", confdir, filename);
127 }
128 return buf;
129 }
130
131 /* 2011-07-24 - Returns pointer to the old and outdated filename used by our configuration file. */
get_config_filename_old(const gchar * filename)132 static const gchar * get_config_filename_old(const gchar *filename)
133 {
134 static gchar buf[1024];
135
136 if(buf[0] == '\0')
137 {
138 const gchar *home = g_getenv("HOME");
139
140 if(home == NULL)
141 home = g_get_home_dir();
142 if(home != NULL)
143 g_snprintf(buf, sizeof buf, "%s" G_DIR_SEPARATOR_S "%s", home, filename);
144 }
145 return buf;
146 }
147
148 /* ----------------------------------------------------------------------------------------- */
149
150 /* 1998-06-26 - Do the work of hiding the config GUI. */
hide_config(CfgGui * cgu)151 static void hide_config(CfgGui *cgu)
152 {
153 guint i;
154
155 for(i = 0; i < CFG_PAGES; i++)
156 {
157 if(cfg_page[i]->hide != NULL)
158 cfg_page[i]->hide(cgu->min);
159 }
160 if(the_cfggui.flags & CFLG_RESET_KEYBOARD)
161 kbd_context_clear(cgu->min->gui->kbd_ctx);
162
163 if(the_cfggui.flags & CFLG_REBUILD_TOP)
164 rebuild_top(cgu->min);
165 if(the_cfggui.flags & CFLG_REBUILD_MIDDLE)
166 rebuild_middle(cgu->min);
167
168 /* The rescanning needs to happen early, since we now have potentially stale Type pointers in the panes. */
169 if(the_cfggui.flags & CFLG_RESCAN_LEFT)
170 dp_rescan(&cgu->min->gui->pane[0]);
171 else if(the_cfggui.flags & CFLG_REDISP_LEFT)
172 dp_redisplay_preserve(&cgu->min->gui->pane[0]);
173 if(the_cfggui.flags & CFLG_RESCAN_RIGHT)
174 dp_rescan(&cgu->min->gui->pane[1]);
175 else if(the_cfggui.flags & CFLG_REDISP_RIGHT)
176 dp_redisplay_preserve(&cgu->min->gui->pane[1]);
177
178 if(the_cfggui.flags & CFLG_REBUILD_BOTTOM)
179 {
180 rebuild_bottom(cgu->min);
181 csq_execute(cgu->min, "ActivateOther");
182 csq_execute(cgu->min, "ActivateOther");
183 }
184
185 if(the_cfggui.flags & CFLG_FLUSH_ICONS)
186 ico_flush(cgu->min);
187
188 if(the_cfggui.flags & CFLG_RESET_KEYBOARD)
189 ctrl_keys_install(cgu->min->cfg.ctrlinfo, cgu->min->gui->kbd_ctx);
190
191 gtk_grab_remove(cgu->dlg);
192 win_window_relink(cgu->min->cfg.wininfo, WIN_CONFIG, cgu->dlg);
193 win_window_close(cgu->dlg);
194 }
195
196 /* 1998-06-26 - The user just clicked the OK button. Let all page modules know, then hide the
197 ** GUI.
198 */
evt_ok_clicked(GtkWidget * wid,gpointer user)199 static gint evt_ok_clicked(GtkWidget *wid, gpointer user)
200 {
201 CfgGui *cgu = user;
202 MainInfo *min = cgu->min;
203 guint i;
204
205 cfg_modified_set(min);
206
207 for(i = 0; i < CFG_PAGES; i++)
208 {
209 if(cfg_page[i]->accept != NULL)
210 cfg_page[i]->accept(min);
211 }
212 hide_config(cgu);
213 return TRUE;
214 }
215
216 /* 1998-09-18 - Broke the saving code out of the button handler, and made it globally
217 ** accessible.
218 */
cfg_save_all(MainInfo * min)219 void cfg_save_all(MainInfo *min)
220 {
221 const gchar *root = "GentooConfig", *rcname = get_config_filename(RCNAME);
222 FILE *out;
223 guint i;
224 const CfgModule *page;
225
226 cfg_modified_clear(min);
227 if((out = xml_put_open(rcname)) != NULL)
228 {
229 xml_put_node_open(out, root);
230 xml_put_text(out, "version", VERSION);
231 for(i = 0; i < CFG_PAGES; i++)
232 {
233 if((page = describe_page[i](min)) != NULL && (page->save != NULL))
234 page->save(min, out);
235 }
236 xml_put_node_close(out, root);
237 xml_put_close(out);
238 }
239 else
240 dlg_dialog_async_new_error(_("Couldn't open configuration file for output"));
241 }
242
243 /* 1998-07-25 - I guess it's becoming time to grow up and start outputting a config file.
244 ** XML seems to be the format of the week, so I'll just go for something like
245 ** that.
246 ** 1998-09-18 - Broke out the actual saving code and put in in a function of its own.
247 */
evt_save_clicked(GtkWidget * wid,gpointer user)248 static gint evt_save_clicked(GtkWidget *wid, gpointer user)
249 {
250 CfgGui *cgu = user;
251 MainInfo *min = cgu->min;
252 guint i;
253
254 for(i = 0; i < CFG_PAGES; i++)
255 {
256 if(cfg_page[i]->accept != NULL)
257 cfg_page[i]->accept(min);
258 }
259 cfg_save_all(min);
260 hide_config(cgu);
261
262 return TRUE;
263 }
264
265 /* 1998-07-12 - User clicked the cancel button. Hide the GUI. */
evt_cancel_clicked(GtkWidget * wid,gpointer user)266 static gint evt_cancel_clicked(GtkWidget *wid, gpointer user)
267 {
268 hide_config(user);
269 return TRUE;
270 }
271
272 /* 1998-05-31 - Build the buttons at the bottom of the config window (OK, Save, Cancel). */
build_buttons(CfgGui * cgu)273 static void build_buttons(CfgGui *cgu)
274 {
275 cgu->ok = gtk_button_new_with_label(_("OK"));
276 g_signal_connect(G_OBJECT(cgu->ok), "clicked", G_CALLBACK(evt_ok_clicked), cgu);
277 gtk_dialog_add_action_widget(GTK_DIALOG(cgu->dlg), cgu->ok, GTK_RESPONSE_OK);
278 cgu->save = gtk_button_new_with_label(_("Save"));
279 g_signal_connect(G_OBJECT(cgu->save), "clicked", G_CALLBACK(evt_save_clicked), cgu);
280 gtk_dialog_add_action_widget(GTK_DIALOG(cgu->dlg), cgu->save, GTK_RESPONSE_OK);
281 cgu->cancel = gtk_button_new_with_label(_("Cancel"));
282 g_signal_connect(G_OBJECT(cgu->cancel), "clicked", G_CALLBACK(evt_cancel_clicked), cgu);
283 gtk_dialog_add_action_widget(GTK_DIALOG(cgu->dlg), cgu->cancel, GTK_RESPONSE_CANCEL);
284
285 gtk_widget_set_can_default(cgu->ok, TRUE);
286 gtk_widget_set_can_default(cgu->save, TRUE);
287 gtk_widget_set_can_default(cgu->cancel, TRUE);
288 gtk_widget_grab_default(cgu->ok);
289 }
290
291 /* 1998-06-16 - This gets called as the user clicks the close button of the config GUI.
292 ** Unlike what the function name might lead you to expect, we don't destroy the
293 ** GUI we so laborously (sp?) created. We just hide it so we can use it again
294 ** later. Neat for several reasons.
295 */
evt_cfg_delete(GtkWidget * wid,GdkEvent * evt,gpointer user)296 static gint evt_cfg_delete(GtkWidget *wid, GdkEvent *evt, gpointer user)
297 {
298 hide_config(user);
299
300 return TRUE;
301 }
302
303 /* 2004-11-2x - Page tree cursor changed, so switch page in the notebook on the right. */
evt_view_cursor_changed(GtkWidget * view,gpointer user)304 static void evt_view_cursor_changed(GtkWidget *view, gpointer user)
305 {
306 CfgGui *cgu = user;
307 GtkTreePath *path;
308 GtkTreeIter iter;
309
310 gtk_tree_view_get_cursor(GTK_TREE_VIEW(view), &path, NULL);
311 if(path == NULL)
312 return;
313 gtk_tree_model_get_iter(GTK_TREE_MODEL(cgu->store), &iter, path);
314 if(gtk_tree_model_iter_n_children(GTK_TREE_MODEL(cgu->store), &iter) > 0) /* Disregard interior nodes. */
315 return;
316 gtk_tree_model_get(GTK_TREE_MODEL(cgu->store), &iter, 1, &cgu->page, -1);
317 gtk_notebook_set_current_page(GTK_NOTEBOOK(cgu->nbook), cgu->page);
318 }
319
320 /* 1998-06-16 - Cooled up (?) this routine a lot. It is now recursive, in a rather
321 ** interesting manner. The point is not to reconstruct the entire config-
322 ** GUI each time it is needed, but rather to just build it once and then
323 ** hide/show it as needed.
324 */
cfg_configure(MainInfo * min)325 gint cfg_configure(MainInfo *min)
326 {
327 CfgGui *cgu = &the_cfggui;
328 gchar *name = NULL;
329 guint i;
330 GtkWidget *hbox, *scwin, *simple;
331 GtkCellRenderer *cr;
332 GtkTreeViewColumn *vc;
333
334 the_cfggui.flags = 0U;
335
336 /* Do the widgets already exist? Then just update and display. */
337 if(cgu->dlg != NULL)
338 {
339 for(i = 0; i < CFG_PAGES; i++)
340 {
341 if(cfg_page[i]->update != NULL)
342 cfg_page[i]->update(min);
343 }
344 win_window_show(cgu->dlg);
345 gtk_notebook_set_current_page(GTK_NOTEBOOK(cgu->nbook), the_cfggui.page);
346 gtk_widget_show_all(cgu->dlg);
347 gtk_grab_add(cgu->dlg);
348 return 1;
349 }
350
351 /* Initialize the cache of page descriptors. */
352 for(i = 0; i < CFG_PAGES; i++)
353 cfg_page[i] = describe_page[i](min);
354
355 cgu->min = min;
356 cgu->dlg = win_window_open(min->cfg.wininfo, WIN_CONFIG);
357 gtk_window_set_modal(GTK_WINDOW(cgu->dlg), TRUE);
358 g_signal_connect(G_OBJECT(cgu->dlg), "delete_event", G_CALLBACK(evt_cfg_delete), cgu);
359
360 cgu->store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_INT); /* Simply store notebook page# in second column. */
361 cgu->level = 0U;
362
363 cgu->first = NULL;
364 cgu->nbook = gtk_notebook_new();
365 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(cgu->nbook), FALSE);
366
367 for(i = 0; i < CFG_PAGES; i++)
368 {
369 if(cfg_page[i]->init != NULL)
370 {
371 if((simple = cfg_page[i]->init(min, &name)) != NULL)
372 {
373 gint pn = gtk_notebook_append_page(GTK_NOTEBOOK(cgu->nbook), simple, NULL);
374
375 gtk_tree_store_append(cgu->store, &cgu->treeiter[cgu->level], NULL);
376 gtk_tree_store_set(cgu->store, &cgu->treeiter[cgu->level], 0, name, -1);
377 gtk_tree_store_set(cgu->store, &cgu->treeiter[cgu->level], 1, pn, -1);
378 }
379 }
380 }
381 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
382 scwin = gtk_scrolled_window_new(NULL, NULL);
383 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
384 /* Build tree view, showing only the first column. */
385 cgu->view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(cgu->store));
386 cr = gtk_cell_renderer_text_new();
387 vc = gtk_tree_view_column_new_with_attributes("(ConfigPages)", cr, "text", 0, NULL);
388 gtk_tree_view_append_column(GTK_TREE_VIEW(cgu->view), vc);
389 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(cgu->view), FALSE);
390 g_signal_connect(G_OBJECT(cgu->view), "cursor_changed", G_CALLBACK(evt_view_cursor_changed), cgu);
391 gtk_container_add(GTK_CONTAINER(scwin), cgu->view);
392 gtk_box_pack_start(GTK_BOX(hbox), scwin, FALSE, FALSE, 0);
393
394 scwin = gtk_scrolled_window_new(NULL, NULL);
395 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
396 gtk_container_add(GTK_CONTAINER(scwin), cgu->nbook);
397 gtk_box_pack_start(GTK_BOX(hbox), scwin, TRUE, TRUE, 0);
398 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(cgu->dlg))), hbox, TRUE, TRUE, 0);
399 build_buttons(cgu);
400
401 return cfg_configure(min); /* Recurse! */
402 }
403
404 /* ----------------------------------------------------------------------------------------- */
405
goto_iterate(const CfgGui * cgu,GtkTreeIter * iter,const char * label)406 static gboolean goto_iterate(const CfgGui *cgu, GtkTreeIter *iter, const char *label)
407 {
408 GtkTreeIter citer;
409 const gchar *lh;
410 gint lp;
411
412 do
413 {
414 gtk_tree_model_get(GTK_TREE_MODEL(cgu->store), iter, 0, &lh, 1, &lp, -1);
415 if(strcmp(lh, label) == 0)
416 {
417 GtkTreePath *path;
418
419 gtk_notebook_set_current_page(GTK_NOTEBOOK(cgu->nbook), lp);
420 path = gtk_tree_model_get_path(GTK_TREE_MODEL(cgu->store), iter);
421 gtk_tree_view_expand_to_path(GTK_TREE_VIEW(cgu->view), path);
422 gtk_tree_view_set_cursor(GTK_TREE_VIEW(cgu->view), path, NULL, FALSE);
423 gtk_tree_path_free(path);
424 return TRUE;
425 }
426 if(gtk_tree_model_iter_children(GTK_TREE_MODEL(cgu->store), &citer, iter))
427 {
428 if(goto_iterate(cgu, &citer, label))
429 return TRUE;
430 }
431 } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(cgu->store), iter));
432
433 return FALSE;
434 }
435
436 /* 2014-12-26 - Switch to a named configuration page. */
cfg_goto_page(const char * label)437 void cfg_goto_page(const char *label)
438 {
439 const CfgGui *cgu = &the_cfggui;
440 GtkTreeIter iter;
441
442 if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(cgu->store), &iter))
443 {
444 goto_iterate(cgu, &iter, label);
445 }
446 }
447
448 /* ----------------------------------------------------------------------------------------- */
449
cfg_tree_level_begin(const gchar * label)450 void cfg_tree_level_begin(const gchar *label)
451 {
452 CfgGui *cgu = &the_cfggui;
453
454 if(cgu->level + 1 < sizeof cgu->treeiter / sizeof *cgu->treeiter)
455 {
456 gtk_tree_store_append(cgu->store, &cgu->treeiter[cgu->level], cgu->level > 0 ? &cgu->treeiter[cgu->level - 1] : NULL);
457 gtk_tree_store_set(cgu->store, &cgu->treeiter[cgu->level], 0, label, -1);
458 cgu->level++;
459 }
460 }
461
cfg_tree_level_append(const gchar * label,GtkWidget * page)462 void cfg_tree_level_append(const gchar *label, GtkWidget *page)
463 {
464 CfgGui *cgu = &the_cfggui;
465
466 gtk_tree_store_append(cgu->store, &cgu->treeiter[cgu->level], &cgu->treeiter[cgu->level - 1]);
467 gtk_tree_store_set(cgu->store, &cgu->treeiter[cgu->level], 0, label, -1);
468
469 if(page != NULL)
470 {
471 gint pn = gtk_notebook_append_page(GTK_NOTEBOOK(the_cfggui.nbook), page, NULL);
472 gtk_tree_store_set(cgu->store, &cgu->treeiter[cgu->level], 1, pn, -1);
473 }
474 }
475
476 /* 2002-07-19 - Replace <old> tree widget with the <new> one. Handy for cases where config widgets
477 ** are seriously rebuilt during update(), such as in cfg_cmdcfg.c.
478 */
cfg_tree_level_replace(GtkWidget * old,GtkWidget * new)479 void cfg_tree_level_replace(GtkWidget *old, GtkWidget *new)
480 {
481 if(old)
482 {
483 gint pn = gtk_notebook_page_num(GTK_NOTEBOOK(the_cfggui.nbook), old);
484 gtk_container_remove(GTK_CONTAINER(the_cfggui.nbook), old);
485 gtk_notebook_insert_page(GTK_NOTEBOOK(the_cfggui.nbook), new, NULL, pn);
486 gtk_widget_show(new);
487 }
488 }
489
cfg_tree_level_end(void)490 void cfg_tree_level_end(void)
491 {
492 if(the_cfggui.level > 0)
493 the_cfggui.level--;
494 }
495
496 /* ----------------------------------------------------------------------------------------- */
497
load_node(const XmlNode * node,gpointer user)498 static void load_node(const XmlNode *node, gpointer user)
499 {
500 MainInfo *min = user;
501 guint i;
502
503 for(i = 0; i < CFG_PAGES; i++)
504 {
505 if(xml_node_has_name(node, cfg_page[i]->node) && cfg_page[i]->load != NULL)
506 {
507 cfg_page[i]->load(min, node);
508 return;
509 }
510 }
511 }
512
513 /* 1998-07-26 - Load the entire program configuration. Pretty complex stuff, made considerably
514 ** less so by the modularization and tree organization.
515 ** 1998-08-30 - Now keeps knowledge about config file name to itself. First checks if there
516 ** is a local config; if so, it is loaded. If not, the system-wide default
517 ** config from /etc/local/etc/ is used. If that fails, whine.
518 ** 1998-10-21 - Now returns a set of flags, rather than the single first boolean.
519 ** 1999-08-25 - Made the error dialog shown when no config is found a bit more informative.
520 */
cfg_load_config(MainInfo * min)521 guint32 cfg_load_config(MainInfo *min)
522 {
523 XmlNode *tree;
524 gchar name[PATH_MAX] = "";
525 const gchar *rcdir, *rcname;
526 guint32 i, flags = 0UL, bad_dir = 0;
527
528 /* Since we know this function is called during boot, ensure the user's config directory exists. */
529 rcdir = get_config_dirname();
530 if(*rcdir == '\0')
531 g_error("Failed to retrieve user's configuration directory, can't save configuration data");
532 else if(!fut_exists(rcdir))
533 g_mkdir_with_parents(rcdir, 0700);
534
535 /* Does the user seem to have a local config? */
536 rcname = get_config_filename(RCNAME);
537 if(!fut_can_read_named(rcname))
538 {
539 rcname = get_config_filename_old("." RCNAME); /* Nope, check old location and name. */
540 bad_dir = 1;
541 }
542 if(!fut_can_read_named(rcname))
543 {
544 g_snprintf(name, sizeof name, PATH_CFG G_DIR_SEPARATOR_S "%s", RCNAME); /* Nope, check for global one. */
545 rcname = name;
546 bad_dir = 1;
547 }
548
549 /* If loading from a "bad" (non-standard) directory, nag. */
550 if(bad_dir)
551 ndl_dialog_sync_new_wait(min, "rcpath", _("Configuration Path Notice"), _("Configuration was not loaded from the current default location.\nPress Save in the Configuration window to update."));
552
553 /* Initialize the cache of page descriptors. */
554 for(i = 0; i < CFG_PAGES; i++)
555 cfg_page[i] = describe_page[i](min);
556
557 if((tree = xml_tree_load(rcname)) != NULL)
558 {
559 const gchar *fver;
560
561 if(xml_get_text(tree, "version", &fver) && strcmp(fver, VERSION) != 0)
562 g_warning(_("Config file version (%s) doesn't match program version (%s)"), fver, VERSION);
563 xml_node_visit_children(tree, load_node, (gpointer) min);
564 xml_tree_destroy(tree);
565 }
566 else
567 {
568 const gchar *rcname, *oldrcname;
569 gchar syscfg[PATH_MAX], whine[3 * PATH_MAX];
570
571 rcname = get_config_filename(RCNAME);
572 oldrcname = get_config_filename_old("." RCNAME);
573 g_snprintf(syscfg, sizeof syscfg, PATH_CFG G_DIR_SEPARATOR_S "%s", RCNAME);
574 g_snprintf(whine, sizeof whine, _("Couldn't find any configuration file; checked:\n"
575 "\"%s\",\n\"%s\" and\n\"%s\".\n"
576 "Using built-in minimal configuration."),
577 rcname, oldrcname, syscfg);
578 dlg_dialog_async_new_error(whine);
579 flags |= CLDF_NONE_FOUND;
580 }
581 return flags;
582 }
583
584 /* ----------------------------------------------------------------------------------------- */
585
586 /* 1998-08-25 - Log a request to get something done. */
cfg_set_flags(guint32 flags)587 void cfg_set_flags(guint32 flags)
588 {
589 the_cfggui.flags |= flags;
590 }
591
592 /* 1999-04-09 - Set the 'configuration modified' flag. */
cfg_modified_set(MainInfo * min)593 void cfg_modified_set(MainInfo *min)
594 {
595 min->cfg.flags |= CFLG_CHANGED;
596 }
597
598 /* 1999-04-09 - Clear the 'configuration modified' flag. Use with care. */
cfg_modified_clear(MainInfo * min)599 void cfg_modified_clear(MainInfo *min)
600 {
601 min->cfg.flags &= ~CFLG_CHANGED;
602 }
603