1 /* Hey EMACS -*- linux-c -*- */
2 /*
3  *
4  * Copyright (C) 2002-2003 Romain Lievin <roms@tilp.info>
5  * Released under the terms of the GNU GPL v2.0.
6  *
7  */
8 
9 #ifdef HAVE_CONFIG_H
10 #  include <config.h>
11 #endif
12 
13 #include <stdlib.h>
14 #include "lkc.h"
15 #include "images.c"
16 
17 #include <glade/glade.h>
18 #include <gtk/gtk.h>
19 #include <glib.h>
20 #include <gdk/gdkkeysyms.h>
21 
22 #include <stdio.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <time.h>
26 
27 //#define DEBUG
28 
29 enum {
30 	SINGLE_VIEW, SPLIT_VIEW, FULL_VIEW
31 };
32 
33 enum {
34 	OPT_NORMAL, OPT_ALL, OPT_PROMPT
35 };
36 
37 static gint view_mode = FULL_VIEW;
38 static gboolean show_name = TRUE;
39 static gboolean show_range = TRUE;
40 static gboolean show_value = TRUE;
41 static gboolean resizeable = FALSE;
42 static int opt_mode = OPT_NORMAL;
43 
44 GtkWidget *main_wnd = NULL;
45 GtkWidget *tree1_w = NULL;	// left  frame
46 GtkWidget *tree2_w = NULL;	// right frame
47 GtkWidget *text_w = NULL;
48 GtkWidget *hpaned = NULL;
49 GtkWidget *vpaned = NULL;
50 GtkWidget *back_btn = NULL;
51 GtkWidget *save_btn = NULL;
52 GtkWidget *save_menu_item = NULL;
53 
54 GtkTextTag *tag1, *tag2;
55 GdkColor color;
56 
57 GtkTreeStore *tree1, *tree2, *tree;
58 GtkTreeModel *model1, *model2;
59 static GtkTreeIter *parents[256];
60 static gint indent;
61 
62 static struct menu *current; // current node for SINGLE view
63 static struct menu *browsed; // browsed node for SPLIT view
64 
65 enum {
66 	COL_OPTION, COL_NAME, COL_NO, COL_MOD, COL_YES, COL_VALUE,
67 	COL_MENU, COL_COLOR, COL_EDIT, COL_PIXBUF,
68 	COL_PIXVIS, COL_BTNVIS, COL_BTNACT, COL_BTNINC, COL_BTNRAD,
69 	COL_NUMBER
70 };
71 
72 static void display_list(void);
73 static void display_tree(struct menu *menu);
74 static void display_tree_part(void);
75 static void update_tree(struct menu *src, GtkTreeIter * dst);
76 static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row);
77 static gchar **fill_row(struct menu *menu);
78 static void conf_changed(void);
79 
80 /* Helping/Debugging Functions */
81 
dbg_sym_flags(int val)82 const char *dbg_sym_flags(int val)
83 {
84 	static char buf[256];
85 
86 	bzero(buf, 256);
87 
88 	if (val & SYMBOL_CONST)
89 		strcat(buf, "const/");
90 	if (val & SYMBOL_CHECK)
91 		strcat(buf, "check/");
92 	if (val & SYMBOL_CHOICE)
93 		strcat(buf, "choice/");
94 	if (val & SYMBOL_CHOICEVAL)
95 		strcat(buf, "choiceval/");
96 	if (val & SYMBOL_VALID)
97 		strcat(buf, "valid/");
98 	if (val & SYMBOL_OPTIONAL)
99 		strcat(buf, "optional/");
100 	if (val & SYMBOL_WRITE)
101 		strcat(buf, "write/");
102 	if (val & SYMBOL_CHANGED)
103 		strcat(buf, "changed/");
104 	if (val & SYMBOL_NO_WRITE)
105 		strcat(buf, "no_write/");
106 
107 	buf[strlen(buf) - 1] = '\0';
108 
109 	return buf;
110 }
111 
replace_button_icon(GladeXML * xml,GdkDrawable * window,GtkStyle * style,gchar * btn_name,gchar ** xpm)112 void replace_button_icon(GladeXML * xml, GdkDrawable * window,
113 			 GtkStyle * style, gchar * btn_name, gchar ** xpm)
114 {
115 	GdkPixmap *pixmap;
116 	GdkBitmap *mask;
117 	GtkToolButton *button;
118 	GtkWidget *image;
119 
120 	pixmap = gdk_pixmap_create_from_xpm_d(window, &mask,
121 					      &style->bg[GTK_STATE_NORMAL],
122 					      xpm);
123 
124 	button = GTK_TOOL_BUTTON(glade_xml_get_widget(xml, btn_name));
125 	image = gtk_image_new_from_pixmap(pixmap, mask);
126 	gtk_widget_show(image);
127 	gtk_tool_button_set_icon_widget(button, image);
128 }
129 
130 /* Main Window Initialization */
init_main_window(const gchar * glade_file)131 void init_main_window(const gchar * glade_file)
132 {
133 	GladeXML *xml;
134 	GtkWidget *widget;
135 	GtkTextBuffer *txtbuf;
136 	GtkStyle *style;
137 
138 	xml = glade_xml_new(glade_file, "window1", NULL);
139 	if (!xml)
140 		g_error("GUI loading failed !\n");
141 	glade_xml_signal_autoconnect(xml);
142 
143 	main_wnd = glade_xml_get_widget(xml, "window1");
144 	hpaned = glade_xml_get_widget(xml, "hpaned1");
145 	vpaned = glade_xml_get_widget(xml, "vpaned1");
146 	tree1_w = glade_xml_get_widget(xml, "treeview1");
147 	tree2_w = glade_xml_get_widget(xml, "treeview2");
148 	text_w = glade_xml_get_widget(xml, "textview3");
149 
150 	back_btn = glade_xml_get_widget(xml, "button1");
151 	gtk_widget_set_sensitive(back_btn, FALSE);
152 
153 	widget = glade_xml_get_widget(xml, "show_name1");
154 	gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget,
155 				       show_name);
156 
157 	widget = glade_xml_get_widget(xml, "show_range1");
158 	gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget,
159 				       show_range);
160 
161 	widget = glade_xml_get_widget(xml, "show_data1");
162 	gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget,
163 				       show_value);
164 
165 	save_btn = glade_xml_get_widget(xml, "button3");
166 	save_menu_item = glade_xml_get_widget(xml, "save1");
167 	conf_set_changed_callback(conf_changed);
168 
169 	style = gtk_widget_get_style(main_wnd);
170 	widget = glade_xml_get_widget(xml, "toolbar1");
171 
172 	replace_button_icon(xml, main_wnd->window, style,
173 			    "button4", (gchar **) xpm_single_view);
174 	replace_button_icon(xml, main_wnd->window, style,
175 			    "button5", (gchar **) xpm_split_view);
176 	replace_button_icon(xml, main_wnd->window, style,
177 			    "button6", (gchar **) xpm_tree_view);
178 
179 	txtbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w));
180 	tag1 = gtk_text_buffer_create_tag(txtbuf, "mytag1",
181 					  "foreground", "red",
182 					  "weight", PANGO_WEIGHT_BOLD,
183 					  NULL);
184 	tag2 = gtk_text_buffer_create_tag(txtbuf, "mytag2",
185 					  /*"style", PANGO_STYLE_OBLIQUE, */
186 					  NULL);
187 
188 	gtk_window_set_title(GTK_WINDOW(main_wnd), rootmenu.prompt->text);
189 
190 	gtk_widget_show(main_wnd);
191 }
192 
init_tree_model(void)193 void init_tree_model(void)
194 {
195 	gint i;
196 
197 	tree = tree2 = gtk_tree_store_new(COL_NUMBER,
198 					  G_TYPE_STRING, G_TYPE_STRING,
199 					  G_TYPE_STRING, G_TYPE_STRING,
200 					  G_TYPE_STRING, G_TYPE_STRING,
201 					  G_TYPE_POINTER, GDK_TYPE_COLOR,
202 					  G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF,
203 					  G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
204 					  G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
205 					  G_TYPE_BOOLEAN);
206 	model2 = GTK_TREE_MODEL(tree2);
207 
208 	for (parents[0] = NULL, i = 1; i < 256; i++)
209 		parents[i] = (GtkTreeIter *) g_malloc(sizeof(GtkTreeIter));
210 
211 	tree1 = gtk_tree_store_new(COL_NUMBER,
212 				   G_TYPE_STRING, G_TYPE_STRING,
213 				   G_TYPE_STRING, G_TYPE_STRING,
214 				   G_TYPE_STRING, G_TYPE_STRING,
215 				   G_TYPE_POINTER, GDK_TYPE_COLOR,
216 				   G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF,
217 				   G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
218 				   G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
219 				   G_TYPE_BOOLEAN);
220 	model1 = GTK_TREE_MODEL(tree1);
221 }
222 
init_left_tree(void)223 void init_left_tree(void)
224 {
225 	GtkTreeView *view = GTK_TREE_VIEW(tree1_w);
226 	GtkCellRenderer *renderer;
227 	GtkTreeSelection *sel;
228 	GtkTreeViewColumn *column;
229 
230 	gtk_tree_view_set_model(view, model1);
231 	gtk_tree_view_set_headers_visible(view, TRUE);
232 	gtk_tree_view_set_rules_hint(view, TRUE);
233 
234 	column = gtk_tree_view_column_new();
235 	gtk_tree_view_append_column(view, column);
236 	gtk_tree_view_column_set_title(column, "Options");
237 
238 	renderer = gtk_cell_renderer_toggle_new();
239 	gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
240 					renderer, FALSE);
241 	gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
242 					    renderer,
243 					    "active", COL_BTNACT,
244 					    "inconsistent", COL_BTNINC,
245 					    "visible", COL_BTNVIS,
246 					    "radio", COL_BTNRAD, NULL);
247 	renderer = gtk_cell_renderer_text_new();
248 	gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
249 					renderer, FALSE);
250 	gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
251 					    renderer,
252 					    "text", COL_OPTION,
253 					    "foreground-gdk",
254 					    COL_COLOR, NULL);
255 
256 	sel = gtk_tree_view_get_selection(view);
257 	gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
258 	gtk_widget_realize(tree1_w);
259 }
260 
261 static void renderer_edited(GtkCellRendererText * cell,
262 			    const gchar * path_string,
263 			    const gchar * new_text, gpointer user_data);
264 
init_right_tree(void)265 void init_right_tree(void)
266 {
267 	GtkTreeView *view = GTK_TREE_VIEW(tree2_w);
268 	GtkCellRenderer *renderer;
269 	GtkTreeSelection *sel;
270 	GtkTreeViewColumn *column;
271 	gint i;
272 
273 	gtk_tree_view_set_model(view, model2);
274 	gtk_tree_view_set_headers_visible(view, TRUE);
275 	gtk_tree_view_set_rules_hint(view, TRUE);
276 
277 	column = gtk_tree_view_column_new();
278 	gtk_tree_view_append_column(view, column);
279 	gtk_tree_view_column_set_title(column, "Options");
280 
281 	renderer = gtk_cell_renderer_pixbuf_new();
282 	gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
283 					renderer, FALSE);
284 	gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
285 					    renderer,
286 					    "pixbuf", COL_PIXBUF,
287 					    "visible", COL_PIXVIS, NULL);
288 	renderer = gtk_cell_renderer_toggle_new();
289 	gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
290 					renderer, FALSE);
291 	gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
292 					    renderer,
293 					    "active", COL_BTNACT,
294 					    "inconsistent", COL_BTNINC,
295 					    "visible", COL_BTNVIS,
296 					    "radio", COL_BTNRAD, NULL);
297 	renderer = gtk_cell_renderer_text_new();
298 	gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
299 					renderer, FALSE);
300 	gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
301 					    renderer,
302 					    "text", COL_OPTION,
303 					    "foreground-gdk",
304 					    COL_COLOR, NULL);
305 
306 	renderer = gtk_cell_renderer_text_new();
307 	gtk_tree_view_insert_column_with_attributes(view, -1,
308 						    "Name", renderer,
309 						    "text", COL_NAME,
310 						    "foreground-gdk",
311 						    COL_COLOR, NULL);
312 	renderer = gtk_cell_renderer_text_new();
313 	gtk_tree_view_insert_column_with_attributes(view, -1,
314 						    "N", renderer,
315 						    "text", COL_NO,
316 						    "foreground-gdk",
317 						    COL_COLOR, NULL);
318 	renderer = gtk_cell_renderer_text_new();
319 	gtk_tree_view_insert_column_with_attributes(view, -1,
320 						    "M", renderer,
321 						    "text", COL_MOD,
322 						    "foreground-gdk",
323 						    COL_COLOR, NULL);
324 	renderer = gtk_cell_renderer_text_new();
325 	gtk_tree_view_insert_column_with_attributes(view, -1,
326 						    "Y", renderer,
327 						    "text", COL_YES,
328 						    "foreground-gdk",
329 						    COL_COLOR, NULL);
330 	renderer = gtk_cell_renderer_text_new();
331 	gtk_tree_view_insert_column_with_attributes(view, -1,
332 						    "Value", renderer,
333 						    "text", COL_VALUE,
334 						    "editable",
335 						    COL_EDIT,
336 						    "foreground-gdk",
337 						    COL_COLOR, NULL);
338 	g_signal_connect(G_OBJECT(renderer), "edited",
339 			 G_CALLBACK(renderer_edited), NULL);
340 
341 	column = gtk_tree_view_get_column(view, COL_NAME);
342 	gtk_tree_view_column_set_visible(column, show_name);
343 	column = gtk_tree_view_get_column(view, COL_NO);
344 	gtk_tree_view_column_set_visible(column, show_range);
345 	column = gtk_tree_view_get_column(view, COL_MOD);
346 	gtk_tree_view_column_set_visible(column, show_range);
347 	column = gtk_tree_view_get_column(view, COL_YES);
348 	gtk_tree_view_column_set_visible(column, show_range);
349 	column = gtk_tree_view_get_column(view, COL_VALUE);
350 	gtk_tree_view_column_set_visible(column, show_value);
351 
352 	if (resizeable) {
353 		for (i = 0; i < COL_VALUE; i++) {
354 			column = gtk_tree_view_get_column(view, i);
355 			gtk_tree_view_column_set_resizable(column, TRUE);
356 		}
357 	}
358 
359 	sel = gtk_tree_view_get_selection(view);
360 	gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
361 }
362 
363 /* Utility Functions */
364 
text_insert_help(struct menu * menu)365 static void text_insert_help(struct menu *menu)
366 {
367 	GtkTextBuffer *buffer;
368 	GtkTextIter start, end;
369 	const char *prompt = menu_get_prompt(menu);
370 	struct gstr help = str_new();
371 
372 	menu_get_ext_help(menu, &help);
373 
374 	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w));
375 	gtk_text_buffer_get_bounds(buffer, &start, &end);
376 	gtk_text_buffer_delete(buffer, &start, &end);
377 	gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w), 15);
378 
379 	gtk_text_buffer_get_end_iter(buffer, &end);
380 	gtk_text_buffer_insert_with_tags(buffer, &end, prompt, -1, tag1,
381 					 NULL);
382 	gtk_text_buffer_insert_at_cursor(buffer, "\n\n", 2);
383 	gtk_text_buffer_get_end_iter(buffer, &end);
384 	gtk_text_buffer_insert_with_tags(buffer, &end, str_get(&help), -1, tag2,
385 					 NULL);
386 	str_free(&help);
387 }
388 
text_insert_msg(const char * title,const char * message)389 static void text_insert_msg(const char *title, const char *message)
390 {
391 	GtkTextBuffer *buffer;
392 	GtkTextIter start, end;
393 	const char *msg = message;
394 
395 	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w));
396 	gtk_text_buffer_get_bounds(buffer, &start, &end);
397 	gtk_text_buffer_delete(buffer, &start, &end);
398 	gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w), 15);
399 
400 	gtk_text_buffer_get_end_iter(buffer, &end);
401 	gtk_text_buffer_insert_with_tags(buffer, &end, title, -1, tag1,
402 					 NULL);
403 	gtk_text_buffer_insert_at_cursor(buffer, "\n\n", 2);
404 	gtk_text_buffer_get_end_iter(buffer, &end);
405 	gtk_text_buffer_insert_with_tags(buffer, &end, msg, -1, tag2,
406 					 NULL);
407 }
408 
409 /* Main Windows Callbacks */
410 
411 void on_save_activate(GtkMenuItem * menuitem, gpointer user_data);
on_window1_delete_event(GtkWidget * widget,GdkEvent * event,gpointer user_data)412 gboolean on_window1_delete_event(GtkWidget * widget, GdkEvent * event,
413 				 gpointer user_data)
414 {
415 	GtkWidget *dialog, *label;
416 	gint result;
417 
418 	if (!conf_get_changed())
419 		return FALSE;
420 
421 	dialog = gtk_dialog_new_with_buttons("Warning !",
422 					     GTK_WINDOW(main_wnd),
423 					     (GtkDialogFlags)
424 					     (GTK_DIALOG_MODAL |
425 					      GTK_DIALOG_DESTROY_WITH_PARENT),
426 					     GTK_STOCK_OK,
427 					     GTK_RESPONSE_YES,
428 					     GTK_STOCK_NO,
429 					     GTK_RESPONSE_NO,
430 					     GTK_STOCK_CANCEL,
431 					     GTK_RESPONSE_CANCEL, NULL);
432 	gtk_dialog_set_default_response(GTK_DIALOG(dialog),
433 					GTK_RESPONSE_CANCEL);
434 
435 	label = gtk_label_new("\nSave configuration ?\n");
436 	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label);
437 	gtk_widget_show(label);
438 
439 	result = gtk_dialog_run(GTK_DIALOG(dialog));
440 	switch (result) {
441 	case GTK_RESPONSE_YES:
442 		on_save_activate(NULL, NULL);
443 		return FALSE;
444 	case GTK_RESPONSE_NO:
445 		return FALSE;
446 	case GTK_RESPONSE_CANCEL:
447 	case GTK_RESPONSE_DELETE_EVENT:
448 	default:
449 		gtk_widget_destroy(dialog);
450 		return TRUE;
451 	}
452 
453 	return FALSE;
454 }
455 
on_window1_destroy(GtkObject * object,gpointer user_data)456 void on_window1_destroy(GtkObject * object, gpointer user_data)
457 {
458 	gtk_main_quit();
459 }
460 
461 void
on_window1_size_request(GtkWidget * widget,GtkRequisition * requisition,gpointer user_data)462 on_window1_size_request(GtkWidget * widget,
463 			GtkRequisition * requisition, gpointer user_data)
464 {
465 	static gint old_h;
466 	gint w, h;
467 
468 	if (widget->window == NULL)
469 		gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h);
470 	else
471 		gdk_window_get_size(widget->window, &w, &h);
472 
473 	if (h == old_h)
474 		return;
475 	old_h = h;
476 
477 	gtk_paned_set_position(GTK_PANED(vpaned), 2 * h / 3);
478 }
479 
480 /* Menu & Toolbar Callbacks */
481 
482 static void
load_filename(GtkFileSelection * file_selector,gpointer user_data)483 load_filename(GtkFileSelection * file_selector, gpointer user_data)
484 {
485 	const gchar *fn;
486 
487 	fn = gtk_file_selection_get_filename(GTK_FILE_SELECTION
488 					     (user_data));
489 
490 	if (conf_read(fn))
491 		text_insert_msg("Error", "Unable to load configuration !");
492 	else
493 		display_tree(&rootmenu);
494 }
495 
on_load1_activate(GtkMenuItem * menuitem,gpointer user_data)496 void on_load1_activate(GtkMenuItem * menuitem, gpointer user_data)
497 {
498 	GtkWidget *fs;
499 
500 	fs = gtk_file_selection_new("Load file...");
501 	g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button),
502 			 "clicked",
503 			 G_CALLBACK(load_filename), (gpointer) fs);
504 	g_signal_connect_swapped(GTK_OBJECT
505 				 (GTK_FILE_SELECTION(fs)->ok_button),
506 				 "clicked", G_CALLBACK(gtk_widget_destroy),
507 				 (gpointer) fs);
508 	g_signal_connect_swapped(GTK_OBJECT
509 				 (GTK_FILE_SELECTION(fs)->cancel_button),
510 				 "clicked", G_CALLBACK(gtk_widget_destroy),
511 				 (gpointer) fs);
512 	gtk_widget_show(fs);
513 }
514 
on_save_activate(GtkMenuItem * menuitem,gpointer user_data)515 void on_save_activate(GtkMenuItem * menuitem, gpointer user_data)
516 {
517 	if (conf_write(NULL))
518 		text_insert_msg("Error", "Unable to save configuration !");
519 }
520 
521 static void
store_filename(GtkFileSelection * file_selector,gpointer user_data)522 store_filename(GtkFileSelection * file_selector, gpointer user_data)
523 {
524 	const gchar *fn;
525 
526 	fn = gtk_file_selection_get_filename(GTK_FILE_SELECTION
527 					     (user_data));
528 
529 	if (conf_write(fn))
530 		text_insert_msg("Error", "Unable to save configuration !");
531 
532 	gtk_widget_destroy(GTK_WIDGET(user_data));
533 }
534 
on_save_as1_activate(GtkMenuItem * menuitem,gpointer user_data)535 void on_save_as1_activate(GtkMenuItem * menuitem, gpointer user_data)
536 {
537 	GtkWidget *fs;
538 
539 	fs = gtk_file_selection_new("Save file as...");
540 	g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button),
541 			 "clicked",
542 			 G_CALLBACK(store_filename), (gpointer) fs);
543 	g_signal_connect_swapped(GTK_OBJECT
544 				 (GTK_FILE_SELECTION(fs)->ok_button),
545 				 "clicked", G_CALLBACK(gtk_widget_destroy),
546 				 (gpointer) fs);
547 	g_signal_connect_swapped(GTK_OBJECT
548 				 (GTK_FILE_SELECTION(fs)->cancel_button),
549 				 "clicked", G_CALLBACK(gtk_widget_destroy),
550 				 (gpointer) fs);
551 	gtk_widget_show(fs);
552 }
553 
on_quit1_activate(GtkMenuItem * menuitem,gpointer user_data)554 void on_quit1_activate(GtkMenuItem * menuitem, gpointer user_data)
555 {
556 	if (!on_window1_delete_event(NULL, NULL, NULL))
557 		gtk_widget_destroy(GTK_WIDGET(main_wnd));
558 }
559 
on_show_name1_activate(GtkMenuItem * menuitem,gpointer user_data)560 void on_show_name1_activate(GtkMenuItem * menuitem, gpointer user_data)
561 {
562 	GtkTreeViewColumn *col;
563 
564 	show_name = GTK_CHECK_MENU_ITEM(menuitem)->active;
565 	col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_NAME);
566 	if (col)
567 		gtk_tree_view_column_set_visible(col, show_name);
568 }
569 
on_show_range1_activate(GtkMenuItem * menuitem,gpointer user_data)570 void on_show_range1_activate(GtkMenuItem * menuitem, gpointer user_data)
571 {
572 	GtkTreeViewColumn *col;
573 
574 	show_range = GTK_CHECK_MENU_ITEM(menuitem)->active;
575 	col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_NO);
576 	if (col)
577 		gtk_tree_view_column_set_visible(col, show_range);
578 	col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_MOD);
579 	if (col)
580 		gtk_tree_view_column_set_visible(col, show_range);
581 	col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_YES);
582 	if (col)
583 		gtk_tree_view_column_set_visible(col, show_range);
584 
585 }
586 
on_show_data1_activate(GtkMenuItem * menuitem,gpointer user_data)587 void on_show_data1_activate(GtkMenuItem * menuitem, gpointer user_data)
588 {
589 	GtkTreeViewColumn *col;
590 
591 	show_value = GTK_CHECK_MENU_ITEM(menuitem)->active;
592 	col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_VALUE);
593 	if (col)
594 		gtk_tree_view_column_set_visible(col, show_value);
595 }
596 
597 void
on_set_option_mode1_activate(GtkMenuItem * menuitem,gpointer user_data)598 on_set_option_mode1_activate(GtkMenuItem *menuitem, gpointer user_data)
599 {
600 	opt_mode = OPT_NORMAL;
601 	gtk_tree_store_clear(tree2);
602 	display_tree(&rootmenu);	/* instead of update_tree to speed-up */
603 }
604 
605 void
on_set_option_mode2_activate(GtkMenuItem * menuitem,gpointer user_data)606 on_set_option_mode2_activate(GtkMenuItem *menuitem, gpointer user_data)
607 {
608 	opt_mode = OPT_ALL;
609 	gtk_tree_store_clear(tree2);
610 	display_tree(&rootmenu);	/* instead of update_tree to speed-up */
611 }
612 
613 void
on_set_option_mode3_activate(GtkMenuItem * menuitem,gpointer user_data)614 on_set_option_mode3_activate(GtkMenuItem *menuitem, gpointer user_data)
615 {
616 	opt_mode = OPT_PROMPT;
617 	gtk_tree_store_clear(tree2);
618 	display_tree(&rootmenu);	/* instead of update_tree to speed-up */
619 }
620 
on_introduction1_activate(GtkMenuItem * menuitem,gpointer user_data)621 void on_introduction1_activate(GtkMenuItem * menuitem, gpointer user_data)
622 {
623 	GtkWidget *dialog;
624 	const gchar *intro_text =
625 	    "Welcome to gkc, the GTK+ graphical configuration tool\n"
626 	    "For each option, a blank box indicates the feature is disabled, a\n"
627 	    "check indicates it is enabled, and a dot indicates that it is to\n"
628 	    "be compiled as a module.  Clicking on the box will cycle through the three states.\n"
629 	    "\n"
630 	    "If you do not see an option (e.g., a device driver) that you\n"
631 	    "believe should be present, try turning on Show All Options\n"
632 	    "under the Options menu.\n"
633 	    "Although there is no cross reference yet to help you figure out\n"
634 	    "what other options must be enabled to support the option you\n"
635 	    "are interested in, you can still view the help of a grayed-out\n"
636 	    "option.\n"
637 	    "\n"
638 	    "Toggling Show Debug Info under the Options menu will show \n"
639 	    "the dependencies, which you can then match by examining other options.";
640 
641 	dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd),
642 					GTK_DIALOG_DESTROY_WITH_PARENT,
643 					GTK_MESSAGE_INFO,
644 					GTK_BUTTONS_CLOSE, "%s", intro_text);
645 	g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
646 				 G_CALLBACK(gtk_widget_destroy),
647 				 GTK_OBJECT(dialog));
648 	gtk_widget_show_all(dialog);
649 }
650 
on_about1_activate(GtkMenuItem * menuitem,gpointer user_data)651 void on_about1_activate(GtkMenuItem * menuitem, gpointer user_data)
652 {
653 	GtkWidget *dialog;
654 	const gchar *about_text =
655 	    "gkc is copyright (c) 2002 Romain Lievin <roms@lpg.ticalc.org>.\n"
656 	      "Based on the source code from Roman Zippel.\n";
657 
658 	dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd),
659 					GTK_DIALOG_DESTROY_WITH_PARENT,
660 					GTK_MESSAGE_INFO,
661 					GTK_BUTTONS_CLOSE, "%s", about_text);
662 	g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
663 				 G_CALLBACK(gtk_widget_destroy),
664 				 GTK_OBJECT(dialog));
665 	gtk_widget_show_all(dialog);
666 }
667 
on_license1_activate(GtkMenuItem * menuitem,gpointer user_data)668 void on_license1_activate(GtkMenuItem * menuitem, gpointer user_data)
669 {
670 	GtkWidget *dialog;
671 	const gchar *license_text =
672 	    "gkc is released under the terms of the GNU GPL v2.\n"
673 	      "For more information, please see the source code or\n"
674 	      "visit http://www.fsf.org/licenses/licenses.html\n";
675 
676 	dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd),
677 					GTK_DIALOG_DESTROY_WITH_PARENT,
678 					GTK_MESSAGE_INFO,
679 					GTK_BUTTONS_CLOSE, "%s", license_text);
680 	g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
681 				 G_CALLBACK(gtk_widget_destroy),
682 				 GTK_OBJECT(dialog));
683 	gtk_widget_show_all(dialog);
684 }
685 
on_back_clicked(GtkButton * button,gpointer user_data)686 void on_back_clicked(GtkButton * button, gpointer user_data)
687 {
688 	enum prop_type ptype;
689 
690 	current = current->parent;
691 	ptype = current->prompt ? current->prompt->type : P_UNKNOWN;
692 	if (ptype != P_MENU)
693 		current = current->parent;
694 	display_tree_part();
695 
696 	if (current == &rootmenu)
697 		gtk_widget_set_sensitive(back_btn, FALSE);
698 }
699 
on_load_clicked(GtkButton * button,gpointer user_data)700 void on_load_clicked(GtkButton * button, gpointer user_data)
701 {
702 	on_load1_activate(NULL, user_data);
703 }
704 
on_single_clicked(GtkButton * button,gpointer user_data)705 void on_single_clicked(GtkButton * button, gpointer user_data)
706 {
707 	view_mode = SINGLE_VIEW;
708 	gtk_widget_hide(tree1_w);
709 	current = &rootmenu;
710 	display_tree_part();
711 }
712 
on_split_clicked(GtkButton * button,gpointer user_data)713 void on_split_clicked(GtkButton * button, gpointer user_data)
714 {
715 	gint w, h;
716 	view_mode = SPLIT_VIEW;
717 	gtk_widget_show(tree1_w);
718 	gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h);
719 	gtk_paned_set_position(GTK_PANED(hpaned), w / 2);
720 	if (tree2)
721 		gtk_tree_store_clear(tree2);
722 	display_list();
723 
724 	/* Disable back btn, like in full mode. */
725 	gtk_widget_set_sensitive(back_btn, FALSE);
726 }
727 
on_full_clicked(GtkButton * button,gpointer user_data)728 void on_full_clicked(GtkButton * button, gpointer user_data)
729 {
730 	view_mode = FULL_VIEW;
731 	gtk_widget_hide(tree1_w);
732 	if (tree2)
733 		gtk_tree_store_clear(tree2);
734 	display_tree(&rootmenu);
735 	gtk_widget_set_sensitive(back_btn, FALSE);
736 }
737 
on_collapse_clicked(GtkButton * button,gpointer user_data)738 void on_collapse_clicked(GtkButton * button, gpointer user_data)
739 {
740 	gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree2_w));
741 }
742 
on_expand_clicked(GtkButton * button,gpointer user_data)743 void on_expand_clicked(GtkButton * button, gpointer user_data)
744 {
745 	gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w));
746 }
747 
748 /* CTree Callbacks */
749 
750 /* Change hex/int/string value in the cell */
renderer_edited(GtkCellRendererText * cell,const gchar * path_string,const gchar * new_text,gpointer user_data)751 static void renderer_edited(GtkCellRendererText * cell,
752 			    const gchar * path_string,
753 			    const gchar * new_text, gpointer user_data)
754 {
755 	GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
756 	GtkTreeIter iter;
757 	const char *old_def, *new_def;
758 	struct menu *menu;
759 	struct symbol *sym;
760 
761 	if (!gtk_tree_model_get_iter(model2, &iter, path))
762 		return;
763 
764 	gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
765 	sym = menu->sym;
766 
767 	gtk_tree_model_get(model2, &iter, COL_VALUE, &old_def, -1);
768 	new_def = new_text;
769 
770 	sym_set_string_value(sym, new_def);
771 
772 	update_tree(&rootmenu, NULL);
773 
774 	gtk_tree_path_free(path);
775 }
776 
777 /* Change the value of a symbol and update the tree */
change_sym_value(struct menu * menu,gint col)778 static void change_sym_value(struct menu *menu, gint col)
779 {
780 	struct symbol *sym = menu->sym;
781 	tristate newval;
782 
783 	if (!sym)
784 		return;
785 
786 	if (col == COL_NO)
787 		newval = no;
788 	else if (col == COL_MOD)
789 		newval = mod;
790 	else if (col == COL_YES)
791 		newval = yes;
792 	else
793 		return;
794 
795 	switch (sym_get_type(sym)) {
796 	case S_BOOLEAN:
797 	case S_TRISTATE:
798 		if (!sym_tristate_within_range(sym, newval))
799 			newval = yes;
800 		sym_set_tristate_value(sym, newval);
801 		if (view_mode == FULL_VIEW)
802 			update_tree(&rootmenu, NULL);
803 		else if (view_mode == SPLIT_VIEW) {
804 			update_tree(browsed, NULL);
805 			display_list();
806 		}
807 		else if (view_mode == SINGLE_VIEW)
808 			display_tree_part();	//fixme: keep exp/coll
809 		break;
810 	case S_INT:
811 	case S_HEX:
812 	case S_STRING:
813 	default:
814 		break;
815 	}
816 }
817 
toggle_sym_value(struct menu * menu)818 static void toggle_sym_value(struct menu *menu)
819 {
820 	if (!menu->sym)
821 		return;
822 
823 	sym_toggle_tristate_value(menu->sym);
824 	if (view_mode == FULL_VIEW)
825 		update_tree(&rootmenu, NULL);
826 	else if (view_mode == SPLIT_VIEW) {
827 		update_tree(browsed, NULL);
828 		display_list();
829 	}
830 	else if (view_mode == SINGLE_VIEW)
831 		display_tree_part();	//fixme: keep exp/coll
832 }
833 
column2index(GtkTreeViewColumn * column)834 static gint column2index(GtkTreeViewColumn * column)
835 {
836 	gint i;
837 
838 	for (i = 0; i < COL_NUMBER; i++) {
839 		GtkTreeViewColumn *col;
840 
841 		col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), i);
842 		if (col == column)
843 			return i;
844 	}
845 
846 	return -1;
847 }
848 
849 /* User click: update choice (full) or goes down (single) */
850 gboolean
on_treeview2_button_press_event(GtkWidget * widget,GdkEventButton * event,gpointer user_data)851 on_treeview2_button_press_event(GtkWidget * widget,
852 				GdkEventButton * event, gpointer user_data)
853 {
854 	GtkTreeView *view = GTK_TREE_VIEW(widget);
855 	GtkTreePath *path;
856 	GtkTreeViewColumn *column;
857 	GtkTreeIter iter;
858 	struct menu *menu;
859 	gint col;
860 
861 #if GTK_CHECK_VERSION(2,1,4) // bug in ctree with earlier version of GTK
862 	gint tx = (gint) event->x;
863 	gint ty = (gint) event->y;
864 	gint cx, cy;
865 
866 	gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx,
867 				      &cy);
868 #else
869 	gtk_tree_view_get_cursor(view, &path, &column);
870 #endif
871 	if (path == NULL)
872 		return FALSE;
873 
874 	if (!gtk_tree_model_get_iter(model2, &iter, path))
875 		return FALSE;
876 	gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
877 
878 	col = column2index(column);
879 	if (event->type == GDK_2BUTTON_PRESS) {
880 		enum prop_type ptype;
881 		ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
882 
883 		if (ptype == P_MENU && view_mode != FULL_VIEW && col == COL_OPTION) {
884 			// goes down into menu
885 			current = menu;
886 			display_tree_part();
887 			gtk_widget_set_sensitive(back_btn, TRUE);
888 		} else if (col == COL_OPTION) {
889 			toggle_sym_value(menu);
890 			gtk_tree_view_expand_row(view, path, TRUE);
891 		}
892 	} else {
893 		if (col == COL_VALUE) {
894 			toggle_sym_value(menu);
895 			gtk_tree_view_expand_row(view, path, TRUE);
896 		} else if (col == COL_NO || col == COL_MOD
897 			   || col == COL_YES) {
898 			change_sym_value(menu, col);
899 			gtk_tree_view_expand_row(view, path, TRUE);
900 		}
901 	}
902 
903 	return FALSE;
904 }
905 
906 /* Key pressed: update choice */
907 gboolean
on_treeview2_key_press_event(GtkWidget * widget,GdkEventKey * event,gpointer user_data)908 on_treeview2_key_press_event(GtkWidget * widget,
909 			     GdkEventKey * event, gpointer user_data)
910 {
911 	GtkTreeView *view = GTK_TREE_VIEW(widget);
912 	GtkTreePath *path;
913 	GtkTreeViewColumn *column;
914 	GtkTreeIter iter;
915 	struct menu *menu;
916 	gint col;
917 
918 	gtk_tree_view_get_cursor(view, &path, &column);
919 	if (path == NULL)
920 		return FALSE;
921 
922 	if (event->keyval == GDK_space) {
923 		if (gtk_tree_view_row_expanded(view, path))
924 			gtk_tree_view_collapse_row(view, path);
925 		else
926 			gtk_tree_view_expand_row(view, path, FALSE);
927 		return TRUE;
928 	}
929 	if (event->keyval == GDK_KP_Enter) {
930 	}
931 	if (widget == tree1_w)
932 		return FALSE;
933 
934 	gtk_tree_model_get_iter(model2, &iter, path);
935 	gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
936 
937 	if (!strcasecmp(event->string, "n"))
938 		col = COL_NO;
939 	else if (!strcasecmp(event->string, "m"))
940 		col = COL_MOD;
941 	else if (!strcasecmp(event->string, "y"))
942 		col = COL_YES;
943 	else
944 		col = -1;
945 	change_sym_value(menu, col);
946 
947 	return FALSE;
948 }
949 
950 /* Row selection changed: update help */
951 void
on_treeview2_cursor_changed(GtkTreeView * treeview,gpointer user_data)952 on_treeview2_cursor_changed(GtkTreeView * treeview, gpointer user_data)
953 {
954 	GtkTreeSelection *selection;
955 	GtkTreeIter iter;
956 	struct menu *menu;
957 
958 	selection = gtk_tree_view_get_selection(treeview);
959 	if (gtk_tree_selection_get_selected(selection, &model2, &iter)) {
960 		gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
961 		text_insert_help(menu);
962 	}
963 }
964 
965 /* User click: display sub-tree in the right frame. */
966 gboolean
on_treeview1_button_press_event(GtkWidget * widget,GdkEventButton * event,gpointer user_data)967 on_treeview1_button_press_event(GtkWidget * widget,
968 				GdkEventButton * event, gpointer user_data)
969 {
970 	GtkTreeView *view = GTK_TREE_VIEW(widget);
971 	GtkTreePath *path;
972 	GtkTreeViewColumn *column;
973 	GtkTreeIter iter;
974 	struct menu *menu;
975 
976 	gint tx = (gint) event->x;
977 	gint ty = (gint) event->y;
978 	gint cx, cy;
979 
980 	gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx,
981 				      &cy);
982 	if (path == NULL)
983 		return FALSE;
984 
985 	gtk_tree_model_get_iter(model1, &iter, path);
986 	gtk_tree_model_get(model1, &iter, COL_MENU, &menu, -1);
987 
988 	if (event->type == GDK_2BUTTON_PRESS) {
989 		toggle_sym_value(menu);
990 		current = menu;
991 		display_tree_part();
992 	} else {
993 		browsed = menu;
994 		display_tree_part();
995 	}
996 
997 	gtk_widget_realize(tree2_w);
998 	gtk_tree_view_set_cursor(view, path, NULL, FALSE);
999 	gtk_widget_grab_focus(tree2_w);
1000 
1001 	return FALSE;
1002 }
1003 
1004 /* Fill a row of strings */
fill_row(struct menu * menu)1005 static gchar **fill_row(struct menu *menu)
1006 {
1007 	static gchar *row[COL_NUMBER];
1008 	struct symbol *sym = menu->sym;
1009 	const char *def;
1010 	int stype;
1011 	tristate val;
1012 	enum prop_type ptype;
1013 	int i;
1014 
1015 	for (i = COL_OPTION; i <= COL_COLOR; i++)
1016 		g_free(row[i]);
1017 	bzero(row, sizeof(row));
1018 
1019 	row[COL_OPTION] =
1020 	    g_strdup_printf("%s %s", menu_get_prompt(menu),
1021 			    sym && !sym_has_value(sym) ? "(NEW)" : "");
1022 
1023 	if (opt_mode == OPT_ALL && !menu_is_visible(menu))
1024 		row[COL_COLOR] = g_strdup("DarkGray");
1025 	else if (opt_mode == OPT_PROMPT &&
1026 			menu_has_prompt(menu) && !menu_is_visible(menu))
1027 		row[COL_COLOR] = g_strdup("DarkGray");
1028 	else
1029 		row[COL_COLOR] = g_strdup("Black");
1030 
1031 	ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
1032 	switch (ptype) {
1033 	case P_MENU:
1034 		row[COL_PIXBUF] = (gchar *) xpm_menu;
1035 		if (view_mode == SINGLE_VIEW)
1036 			row[COL_PIXVIS] = GINT_TO_POINTER(TRUE);
1037 		row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
1038 		break;
1039 	case P_COMMENT:
1040 		row[COL_PIXBUF] = (gchar *) xpm_void;
1041 		row[COL_PIXVIS] = GINT_TO_POINTER(FALSE);
1042 		row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
1043 		break;
1044 	default:
1045 		row[COL_PIXBUF] = (gchar *) xpm_void;
1046 		row[COL_PIXVIS] = GINT_TO_POINTER(FALSE);
1047 		row[COL_BTNVIS] = GINT_TO_POINTER(TRUE);
1048 		break;
1049 	}
1050 
1051 	if (!sym)
1052 		return row;
1053 	row[COL_NAME] = g_strdup(sym->name);
1054 
1055 	sym_calc_value(sym);
1056 	sym->flags &= ~SYMBOL_CHANGED;
1057 
1058 	if (sym_is_choice(sym)) {	// parse childs for getting final value
1059 		struct menu *child;
1060 		struct symbol *def_sym = sym_get_choice_value(sym);
1061 		struct menu *def_menu = NULL;
1062 
1063 		row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
1064 
1065 		for (child = menu->list; child; child = child->next) {
1066 			if (menu_is_visible(child)
1067 			    && child->sym == def_sym)
1068 				def_menu = child;
1069 		}
1070 
1071 		if (def_menu)
1072 			row[COL_VALUE] =
1073 			    g_strdup(menu_get_prompt(def_menu));
1074 	}
1075 	if (sym->flags & SYMBOL_CHOICEVAL)
1076 		row[COL_BTNRAD] = GINT_TO_POINTER(TRUE);
1077 
1078 	stype = sym_get_type(sym);
1079 	switch (stype) {
1080 	case S_BOOLEAN:
1081 		if (GPOINTER_TO_INT(row[COL_PIXVIS]) == FALSE)
1082 			row[COL_BTNVIS] = GINT_TO_POINTER(TRUE);
1083 		if (sym_is_choice(sym))
1084 			break;
1085 		/* fall through */
1086 	case S_TRISTATE:
1087 		val = sym_get_tristate_value(sym);
1088 		switch (val) {
1089 		case no:
1090 			row[COL_NO] = g_strdup("N");
1091 			row[COL_VALUE] = g_strdup("N");
1092 			row[COL_BTNACT] = GINT_TO_POINTER(FALSE);
1093 			row[COL_BTNINC] = GINT_TO_POINTER(FALSE);
1094 			break;
1095 		case mod:
1096 			row[COL_MOD] = g_strdup("M");
1097 			row[COL_VALUE] = g_strdup("M");
1098 			row[COL_BTNINC] = GINT_TO_POINTER(TRUE);
1099 			break;
1100 		case yes:
1101 			row[COL_YES] = g_strdup("Y");
1102 			row[COL_VALUE] = g_strdup("Y");
1103 			row[COL_BTNACT] = GINT_TO_POINTER(TRUE);
1104 			row[COL_BTNINC] = GINT_TO_POINTER(FALSE);
1105 			break;
1106 		}
1107 
1108 		if (val != no && sym_tristate_within_range(sym, no))
1109 			row[COL_NO] = g_strdup("_");
1110 		if (val != mod && sym_tristate_within_range(sym, mod))
1111 			row[COL_MOD] = g_strdup("_");
1112 		if (val != yes && sym_tristate_within_range(sym, yes))
1113 			row[COL_YES] = g_strdup("_");
1114 		break;
1115 	case S_INT:
1116 	case S_HEX:
1117 	case S_STRING:
1118 		def = sym_get_string_value(sym);
1119 		row[COL_VALUE] = g_strdup(def);
1120 		row[COL_EDIT] = GINT_TO_POINTER(TRUE);
1121 		row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
1122 		break;
1123 	}
1124 
1125 	return row;
1126 }
1127 
1128 /* Set the node content with a row of strings */
set_node(GtkTreeIter * node,struct menu * menu,gchar ** row)1129 static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row)
1130 {
1131 	GdkColor color;
1132 	gboolean success;
1133 	GdkPixbuf *pix;
1134 
1135 	pix = gdk_pixbuf_new_from_xpm_data((const char **)
1136 					   row[COL_PIXBUF]);
1137 
1138 	gdk_color_parse(row[COL_COLOR], &color);
1139 	gdk_colormap_alloc_colors(gdk_colormap_get_system(), &color, 1,
1140 				  FALSE, FALSE, &success);
1141 
1142 	gtk_tree_store_set(tree, node,
1143 			   COL_OPTION, row[COL_OPTION],
1144 			   COL_NAME, row[COL_NAME],
1145 			   COL_NO, row[COL_NO],
1146 			   COL_MOD, row[COL_MOD],
1147 			   COL_YES, row[COL_YES],
1148 			   COL_VALUE, row[COL_VALUE],
1149 			   COL_MENU, (gpointer) menu,
1150 			   COL_COLOR, &color,
1151 			   COL_EDIT, GPOINTER_TO_INT(row[COL_EDIT]),
1152 			   COL_PIXBUF, pix,
1153 			   COL_PIXVIS, GPOINTER_TO_INT(row[COL_PIXVIS]),
1154 			   COL_BTNVIS, GPOINTER_TO_INT(row[COL_BTNVIS]),
1155 			   COL_BTNACT, GPOINTER_TO_INT(row[COL_BTNACT]),
1156 			   COL_BTNINC, GPOINTER_TO_INT(row[COL_BTNINC]),
1157 			   COL_BTNRAD, GPOINTER_TO_INT(row[COL_BTNRAD]),
1158 			   -1);
1159 
1160 	g_object_unref(pix);
1161 }
1162 
1163 /* Add a node to the tree */
place_node(struct menu * menu,char ** row)1164 static void place_node(struct menu *menu, char **row)
1165 {
1166 	GtkTreeIter *parent = parents[indent - 1];
1167 	GtkTreeIter *node = parents[indent];
1168 
1169 	gtk_tree_store_append(tree, node, parent);
1170 	set_node(node, menu, row);
1171 }
1172 
1173 /* Find a node in the GTK+ tree */
1174 static GtkTreeIter found;
1175 
1176 /*
1177  * Find a menu in the GtkTree starting at parent.
1178  */
gtktree_iter_find_node(GtkTreeIter * parent,struct menu * tofind)1179 GtkTreeIter *gtktree_iter_find_node(GtkTreeIter * parent,
1180 				    struct menu *tofind)
1181 {
1182 	GtkTreeIter iter;
1183 	GtkTreeIter *child = &iter;
1184 	gboolean valid;
1185 	GtkTreeIter *ret;
1186 
1187 	valid = gtk_tree_model_iter_children(model2, child, parent);
1188 	while (valid) {
1189 		struct menu *menu;
1190 
1191 		gtk_tree_model_get(model2, child, 6, &menu, -1);
1192 
1193 		if (menu == tofind) {
1194 			memcpy(&found, child, sizeof(GtkTreeIter));
1195 			return &found;
1196 		}
1197 
1198 		ret = gtktree_iter_find_node(child, tofind);
1199 		if (ret)
1200 			return ret;
1201 
1202 		valid = gtk_tree_model_iter_next(model2, child);
1203 	}
1204 
1205 	return NULL;
1206 }
1207 
1208 /*
1209  * Update the tree by adding/removing entries
1210  * Does not change other nodes
1211  */
update_tree(struct menu * src,GtkTreeIter * dst)1212 static void update_tree(struct menu *src, GtkTreeIter * dst)
1213 {
1214 	struct menu *child1;
1215 	GtkTreeIter iter, tmp;
1216 	GtkTreeIter *child2 = &iter;
1217 	gboolean valid;
1218 	GtkTreeIter *sibling;
1219 	struct symbol *sym;
1220 	struct menu *menu1, *menu2;
1221 
1222 	if (src == &rootmenu)
1223 		indent = 1;
1224 
1225 	valid = gtk_tree_model_iter_children(model2, child2, dst);
1226 	for (child1 = src->list; child1; child1 = child1->next) {
1227 
1228 		sym = child1->sym;
1229 
1230 	      reparse:
1231 		menu1 = child1;
1232 		if (valid)
1233 			gtk_tree_model_get(model2, child2, COL_MENU,
1234 					   &menu2, -1);
1235 		else
1236 			menu2 = NULL;	// force adding of a first child
1237 
1238 #ifdef DEBUG
1239 		printf("%*c%s | %s\n", indent, ' ',
1240 		       menu1 ? menu_get_prompt(menu1) : "nil",
1241 		       menu2 ? menu_get_prompt(menu2) : "nil");
1242 #endif
1243 
1244 		if ((opt_mode == OPT_NORMAL && !menu_is_visible(child1)) ||
1245 		    (opt_mode == OPT_PROMPT && !menu_has_prompt(child1)) ||
1246 		    (opt_mode == OPT_ALL    && !menu_get_prompt(child1))) {
1247 
1248 			/* remove node */
1249 			if (gtktree_iter_find_node(dst, menu1) != NULL) {
1250 				memcpy(&tmp, child2, sizeof(GtkTreeIter));
1251 				valid = gtk_tree_model_iter_next(model2,
1252 								 child2);
1253 				gtk_tree_store_remove(tree2, &tmp);
1254 				if (!valid)
1255 					return;		/* next parent */
1256 				else
1257 					goto reparse;	/* next child */
1258 			} else
1259 				continue;
1260 		}
1261 
1262 		if (menu1 != menu2) {
1263 			if (gtktree_iter_find_node(dst, menu1) == NULL) {	// add node
1264 				if (!valid && !menu2)
1265 					sibling = NULL;
1266 				else
1267 					sibling = child2;
1268 				gtk_tree_store_insert_before(tree2,
1269 							     child2,
1270 							     dst, sibling);
1271 				set_node(child2, menu1, fill_row(menu1));
1272 				if (menu2 == NULL)
1273 					valid = TRUE;
1274 			} else {	// remove node
1275 				memcpy(&tmp, child2, sizeof(GtkTreeIter));
1276 				valid = gtk_tree_model_iter_next(model2,
1277 								 child2);
1278 				gtk_tree_store_remove(tree2, &tmp);
1279 				if (!valid)
1280 					return;	// next parent
1281 				else
1282 					goto reparse;	// next child
1283 			}
1284 		} else if (sym && (sym->flags & SYMBOL_CHANGED)) {
1285 			set_node(child2, menu1, fill_row(menu1));
1286 		}
1287 
1288 		indent++;
1289 		update_tree(child1, child2);
1290 		indent--;
1291 
1292 		valid = gtk_tree_model_iter_next(model2, child2);
1293 	}
1294 }
1295 
1296 /* Display the whole tree (single/split/full view) */
display_tree(struct menu * menu)1297 static void display_tree(struct menu *menu)
1298 {
1299 	struct symbol *sym;
1300 	struct property *prop;
1301 	struct menu *child;
1302 	enum prop_type ptype;
1303 
1304 	if (menu == &rootmenu) {
1305 		indent = 1;
1306 		current = &rootmenu;
1307 	}
1308 
1309 	for (child = menu->list; child; child = child->next) {
1310 		prop = child->prompt;
1311 		sym = child->sym;
1312 		ptype = prop ? prop->type : P_UNKNOWN;
1313 
1314 		if (sym)
1315 			sym->flags &= ~SYMBOL_CHANGED;
1316 
1317 		if ((view_mode == SPLIT_VIEW)
1318 		    && !(child->flags & MENU_ROOT) && (tree == tree1))
1319 			continue;
1320 
1321 		if ((view_mode == SPLIT_VIEW) && (child->flags & MENU_ROOT)
1322 		    && (tree == tree2))
1323 			continue;
1324 
1325 		if ((opt_mode == OPT_NORMAL && menu_is_visible(child)) ||
1326 		    (opt_mode == OPT_PROMPT && menu_has_prompt(child)) ||
1327 		    (opt_mode == OPT_ALL    && menu_get_prompt(child)))
1328 			place_node(child, fill_row(child));
1329 #ifdef DEBUG
1330 		printf("%*c%s: ", indent, ' ', menu_get_prompt(child));
1331 		printf("%s", child->flags & MENU_ROOT ? "rootmenu | " : "");
1332 		printf("%s", prop_get_type_name(ptype));
1333 		printf(" | ");
1334 		if (sym) {
1335 			printf("%s", sym_type_name(sym->type));
1336 			printf(" | ");
1337 			printf("%s", dbg_sym_flags(sym->flags));
1338 			printf("\n");
1339 		} else
1340 			printf("\n");
1341 #endif
1342 		if ((view_mode != FULL_VIEW) && (ptype == P_MENU)
1343 		    && (tree == tree2))
1344 			continue;
1345 /*
1346 		if (((menu != &rootmenu) && !(menu->flags & MENU_ROOT))
1347 		    || (view_mode == FULL_VIEW)
1348 		    || (view_mode == SPLIT_VIEW))*/
1349 
1350 		/* Change paned position if the view is not in 'split mode' */
1351 		if (view_mode == SINGLE_VIEW || view_mode == FULL_VIEW) {
1352 			gtk_paned_set_position(GTK_PANED(hpaned), 0);
1353 		}
1354 
1355 		if (((view_mode == SINGLE_VIEW) && (menu->flags & MENU_ROOT))
1356 		    || (view_mode == FULL_VIEW)
1357 		    || (view_mode == SPLIT_VIEW)) {
1358 			indent++;
1359 			display_tree(child);
1360 			indent--;
1361 		}
1362 	}
1363 }
1364 
1365 /* Display a part of the tree starting at current node (single/split view) */
display_tree_part(void)1366 static void display_tree_part(void)
1367 {
1368 	if (tree2)
1369 		gtk_tree_store_clear(tree2);
1370 	if (view_mode == SINGLE_VIEW)
1371 		display_tree(current);
1372 	else if (view_mode == SPLIT_VIEW)
1373 		display_tree(browsed);
1374 	gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w));
1375 }
1376 
1377 /* Display the list in the left frame (split view) */
display_list(void)1378 static void display_list(void)
1379 {
1380 	if (tree1)
1381 		gtk_tree_store_clear(tree1);
1382 
1383 	tree = tree1;
1384 	display_tree(&rootmenu);
1385 	gtk_tree_view_expand_all(GTK_TREE_VIEW(tree1_w));
1386 	tree = tree2;
1387 }
1388 
fixup_rootmenu(struct menu * menu)1389 void fixup_rootmenu(struct menu *menu)
1390 {
1391 	struct menu *child;
1392 	static int menu_cnt = 0;
1393 
1394 	menu->flags |= MENU_ROOT;
1395 	for (child = menu->list; child; child = child->next) {
1396 		if (child->prompt && child->prompt->type == P_MENU) {
1397 			menu_cnt++;
1398 			fixup_rootmenu(child);
1399 			menu_cnt--;
1400 		} else if (!menu_cnt)
1401 			fixup_rootmenu(child);
1402 	}
1403 }
1404 
1405 /* Main */
main(int ac,char * av[])1406 int main(int ac, char *av[])
1407 {
1408 	const char *name;
1409 	char *env;
1410 	gchar *glade_file;
1411 
1412 	/* GTK stuffs */
1413 	gtk_set_locale();
1414 	gtk_init(&ac, &av);
1415 	glade_init();
1416 
1417 	//add_pixmap_directory (PACKAGE_DATA_DIR "/" PACKAGE "/pixmaps");
1418 	//add_pixmap_directory (PACKAGE_SOURCE_DIR "/pixmaps");
1419 
1420 	/* Determine GUI path */
1421 	env = getenv(SRCTREE);
1422 	if (env)
1423 		glade_file = g_strconcat(env, "/scripts/kconfig/gconf.glade", NULL);
1424 	else if (av[0][0] == '/')
1425 		glade_file = g_strconcat(av[0], ".glade", NULL);
1426 	else
1427 		glade_file = g_strconcat(g_get_current_dir(), "/", av[0], ".glade", NULL);
1428 
1429 	/* Conf stuffs */
1430 	if (ac > 1 && av[1][0] == '-') {
1431 		switch (av[1][1]) {
1432 		case 'a':
1433 			//showAll = 1;
1434 			break;
1435 		case 's':
1436 			conf_set_message_callback(NULL);
1437 			break;
1438 		case 'h':
1439 		case '?':
1440 			printf("%s [-s] <config>\n", av[0]);
1441 			exit(0);
1442 		}
1443 		name = av[2];
1444 	} else
1445 		name = av[1];
1446 
1447 	conf_parse(name);
1448 	fixup_rootmenu(&rootmenu);
1449 	conf_read(NULL);
1450 
1451 	/* Load the interface and connect signals */
1452 	init_main_window(glade_file);
1453 	init_tree_model();
1454 	init_left_tree();
1455 	init_right_tree();
1456 
1457 	switch (view_mode) {
1458 	case SINGLE_VIEW:
1459 		display_tree_part();
1460 		break;
1461 	case SPLIT_VIEW:
1462 		display_list();
1463 		break;
1464 	case FULL_VIEW:
1465 		display_tree(&rootmenu);
1466 		break;
1467 	}
1468 
1469 	gtk_main();
1470 
1471 	return 0;
1472 }
1473 
conf_changed(void)1474 static void conf_changed(void)
1475 {
1476 	bool changed = conf_get_changed();
1477 	gtk_widget_set_sensitive(save_btn, changed);
1478 	gtk_widget_set_sensitive(save_menu_item, changed);
1479 }
1480