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