1 /*
2  *  inspect.c
3  *
4  *  Copyright 2012 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23 
24 #include <ctype.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include "common.h"
29 
30 enum
31 {
32 	INSPECT_VAR1,
33 	INSPECT_DISPLAY,
34 	INSPECT_VALUE,
35 	INSPECT_HB_MODE,
36 	INSPECT_SCID,
37 	INSPECT_EXPR,
38 	INSPECT_NAME,
39 	INSPECT_FRAME,
40 	INSPECT_RUN_APPLY,
41 	INSPECT_START,
42 	INSPECT_COUNT,
43 	INSPECT_EXPAND,
44 	INSPECT_NUMCHILD,
45 	INSPECT_FORMAT,
46 	INSPECT_PATH_EXPR
47 };
48 
49 enum
50 {
51 	FORMAT_NATURAL,
52 	FORMAT_DECIMAL,
53 	FORMAT_HEX,
54 	FORMAT_OCTAL,
55 	FORMAT_BINARY,
56 	FORMAT_COUNT
57 };
58 
59 static GtkTreeView *tree;
60 static ScpTreeStore *store;
61 static GtkTreeSelection *selection;
62 static gint scid_gen = 0;
63 
append_stub(GtkTreeIter * parent,const gchar * text,gboolean expand)64 static void append_stub(GtkTreeIter *parent, const gchar *text, gboolean expand)
65 {
66 	scp_tree_store_append_with_values(store, NULL, parent, INSPECT_EXPR, text,
67 		INSPECT_EXPAND, expand, -1);
68 }
69 
70 #define append_ellipsis(parent, expand) append_stub((parent), _("..."), (expand))
71 
inspect_find_recursive(GtkTreeIter * iter,gint i,const char * key)72 static gboolean inspect_find_recursive(GtkTreeIter *iter, gint i, const char *key)
73 {
74 	do
75 	{
76 		gboolean flag = !key;
77 
78 		if (flag)
79 		{
80 			gint scid;
81 
82 			scp_tree_store_get(store, iter, INSPECT_SCID, &scid, -1);
83 			if (scid == i)
84 				return TRUE;
85 		}
86 		else
87 		{
88 			const char *var1;
89 			size_t len;
90 
91 			scp_tree_store_get(store, iter, INSPECT_VAR1, &var1, -1);
92 			len = var1 ? strlen(var1) : 0;
93 
94 			if (var1 && !strncmp(key, var1, len))
95 			{
96 				if (key[len] == '\0')
97 					return TRUE;
98 
99 				flag = key[len] == '.' && key[len + 1];
100 			}
101 		}
102 
103 		if (flag)
104 		{
105 			GtkTreeIter child;
106 
107 			if (scp_tree_store_iter_children(store, &child, iter) &&
108 				inspect_find_recursive(&child, i, key))
109 			{
110 				*iter = child;
111 				return TRUE;
112 			}
113 		}
114 	} while (scp_tree_store_iter_next(store, iter));
115 
116 	return FALSE;
117 }
118 
inspect_find(GtkTreeIter * iter,gboolean string,const char * key)119 static gboolean inspect_find(GtkTreeIter *iter, gboolean string, const char *key)
120 {
121 	if (scp_tree_store_get_iter_first(store, iter) &&
122 		inspect_find_recursive(iter, atoi(key), string ? key : NULL))
123 	{
124 		return TRUE;
125 	}
126 
127 	if (!string)
128 		dc_error("%s: i_scid not found", key);
129 
130 	return FALSE;
131 }
132 
inspect_get_scid(GtkTreeIter * iter)133 static gint inspect_get_scid(GtkTreeIter *iter)
134 {
135 	gint scid;
136 
137 	scp_tree_store_get(store, iter, INSPECT_SCID, &scid, -1);
138 	if (!scid)
139 		scp_tree_store_set(store, iter, INSPECT_SCID, scid = ++scid_gen, -1);
140 
141 	return scid;
142 }
143 
inspect_expand(GtkTreeIter * iter)144 static void inspect_expand(GtkTreeIter *iter)
145 {
146 	const char *var1;
147 	gint scid, start, count, numchild;
148 	char *s;
149 
150 	scid = inspect_get_scid(iter);
151 	scp_tree_store_get(store, iter, INSPECT_VAR1, &var1, INSPECT_START, &start,
152 		INSPECT_COUNT, &count, INSPECT_NUMCHILD, &numchild, -1);
153 	s = g_strdup_printf("%d", start);
154 	debug_send_format(N, "07%c%d%d-var-list-children 1 %s %d %d", '0' + (int) strlen(s) - 1,
155 		start, scid, var1, start, count ? start + count : numchild);
156 	g_free(s);
157 }
158 
on_jump_to_menu_item_activate(GtkMenuItem * menuitem,G_GNUC_UNUSED gpointer gdata)159 static void on_jump_to_menu_item_activate(GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer gdata)
160 {
161 	GtkTreeIter iter;
162 	const gchar *expr = gtk_menu_item_get_label(menuitem);
163 
164 	if (store_find(store, &iter, INSPECT_EXPR, expr))
165 		utils_tree_set_cursor(selection, &iter, 0);
166 }
167 
168 static GtkWidget *jump_to_item;
169 static GtkContainer *jump_to_menu;
170 static gchar *jump_to_expr = NULL;
171 
on_inspect_row_inserted(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,G_GNUC_UNUSED gpointer gdata)172 static void on_inspect_row_inserted(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
173 	G_GNUC_UNUSED gpointer gdata)
174 {
175 	if (gtk_tree_path_get_depth(path) == 1)
176 	{
177 		GtkWidget *item;
178 		const gint *index = gtk_tree_path_get_indices(path);
179 
180 		g_free(jump_to_expr);
181 		gtk_tree_model_get(model, iter, INSPECT_EXPR, &jump_to_expr, -1);
182 		item = gtk_menu_item_new_with_label(jump_to_expr ? jump_to_expr : "");
183 		gtk_widget_show(item);
184 		gtk_menu_shell_insert(GTK_MENU_SHELL(jump_to_menu), item, *index);
185 		g_signal_connect(item, "activate", G_CALLBACK(on_jump_to_menu_item_activate), NULL);
186 	}
187 }
188 
on_inspect_row_changed(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,G_GNUC_UNUSED gpointer gdata)189 static void on_inspect_row_changed(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
190 	G_GNUC_UNUSED gpointer gdata)
191 {
192 	if (!jump_to_expr && gtk_tree_path_get_depth(path) == 1)
193 	{
194 		const gint *index = gtk_tree_path_get_indices(path);
195 		GList *list = gtk_container_get_children(jump_to_menu);
196 		gpointer item = g_list_nth_data(list, *index);
197 
198 		gtk_tree_model_get(model, iter, INSPECT_EXPR, &jump_to_expr, -1);
199 		if (jump_to_expr)
200 			gtk_menu_item_set_label(GTK_MENU_ITEM(item), jump_to_expr);
201 		g_list_free(list);
202 	}
203 }
204 
on_inspect_row_deleted(GtkTreeModel * model,GtkTreePath * path,G_GNUC_UNUSED gpointer gdata)205 static void on_inspect_row_deleted(GtkTreeModel *model, GtkTreePath *path,
206 	G_GNUC_UNUSED gpointer gdata)
207 {
208 	if (gtk_tree_path_get_depth(path) == 1)
209 	{
210 		const gint *index = gtk_tree_path_get_indices(path);
211 		GList *list = gtk_container_get_children(jump_to_menu);
212 		gpointer item = g_list_nth_data(list, *index);
213 		GtkTreeIter iter;
214 
215 		gtk_container_remove(jump_to_menu, GTK_WIDGET(item));
216 		if (!gtk_tree_model_get_iter_first(model, &iter))
217 			gtk_widget_set_sensitive(jump_to_item, FALSE);
218 		g_list_free(list);
219 	}
220 }
221 
222 static const MenuItem *apply_item;
223 
inspect_redisplay(GtkTreeIter * iter,const char * value,gchar * display)224 static gchar *inspect_redisplay(GtkTreeIter *iter, const char *value, gchar *display)
225 {
226 	gint hb_mode;
227 
228 	scp_tree_store_get(store, iter, INSPECT_HB_MODE, &hb_mode, -1);
229 	g_free(display);
230 	return value && *value ? utils_get_display_from_7bit(value, hb_mode) : g_strdup("??");
231 }
232 
inspect_variable_store(GtkTreeIter * iter,const ParseVariable * var)233 static gint inspect_variable_store(GtkTreeIter *iter, const ParseVariable *var)
234 {
235 	gint format;
236 	gboolean expand;
237 
238 	scp_tree_store_get(store, iter, INSPECT_EXPAND, &expand, INSPECT_FORMAT, &format, -1);
239 	scp_tree_store_set(store, iter, INSPECT_VAR1, var->name, INSPECT_DISPLAY, var->display,
240 		INSPECT_VALUE, var->value, INSPECT_NUMCHILD, var->numchild, -1);
241 
242 	if (var->numchild)
243 	{
244 		append_ellipsis(iter, TRUE);
245 		if (expand)
246 			inspect_expand(iter);
247 	}
248 
249 	return format;
250 }
251 
252 static const char *const inspect_formats[FORMAT_COUNT] = { "natural", "decimal",
253 	"hexadecimal", "octal", "binary" };
254 
on_inspect_variable(GArray * nodes)255 void on_inspect_variable(GArray *nodes)
256 {
257 	GtkTreeIter iter;
258 	const char *token = parse_grab_token(nodes);
259 
260 	iff (store_find(store, &iter, INSPECT_SCID, token), "%s: no vid", token)
261 	{
262 		ParseVariable var;
263 		gint format;
264 
265 		parse_variable(nodes, &var, "numchild");
266 		var.display = inspect_redisplay(&iter, var.value, var.display);
267 		scp_tree_store_clear_children(store, &iter, FALSE);
268 
269 		if ((format = inspect_variable_store(&iter, &var)) != FORMAT_NATURAL)
270 		{
271 			debug_send_format(N, "07%s-var-set-format %s %s", token, var.name,
272 				inspect_formats[format]);
273 		}
274 
275 		if (gtk_tree_selection_iter_is_selected(selection, &iter))
276 			menu_item_set_active(apply_item, TRUE);
277 
278 		parse_variable_free(&var);
279 	}
280 }
281 
inspect_set(GArray * nodes,const char * value,gint format)282 static void inspect_set(GArray *nodes, const char *value, gint format)
283 {
284 	const char *token = parse_grab_token(nodes);
285 	GtkTreeIter iter;
286 
287 	if (inspect_find(&iter, FALSE, token))
288 	{
289 		if (!value || *value)
290 		{
291 			gchar *display = inspect_redisplay(&iter, value, NULL);
292 
293 			scp_tree_store_set(store, &iter, INSPECT_DISPLAY, display, INSPECT_VALUE,
294 				value, -1);
295 			g_free(display);
296 		}
297 		else
298 		{
299 			scp_tree_store_get(store, &iter, INSPECT_VALUE, &value, -1);
300 
301 			if (value)
302 			{
303 				scp_tree_store_set(store, &iter, INSPECT_DISPLAY, "??", INSPECT_VALUE,
304 					NULL);
305 			}
306 		}
307 
308 		if (format < FORMAT_COUNT)
309 			scp_tree_store_set(store, &iter, INSPECT_FORMAT, format, -1);
310 	}
311 }
312 
on_inspect_format(GArray * nodes)313 void on_inspect_format(GArray *nodes)
314 {
315 	const char *fmtstr = parse_lead_value(nodes);
316 	gint format;
317 
318 	for (format = FORMAT_NATURAL; format < FORMAT_COUNT; format++)
319 		if (!strcmp(inspect_formats[format], fmtstr))
320 			break;
321 
322 	iff (format < FORMAT_COUNT, "bad format")
323 		inspect_set(nodes, parse_find_value(nodes, "value"), format);
324 }
325 
on_inspect_evaluate(GArray * nodes)326 void on_inspect_evaluate(GArray *nodes)
327 {
328 	inspect_set(nodes, parse_lead_value(nodes), FORMAT_COUNT);
329 }
330 
on_inspect_assign(GArray * nodes)331 void on_inspect_assign(GArray *nodes)
332 {
333 	on_inspect_evaluate(nodes);
334 	views_data_dirty(DS_BUSY);
335 }
336 
inspect_node_append(const ParseNode * node,GtkTreeIter * parent)337 static void inspect_node_append(const ParseNode *node, GtkTreeIter *parent)
338 {
339 	GArray *nodes = (GArray *) node->value;
340 	ParseVariable var;
341 
342 	if (node->type == PT_VALUE || !parse_variable(nodes, &var, "numchild"))
343 		append_stub(parent, _("invalid data"), FALSE);
344 	else
345 	{
346 		GtkTreeIter iter;
347 
348 		scp_tree_store_append(store, &iter, parent);
349 		inspect_variable_store(&iter, &var);
350 		scp_tree_store_set(store, &iter, INSPECT_EXPR, var.expr ? var.expr : var.name,
351 			INSPECT_HB_MODE, var.hb_mode, INSPECT_FORMAT, FORMAT_NATURAL, -1);
352 		parse_variable_free(&var);
353 	}
354 }
355 
on_inspect_children(GArray * nodes)356 void on_inspect_children(GArray *nodes)
357 {
358 	char *token = (char *) parse_grab_token(nodes);
359 	size_t size = *token - '0' + 2;
360 
361 	iff (strlen(token) >= size + 1, "bad token")
362 	{
363 		GtkTreeIter iter;
364 
365 		if (inspect_find(&iter, FALSE, token + size))
366 		{
367 			gint from;
368 			GtkTreePath *path = scp_tree_store_get_path(store, &iter);
369 
370 			token[size] = '\0';
371 			from = atoi(token + 1);
372 			scp_tree_store_clear_children(store, &iter, FALSE);
373 
374 			if ((nodes = parse_find_array(nodes, "children")) == NULL)
375 				append_stub(&iter, _("no children in range"), FALSE);
376 			else
377 			{
378 				gint numchild, end;
379 				const char *var1;
380 
381 				if (from)
382 					append_ellipsis(&iter, FALSE);
383 
384 				scp_tree_store_get(store, &iter, INSPECT_VAR1, &var1,
385 					INSPECT_NUMCHILD, &numchild, -1);
386 
387 				parse_foreach(nodes, (GFunc) inspect_node_append, &iter);
388 				end = from + nodes->len;
389 
390 				if (nodes->len && (from || end < numchild))
391 				{
392 					debug_send_format(N, "04-var-set-update-range %s %d %d", var1,
393 						from, end);
394 				}
395 
396 				if (nodes->len ? end < numchild : !from)
397 					append_ellipsis(&iter, FALSE);
398 			}
399 
400 			gtk_tree_view_expand_row(tree, path, FALSE);
401 			gtk_tree_path_free(path);
402 		}
403 	}
404 }
405 
inspect_iter_clear(GtkTreeIter * iter,G_GNUC_UNUSED gpointer gdata)406 static void inspect_iter_clear(GtkTreeIter *iter, G_GNUC_UNUSED gpointer gdata)
407 {
408 	scp_tree_store_clear_children(store, iter, FALSE);
409 	scp_tree_store_set(store, iter, INSPECT_DISPLAY, NULL, INSPECT_VALUE, NULL,
410 		INSPECT_VAR1, NULL, INSPECT_NUMCHILD, 0, INSPECT_PATH_EXPR, NULL, -1);
411 
412 	if (gtk_tree_selection_iter_is_selected(selection, iter))
413 		menu_item_set_active(apply_item, FALSE);
414 }
415 
on_inspect_ndeleted(GArray * nodes)416 void on_inspect_ndeleted(GArray *nodes)
417 {
418 	const char *token = parse_grab_token(nodes);
419 
420 	iff (*token <= '1', "%s: invalid i_oper", token)
421 	{
422 		GtkTreeIter iter;
423 
424 		if (inspect_find(&iter, FALSE, token + 1))
425 		{
426 			if (*token == '0')
427 				inspect_iter_clear(&iter, NULL);
428 			else
429 				scp_tree_store_remove(store, &iter);
430 		}
431 	}
432 }
433 
on_inspect_path_expr(GArray * nodes)434 void on_inspect_path_expr(GArray *nodes)
435 {
436 	const char *token = parse_grab_token(nodes);
437 	GtkTreeIter iter;
438 
439 	if (inspect_find(&iter, FALSE, token))
440 		scp_tree_store_set(store, &iter, INSPECT_PATH_EXPR, parse_lead_value(nodes), -1);
441 }
442 
inspect_node_change(const ParseNode * node,G_GNUC_UNUSED gpointer gdata)443 static void inspect_node_change(const ParseNode *node, G_GNUC_UNUSED gpointer gdata)
444 {
445 	iff (node->type == PT_ARRAY, "changelist: contains value")
446 	{
447 		GArray *nodes = (GArray *) node->value;
448 		ParseVariable var;
449 		GtkTreeIter iter;
450 
451 		if (parse_variable(nodes, &var, "new_num_children") &&
452 			inspect_find(&iter, TRUE, var.name))
453 		{
454 			const char *in_scope = parse_find_value(nodes, "in_scope");
455 
456 			if (!g_strcmp0(in_scope, "false"))
457 			{
458 				scp_tree_store_set(store, &iter, INSPECT_DISPLAY, _("out of scope"),
459 					INSPECT_VALUE, NULL, -1);
460 			}
461 			else if (!g_strcmp0(in_scope, "invalid"))
462 			{
463 				debug_send_format(N, "070%d-var-delete %s", inspect_get_scid(&iter),
464 					var.name);
465 			}
466 			else
467 			{
468 				var.display = inspect_redisplay(&iter, var.value, var.display);
469 
470 				if (var.children)
471 				{
472 					scp_tree_store_clear_children(store, &iter, FALSE);
473 					inspect_variable_store(&iter, &var);
474 				}
475 				else
476 				{
477 					scp_tree_store_set(store, &iter, INSPECT_DISPLAY, var.display,
478 						INSPECT_VALUE, var.value, -1);
479 				}
480 			}
481 		}
482 
483 		parse_variable_free(&var);
484 	}
485 }
486 
487 static gboolean query_all_inspects = FALSE;
488 
on_inspect_changelist(GArray * nodes)489 void on_inspect_changelist(GArray *nodes)
490 {
491 	GArray *changelist = parse_lead_array(nodes);
492 	const char *token = parse_grab_token(nodes);
493 
494 	if (token)
495 	{
496 		iff (*token <= '1', "%s: invalid i_oper", token)
497 			if (*token == '0')
498 				parse_foreach(changelist, (GFunc) inspect_node_change, NULL);
499 	}
500 	else if (changelist->len)
501 		query_all_inspects = TRUE;
502 }
503 
inspect_apply(GtkTreeIter * iter)504 static void inspect_apply(GtkTreeIter *iter)
505 {
506 	gint scid;
507 	const gchar *expr;
508 	const char *name, *frame;
509 	char *locale;
510 
511 	scp_tree_store_get(store, iter, INSPECT_EXPR, &expr, INSPECT_SCID, &scid,
512 		INSPECT_NAME, &name, INSPECT_FRAME, &frame, -1);
513 	locale = utils_get_locale_from_utf8(expr);
514 	debug_send_format(F, "07%d-var-create %s %s %s", scid, name, frame, locale);
515 	g_free(locale);
516 }
517 
on_inspect_signal(const char * name)518 void on_inspect_signal(const char *name)
519 {
520 	GtkTreeIter iter;
521 
522 	iff (isalpha(*name), "%s: invalid var name", name)
523 	{
524 		iff (store_find(store, &iter, INSPECT_NAME, name), "%s: var not found", name)
525 		{
526 			const char *var1;
527 
528 			scp_tree_store_get(store, &iter, INSPECT_VAR1, &var1, -1);
529 
530 			iff (!var1, "%s: already applied", name)
531 				inspect_apply(&iter);
532 		}
533 	}
534 }
535 
inspects_clear(void)536 void inspects_clear(void)
537 {
538 	store_foreach(store, (GFunc) inspect_iter_clear, NULL);
539 	query_all_inspects = FALSE;
540 }
541 
inspect_iter_refresh(ScpTreeStore * store,GtkTreeIter * iter,gpointer gdata)542 static gint inspect_iter_refresh(ScpTreeStore *store, GtkTreeIter *iter, gpointer gdata)
543 {
544 	const char *var1;
545 	gint numchild;
546 
547 	scp_tree_store_get(store, iter, INSPECT_VAR1, &var1, INSPECT_NUMCHILD, &numchild, -1);
548 
549 	if (var1 && !numchild)
550 	{
551 		debug_send_format(F, "0%c%d-var-evaluate-expression %s", GPOINTER_TO_INT(gdata),
552 			inspect_get_scid(iter), var1);
553 	}
554 	return FALSE;
555 }
556 
inspects_send_refresh(char token)557 static void inspects_send_refresh(char token)
558 {
559 	scp_tree_store_traverse(store, TRUE, NULL, NULL, inspect_iter_refresh,
560 		GINT_TO_POINTER((gint) token));
561 	query_all_inspects = FALSE;
562 }
563 
inspects_update(void)564 gboolean inspects_update(void)
565 {
566 	if (query_all_inspects)
567 		inspects_send_refresh('4');
568 	else
569 		debug_send_command(F, "040-var-update 1 *");
570 
571 	return TRUE;
572 }
573 
inspect_iter_apply(GtkTreeIter * iter,G_GNUC_UNUSED gpointer gdata)574 static void inspect_iter_apply(GtkTreeIter *iter, G_GNUC_UNUSED gpointer gdata)
575 {
576 	const char *var1, *frame;
577 	gboolean run_apply;
578 
579 	scp_tree_store_get(store, iter, INSPECT_VAR1, &var1, INSPECT_FRAME, &frame,
580 		INSPECT_RUN_APPLY, &run_apply, -1);
581 
582 	if (run_apply && !var1 && !isdigit(*frame))
583 		inspect_apply(iter);
584 }
585 
inspects_apply(void)586 void inspects_apply(void)
587 {
588 	store_foreach(store, (GFunc) inspect_iter_apply, NULL);
589 }
590 
inspect_name_valid(const char * name)591 static gboolean inspect_name_valid(const char *name)
592 {
593 	return !strcmp(name, "-") || isalpha(*name);
594 }
595 
inspect_frame_valid(const char * frame)596 static gboolean inspect_frame_valid(const char *frame)
597 {
598 	char *end;
599 	strtol(frame, &end, 0);
600 	return !strcmp(frame, "*") || !strcmp(frame, "@") || (end > frame && *end == '\0');
601 }
602 
603 static GtkWidget *inspect_dialog;
604 static GtkEntry *inspect_expr;
605 static GtkEntry *inspect_name;
606 static GtkEntry *inspect_frame;
607 static GtkToggleButton *inspect_run_apply;
608 static GtkWidget *inspect_ok;
609 
on_inspect_entry_changed(G_GNUC_UNUSED GtkEditable * editable,G_GNUC_UNUSED gpointer gdata)610 static void on_inspect_entry_changed(G_GNUC_UNUSED GtkEditable *editable,
611 	G_GNUC_UNUSED gpointer gdata)
612 {
613 	const char *frame = gtk_entry_get_text(inspect_frame);
614 	const char *expr = gtk_entry_get_text(inspect_expr);
615 
616 	gtk_widget_set_sensitive(GTK_WIDGET(inspect_run_apply), !isdigit(*frame));
617 	gtk_widget_set_sensitive(inspect_ok,
618 		inspect_name_valid(gtk_entry_get_text(inspect_name)) &&
619 		inspect_frame_valid(frame) && *utils_skip_spaces(expr));
620 }
621 
on_inspect_ok_button_clicked(G_GNUC_UNUSED GtkButton * button,G_GNUC_UNUSED gpointer gdata)622 static void on_inspect_ok_button_clicked(G_GNUC_UNUSED GtkButton *button,
623 	G_GNUC_UNUSED gpointer gdata)
624 {
625 	GtkTreeIter iter;
626 	const char *name = gtk_entry_get_text(inspect_name);
627 
628 	if ((strcmp(name, "-") && store_find(store, &iter, INSPECT_NAME, name)) ||
629 		inspect_find(&iter, TRUE, name))
630 	{
631 		show_error(_("Duplicate inspect variable name."));
632 	}
633 	else
634 		gtk_dialog_response(GTK_DIALOG(inspect_dialog), GTK_RESPONSE_ACCEPT);
635 }
636 
inspect_dialog_store(GtkTreeIter * iter)637 static void inspect_dialog_store(GtkTreeIter *iter)
638 {
639 	const gchar *expr = gtk_entry_get_text(inspect_expr);
640 
641 	scp_tree_store_set(store, iter, INSPECT_EXPR, expr, INSPECT_PATH_EXPR, expr,
642 		INSPECT_NAME, gtk_entry_get_text(inspect_name),
643 		INSPECT_FRAME, gtk_entry_get_text(inspect_frame),
644 		INSPECT_RUN_APPLY, gtk_toggle_button_get_active(inspect_run_apply), -1);
645 }
646 
inspect_add(const gchar * text)647 void inspect_add(const gchar *text)
648 {
649 	gtk_entry_set_text(inspect_expr, text ? text : "");
650 	gtk_entry_set_text(inspect_name, "-");
651 	gtk_toggle_button_set_active(inspect_run_apply, FALSE);
652 	on_inspect_entry_changed(NULL, NULL);
653 	gtk_widget_grab_focus(GTK_WIDGET(inspect_expr));
654 
655 	if (gtk_dialog_run(GTK_DIALOG(inspect_dialog)) == GTK_RESPONSE_ACCEPT)
656 	{
657 		GtkTreeIter iter;
658 		const gchar *expr = gtk_entry_get_text(inspect_expr);
659 
660 		scp_tree_store_append_with_values(store, &iter, NULL, INSPECT_HB_MODE,
661 			parse_mode_get(expr, MODE_HBIT), INSPECT_SCID, ++scid_gen, INSPECT_FORMAT,
662 			FORMAT_NATURAL, INSPECT_COUNT, option_inspect_count, INSPECT_EXPAND,
663 			option_inspect_expand, -1);
664 		inspect_dialog_store(&iter);
665 		utils_tree_set_cursor(selection, &iter, -1);
666 
667 		if (debug_state() != DS_INACTIVE)
668 			gtk_widget_set_sensitive(jump_to_item, TRUE);
669 
670 		if (debug_state() & DS_DEBUG)
671 			inspect_apply(&iter);
672 	}
673 }
674 
on_inspect_display_edited(G_GNUC_UNUSED GtkCellRendererText * renderer,gchar * path_str,gchar * new_text,G_GNUC_UNUSED gpointer gdata)675 static void on_inspect_display_edited(G_GNUC_UNUSED GtkCellRendererText *renderer,
676 	gchar *path_str, gchar *new_text, G_GNUC_UNUSED gpointer gdata)
677 {
678 	GtkTreeIter iter;
679 	char *format;
680 
681 	scp_tree_store_get_iter_from_string(store, &iter, path_str);
682 	format = g_strdup_printf("07%d-var-assign %%s %%s", inspect_get_scid(&iter));
683 	view_display_edited(store, debug_state() & DS_VARIABLE, path_str, format, new_text);
684 	g_free(format);
685 }
686 
687 static const TreeCell inspect_cells[] =
688 {
689 	{ "inspect_display", G_CALLBACK(on_inspect_display_edited) },
690 	{ NULL, NULL }
691 };
692 
693 static GObject *inspect_display;
694 
inspects_update_state(DebugState state)695 void inspects_update_state(DebugState state)
696 {
697 	static gboolean last_active = FALSE;
698 	gboolean active = state != DS_INACTIVE;
699 	GtkTreeIter iter;
700 
701 	if (gtk_tree_selection_get_selected(selection, NULL, &iter))
702 	{
703 		const char *var1 = NULL;
704 		gint numchild = 0;
705 
706 		if (state & DS_VARIABLE)
707 		{
708 			scp_tree_store_get(store, &iter, INSPECT_VAR1, &var1, INSPECT_NUMCHILD,
709 				&numchild, -1);
710 		}
711 		g_object_set(inspect_display, "editable", var1 && !numchild, NULL);
712 	}
713 
714 	if (active != last_active)
715 	{
716 		gtk_widget_set_sensitive(jump_to_item, active &&
717 			scp_tree_store_get_iter_first(store, &iter));
718 		last_active = active;
719 	}
720 }
721 
inspects_delete_all(void)722 void inspects_delete_all(void)
723 {
724 	store_clear(store);
725 	scid_gen = 0;
726 }
727 
inspect_load(GKeyFile * config,const char * section)728 static gboolean inspect_load(GKeyFile *config, const char *section)
729 {
730 	char *name = utils_key_file_get_string(config, section, "name");
731 	gchar *expr = utils_key_file_get_string(config, section, "expr");
732 	gint hb_mode = utils_get_setting_integer(config, section, "hbit", HB_DEFAULT);
733 	char *frame = utils_key_file_get_string(config, section, "frame");
734 	gboolean run_apply = utils_get_setting_boolean(config, section, "run_apply", FALSE);
735 	gint start = utils_get_setting_integer(config, section, "start", 0);
736 	gint count = utils_get_setting_integer(config, section, "count", option_inspect_count);
737 	gboolean expand = utils_get_setting_boolean(config, section, "expand",
738 		option_inspect_expand);
739 	gint format = utils_get_setting_integer(config, section, "format", FORMAT_NATURAL);
740 	gboolean valid = FALSE;
741 
742 	if (name && inspect_name_valid(name) && expr && (unsigned) hb_mode < HB_COUNT &&
743 		frame && inspect_frame_valid(frame) && (unsigned) start <= EXPAND_MAX &&
744 		(unsigned) count <= EXPAND_MAX && (unsigned) format < FORMAT_COUNT)
745 	{
746 		scp_tree_store_append_with_values(store, NULL, NULL, INSPECT_EXPR, expr,
747 			INSPECT_PATH_EXPR, expr, INSPECT_HB_MODE, hb_mode, INSPECT_SCID, ++scid_gen,
748 			INSPECT_NAME, name, INSPECT_FRAME, frame, INSPECT_RUN_APPLY, run_apply,
749 			INSPECT_START, start, INSPECT_COUNT, count, INSPECT_EXPAND, expand,
750 			INSPECT_FORMAT, format, -1);
751 		valid = TRUE;
752 	}
753 
754 	g_free(frame);
755 	g_free(expr);
756 	g_free(name);
757 	return valid;
758 }
759 
inspects_load(GKeyFile * config)760 void inspects_load(GKeyFile *config)
761 {
762 	inspects_delete_all();
763 	utils_load(config, "inspect", inspect_load);
764 }
765 
inspect_save(GKeyFile * config,const char * section,GtkTreeIter * iter)766 static gboolean inspect_save(GKeyFile *config, const char *section, GtkTreeIter *iter)
767 {
768 	gint hb_mode, start, count, format;
769 	const char *name, *frame;
770 	const gchar *expr;
771 	gboolean run_apply, expand;
772 
773 	scp_tree_store_get(store, iter, INSPECT_EXPR, &expr, INSPECT_HB_MODE, &hb_mode,
774 		INSPECT_NAME, &name, INSPECT_FRAME, &frame, INSPECT_RUN_APPLY, &run_apply,
775 		INSPECT_START, &start, INSPECT_COUNT, &count, INSPECT_EXPAND, &expand,
776 		INSPECT_FORMAT, &format, -1);
777 	g_key_file_set_string(config, section, "name", name);
778 	g_key_file_set_string(config, section, "expr", expr);
779 	g_key_file_set_integer(config, section, "hbit", hb_mode);
780 	g_key_file_set_string(config, section, "frame", frame);
781 	g_key_file_set_boolean(config, section, "run_apply", run_apply);
782 	g_key_file_set_integer(config, section, "start", start);
783 	g_key_file_set_integer(config, section, "count", count);
784 	g_key_file_set_boolean(config, section, "expand", expand);
785 	g_key_file_set_integer(config, section, "format", format);
786 	return TRUE;
787 }
788 
inspects_save(GKeyFile * config)789 void inspects_save(GKeyFile *config)
790 {
791 	store_save(store, config, "inspect", inspect_save);
792 }
793 
on_inspect_refresh(G_GNUC_UNUSED const MenuItem * menu_item)794 static void on_inspect_refresh(G_GNUC_UNUSED const MenuItem *menu_item)
795 {
796 	debug_send_command(F, "041-var-update 1 *");
797 	inspects_send_refresh('2');
798 }
799 
on_inspect_add(G_GNUC_UNUSED const MenuItem * menu_item)800 static void on_inspect_add(G_GNUC_UNUSED const MenuItem *menu_item)
801 {
802 	GtkTreeIter iter;
803 	const gchar *expr = NULL;
804 
805 	if (gtk_tree_selection_get_selected(selection, NULL, &iter))
806 		scp_tree_store_get(store, &iter, INSPECT_PATH_EXPR, &expr, -1);
807 
808 	inspect_add(expr);
809 }
810 
on_inspect_edit(G_GNUC_UNUSED const MenuItem * menu_item)811 static void on_inspect_edit(G_GNUC_UNUSED const MenuItem *menu_item)
812 {
813 	GtkTreeIter iter;
814 	const gchar *expr;
815 	const char *name, *frame;
816 	gboolean run_apply;
817 
818 	if (gtk_tree_selection_get_selected(selection, NULL, &iter))
819 	{
820 		scp_tree_store_get(store, &iter, INSPECT_EXPR, &expr, INSPECT_NAME, &name,
821 			INSPECT_FRAME, &frame, INSPECT_RUN_APPLY, &run_apply, -1);
822 		scp_tree_store_set(store, &iter, INSPECT_NAME, "-", -1);  /* for duplicate name check */
823 
824 		gtk_entry_set_text(inspect_expr, expr);
825 		gtk_entry_set_text(inspect_name, name);
826 		gtk_entry_set_text(inspect_frame, frame);
827 		gtk_toggle_button_set_active(inspect_run_apply, run_apply);
828 		on_inspect_entry_changed(NULL, NULL);
829 
830 		if (gtk_dialog_run(GTK_DIALOG(inspect_dialog)) == GTK_RESPONSE_ACCEPT)
831 		{
832 			g_free(jump_to_expr);
833 			jump_to_expr = NULL;
834 			inspect_dialog_store(&iter);
835 		}
836 		else
837 		{
838 			scp_tree_store_set(store, &iter, INSPECT_NAME, name, -1);
839 		}
840 	}
841 }
842 
on_inspect_apply(G_GNUC_UNUSED const MenuItem * menu_item)843 static void on_inspect_apply(G_GNUC_UNUSED const MenuItem *menu_item)
844 {
845 	GtkTreeIter iter;
846 	const char *var1;
847 	gint scid;
848 
849 	if (gtk_tree_selection_get_selected(selection, NULL, &iter))
850 	{
851 		scp_tree_store_get(store, &iter, INSPECT_SCID, &scid, INSPECT_VAR1, &var1, -1);
852 
853 		if (var1)
854 		{
855 			debug_send_format(N, "070%d-var-delete %s", scid, var1);
856 		}
857 		else
858 		{
859 			inspect_apply(&iter);
860 		}
861 	}
862 }
863 
864 static GtkWidget *expand_dialog;
865 static GtkSpinButton *expand_start;
866 static GtkSpinButton *expand_count;
867 static GtkToggleButton *expand_automatic;
868 
on_inspect_expand(G_GNUC_UNUSED const MenuItem * menu_item)869 static void on_inspect_expand(G_GNUC_UNUSED const MenuItem *menu_item)
870 {
871 	GtkTreeIter iter;
872 	const char *name;
873 	gint start, count;
874 	gboolean expand;
875 
876 	if (gtk_tree_selection_get_selected(selection, NULL, &iter))
877 	{
878 		scp_tree_store_get(store, &iter, INSPECT_NAME, &name, INSPECT_START, &start,
879 			INSPECT_COUNT, &count, INSPECT_EXPAND, &expand, -1);
880 		gtk_spin_button_set_value(expand_start, start);
881 		gtk_spin_button_set_value(expand_count, count);
882 		gtk_toggle_button_set_active(expand_automatic, expand);
883 		gtk_widget_set_sensitive(GTK_WIDGET(expand_automatic), name != NULL);
884 
885 		if (gtk_dialog_run(GTK_DIALOG(expand_dialog)) == GTK_RESPONSE_ACCEPT)
886 		{
887 			scp_tree_store_set(store, &iter,
888 				INSPECT_START, gtk_spin_button_get_value_as_int(expand_start),
889 				INSPECT_COUNT, gtk_spin_button_get_value_as_int(expand_count),
890 				INSPECT_EXPAND, gtk_toggle_button_get_active(expand_automatic), -1);
891 
892 			if (debug_state() & DS_VARIABLE)
893 			{
894 				inspect_expand(&iter);
895 			}
896 			else
897 			{
898 				plugin_beep();
899 			}
900 		}
901 	}
902 }
903 
on_inspect_copy(const MenuItem * menu_item)904 static void on_inspect_copy(const MenuItem *menu_item)
905 {
906 	menu_copy(selection, menu_item);
907 }
908 
on_inspect_format_display(const MenuItem * menu_item)909 static void on_inspect_format_display(const MenuItem *menu_item)
910 {
911 	menu_mode_display(selection, menu_item, INSPECT_FORMAT);
912 }
913 
on_inspect_format_update(const MenuItem * menu_item)914 static void on_inspect_format_update(const MenuItem *menu_item)
915 {
916 	GtkTreeIter iter;
917 	gint format = GPOINTER_TO_INT(menu_item->gdata);
918 	const char *var1;
919 
920 	if (gtk_tree_selection_get_selected(selection, NULL, &iter))
921 	{
922 		scp_tree_store_get(store, &iter, INSPECT_VAR1, &var1, -1);
923 
924 		if (var1)
925 		{
926 			debug_send_format(N, "07%d-var-set-format %s %s", inspect_get_scid(&iter), var1,
927 				inspect_formats[format]);
928 		}
929 		else
930 		{
931 			scp_tree_store_set(store, &iter, INSPECT_FORMAT, format, -1);
932 		}
933 	}
934 }
935 
on_inspect_hbit_display(const MenuItem * menu_item)936 static void on_inspect_hbit_display(const MenuItem *menu_item)
937 {
938 	menu_hbit_display(selection, menu_item);
939 }
940 
inspect_hbit_update_iter(GtkTreeIter * iter,gint hb_mode)941 static void inspect_hbit_update_iter(GtkTreeIter *iter, gint hb_mode)
942 {
943 	const char *var1, *value;
944 
945 	scp_tree_store_get(store, iter, INSPECT_VAR1, &var1, INSPECT_VALUE, &value, -1);
946 	scp_tree_store_set(store, iter, INSPECT_HB_MODE, hb_mode, -1);
947 
948 	if (var1)
949 	{
950 		gchar *display = inspect_redisplay(iter, value, NULL);
951 		scp_tree_store_set(store, iter, INSPECT_DISPLAY, display, -1);
952 		g_free(display);
953 	}
954 }
955 
on_inspect_hbit_update(const MenuItem * menu_item)956 static void on_inspect_hbit_update(const MenuItem *menu_item)
957 {
958 	GtkTreeIter iter;
959 	const char *expr, *name;
960 	gint hb_mode = GPOINTER_TO_INT(menu_item->gdata);
961 
962 	if (gtk_tree_selection_get_selected(selection, NULL, &iter))
963 	{
964 		scp_tree_store_get(store, &iter, INSPECT_EXPR, &expr, INSPECT_NAME, &name, -1);
965 		inspect_hbit_update_iter(&iter, hb_mode);
966 		parse_mode_update(expr, MODE_HBIT, hb_mode);
967 
968 		if (name)
969 		{
970 			char *reverse = parse_mode_reentry(expr);
971 
972 			if (store_find(store, &iter, INSPECT_EXPR, reverse))
973 				inspect_hbit_update_iter(&iter, hb_mode);
974 			g_free(reverse);
975 		}
976 	}
977 }
978 
on_inspect_delete(G_GNUC_UNUSED const MenuItem * menu_item)979 static void on_inspect_delete(G_GNUC_UNUSED const MenuItem *menu_item)
980 {
981 	GtkTreeIter iter;
982 	const char *var1;
983 
984 	if (gtk_tree_selection_get_selected(selection, NULL, &iter))
985 	{
986 		scp_tree_store_get(store, &iter, INSPECT_VAR1, &var1, -1);
987 
988 		if (var1)
989 		{
990 			debug_send_format(N, "071%d-var-delete %s", inspect_get_scid(&iter), var1);
991 		}
992 		else
993 		{
994 			scp_tree_store_remove(store, &iter);
995 		}
996 	}
997 }
998 
999 #define DS_EDITABLE (DS_BASICS | DS_EXTRA_2)
1000 #define DS_APPLIABLE (DS_VARIABLE | DS_EXTRA_3)
1001 #define DS_EXPANDABLE (DS_VARIABLE | DS_EXTRA_4)
1002 #define DS_COPYABLE (DS_BASICS | DS_EXTRA_1)
1003 #define DS_FORMATABLE (DS_INACTIVE | DS_VARIABLE | DS_EXTRA_1)
1004 #define DS_REPARSABLE (DS_BASICS | DS_EXTRA_1)
1005 #define DS_DELETABLE (DS_NOT_BUSY | DS_EXTRA_1)
1006 
1007 #define FORMAT_ITEM(format, FORMAT) \
1008 	{ ("inspect_format_"format), on_inspect_format_update, DS_FORMATABLE, NULL, \
1009 		GINT_TO_POINTER(FORMAT) }
1010 
1011 static MenuItem inspect_menu_items[] =
1012 {
1013 	{ "inspect_refresh",   on_inspect_refresh,        DS_VARIABLE,   NULL, NULL },
1014 	{ "inspect_add",       on_inspect_add,            DS_NOT_BUSY,   NULL, NULL },
1015 	{ "inspect_edit",      on_inspect_edit,           DS_EDITABLE,   NULL, NULL },
1016 	{ "inspect_apply",     on_inspect_apply,          DS_APPLIABLE,  NULL, NULL },
1017 	{ "inspect_expand",    on_inspect_expand,         DS_EXPANDABLE, NULL, NULL },
1018 	{ "inspect_copy",      on_inspect_copy,           DS_COPYABLE,   NULL, NULL },
1019 	{ "inspect_format",    on_inspect_format_display, DS_FORMATABLE, NULL, NULL },
1020 	FORMAT_ITEM("natural", FORMAT_NATURAL),
1021 	FORMAT_ITEM("decimal", FORMAT_DECIMAL),
1022 	FORMAT_ITEM("hex",     FORMAT_HEX),
1023 	FORMAT_ITEM("octal",   FORMAT_OCTAL),
1024 	FORMAT_ITEM("binary",  FORMAT_BINARY),
1025 	MENU_HBIT_ITEMS(inspect),
1026 	{ "inspect_delete",    on_inspect_delete, DS_DELETABLE, NULL, NULL },
1027 	{ NULL, NULL, 0, NULL, NULL }
1028 };
1029 
inspect_menu_extra_state(void)1030 static guint inspect_menu_extra_state(void)
1031 {
1032 	GtkTreeIter iter;
1033 
1034 	if (gtk_tree_selection_get_selected(selection, NULL, &iter))
1035 	{
1036 		const char *var1, *name;
1037 		gint numchild;
1038 
1039 		scp_tree_store_get(store, &iter, INSPECT_VAR1, &var1, INSPECT_NAME, &name,
1040 			INSPECT_NUMCHILD, &numchild, -1);
1041 
1042 		if (name || var1)
1043 		{
1044 			return (1 << DS_INDEX_1) | ((name && !var1) << DS_INDEX_2) |
1045 				((name != NULL) << DS_INDEX_3) | ((numchild != 0) << DS_INDEX_4);
1046 		}
1047 	}
1048 
1049 	return 0;
1050 }
1051 
1052 static MenuInfo inspect_menu_info = { inspect_menu_items, inspect_menu_extra_state, 0 };
1053 
on_inspect_selection_changed(G_GNUC_UNUSED GtkTreeSelection * selection,G_GNUC_UNUSED gpointer gdata)1054 static void on_inspect_selection_changed(G_GNUC_UNUSED GtkTreeSelection *selection,
1055 	G_GNUC_UNUSED gpointer gdata)
1056 {
1057 	GtkTreeIter iter;
1058 	const char *name = NULL;
1059 
1060 	if (gtk_widget_get_visible(inspect_dialog))
1061 		gtk_widget_hide(inspect_dialog);
1062 	else if (gtk_widget_get_visible(expand_dialog))
1063 		gtk_widget_hide(expand_dialog);
1064 
1065 	if (gtk_tree_selection_get_selected(selection, NULL, &iter))
1066 		scp_tree_store_get(store, &iter, INSPECT_NAME, &name, -1);
1067 
1068 	gtk_tree_view_set_reorderable(tree, name != NULL);
1069 	inspects_update_state(debug_state());
1070 }
1071 
inspect_test_expand_row(G_GNUC_UNUSED GtkTreeView * tree_view,GtkTreeIter * iter,G_GNUC_UNUSED GtkTreePath * path,G_GNUC_UNUSED gpointer gdata)1072 static gboolean inspect_test_expand_row(G_GNUC_UNUSED GtkTreeView *tree_view,
1073 	GtkTreeIter *iter, G_GNUC_UNUSED GtkTreePath *path, G_GNUC_UNUSED gpointer gdata)
1074 {
1075 	GtkTreeIter child;
1076 	const char *var1;
1077 	gboolean expand;
1078 
1079 	scp_tree_store_iter_children(store, &child, iter);
1080 	scp_tree_store_get(store, &child, INSPECT_VAR1, &var1, INSPECT_EXPAND, &expand, -1);
1081 
1082 	if (var1 || !expand)
1083 		return FALSE;
1084 
1085 	if (debug_state() & DS_VARIABLE)
1086 		inspect_expand(iter);
1087 	else
1088 		plugin_blink();
1089 
1090 	return TRUE;
1091 }
1092 
on_inspect_key_press(G_GNUC_UNUSED GtkWidget * widget,GdkEventKey * event,G_GNUC_UNUSED gpointer gdata)1093 static gboolean on_inspect_key_press(G_GNUC_UNUSED GtkWidget *widget, GdkEventKey *event,
1094 	G_GNUC_UNUSED gpointer gdata)
1095 {
1096 	if (ui_is_keyval_enter_or_return(event->keyval))
1097 	{
1098 		menu_item_execute(&inspect_menu_info, apply_item, FALSE);
1099 		return TRUE;
1100 	}
1101 
1102 	return menu_insert_delete(event, &inspect_menu_info, "inspect_add", "inspect_delete");
1103 }
1104 
on_inspect_button_press(GtkWidget * widget,GdkEventButton * event,G_GNUC_UNUSED gpointer gdata)1105 gboolean on_inspect_button_press(GtkWidget *widget, GdkEventButton *event,
1106 	G_GNUC_UNUSED gpointer gdata)
1107 {
1108 	if (event->button == 1 && event->type == GDK_2BUTTON_PRESS)
1109 	{
1110 		utils_handle_button_press(widget, event);
1111 		menu_item_execute(&inspect_menu_info, apply_item, FALSE);
1112 		return TRUE;
1113 	}
1114 
1115 	return FALSE;
1116 }
1117 
on_inspect_drag_motion(G_GNUC_UNUSED GtkWidget * widget,G_GNUC_UNUSED GdkDragContext * context,gint x,gint y,G_GNUC_UNUSED guint time,G_GNUC_UNUSED gpointer gdata)1118 gboolean on_inspect_drag_motion(G_GNUC_UNUSED GtkWidget *widget,
1119 	G_GNUC_UNUSED GdkDragContext *context, gint x, gint y, G_GNUC_UNUSED guint time,
1120 	G_GNUC_UNUSED gpointer gdata)
1121 {
1122 	GtkTreePath *path;
1123 	GtkTreeViewDropPosition pos;
1124 
1125 	if (gtk_tree_view_get_dest_row_at_pos(tree, x, y, &path, &pos))
1126 	{
1127 		GtkTreeIter iter;
1128 		const char *name;
1129 
1130 		scp_tree_store_get_iter(store, &iter, path);
1131 		gtk_tree_path_free(path);
1132 		scp_tree_store_get(store, &iter, INSPECT_NAME, &name, -1);
1133 
1134 		if (!name || pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE ||
1135 			pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER)
1136 		{
1137 			g_signal_stop_emission_by_name(tree, "drag-motion");
1138 		}
1139 	}
1140 
1141 	return FALSE;
1142 }
1143 
on_inspect_menu_show(G_GNUC_UNUSED GtkWidget * widget,G_GNUC_UNUSED gpointer gdata)1144 static void on_inspect_menu_show(G_GNUC_UNUSED GtkWidget *widget, G_GNUC_UNUSED gpointer gdata)
1145 {
1146 	GtkTreeIter iter;
1147 
1148 	if (gtk_tree_selection_get_selected(selection, NULL, &iter))
1149 	{
1150 		const char *var1, *path_expr;
1151 
1152 		scp_tree_store_get(store, &iter, INSPECT_VAR1, &var1, INSPECT_PATH_EXPR,
1153 			&path_expr, -1);
1154 		menu_item_set_active(apply_item, var1 != NULL);
1155 
1156 		if (var1 && !path_expr && (debug_state() & DS_SENDABLE))
1157 		{
1158 			debug_send_format(N, "04%d-var-info-path-expression %s",
1159 				inspect_get_scid(&iter), var1);
1160 		}
1161 	}
1162 }
1163 
inspect_init(void)1164 void inspect_init(void)
1165 {
1166 	GtkWidget *menu;
1167 
1168 	jump_to_item = get_widget("inspect_jump_to_item");
1169 	jump_to_menu = GTK_CONTAINER(get_widget("inspect_jump_to_menu"));
1170 	apply_item = menu_item_find(inspect_menu_items, "inspect_apply");
1171 
1172 	tree = view_connect("inspect_view", &store, &selection, inspect_cells, "inspect_window",
1173 		&inspect_display);
1174 	g_signal_connect(tree, "test-expand-row", G_CALLBACK(inspect_test_expand_row), NULL);
1175 	g_signal_connect(tree, "key-press-event", G_CALLBACK(on_inspect_key_press), NULL);
1176 	g_signal_connect(tree, "button-press-event", G_CALLBACK(on_inspect_button_press), NULL);
1177 	g_signal_connect(tree, "drag-motion", G_CALLBACK(on_inspect_drag_motion), NULL);
1178 
1179 	g_signal_connect(store, "row-inserted", G_CALLBACK(on_inspect_row_inserted), NULL);
1180 	g_signal_connect(store, "row-changed", G_CALLBACK(on_inspect_row_changed), NULL);
1181 	g_signal_connect(store, "row-deleted", G_CALLBACK(on_inspect_row_deleted), NULL);
1182 
1183 	g_signal_connect(selection, "changed", G_CALLBACK(on_inspect_selection_changed), NULL);
1184 	menu = menu_select("inspect_menu", &inspect_menu_info, selection);
1185 	g_signal_connect(menu, "show", G_CALLBACK(on_inspect_menu_show), NULL);
1186 	if (pref_var_update_bug)
1187 		inspect_menu_items->state = DS_DEBUG;
1188 
1189 	inspect_dialog = dialog_connect("inspect_dialog");
1190 	inspect_name = GTK_ENTRY(get_widget("inspect_name_entry"));
1191 	validator_attach(GTK_EDITABLE(inspect_name), VALIDATOR_NOSPACE);
1192 	g_signal_connect(inspect_name, "changed", G_CALLBACK(on_inspect_entry_changed), NULL);
1193 	inspect_frame = GTK_ENTRY(get_widget("inspect_frame_entry"));
1194 	validator_attach(GTK_EDITABLE(inspect_frame), VALIDATOR_VARFRAME);
1195 	g_signal_connect(inspect_frame, "changed", G_CALLBACK(on_inspect_entry_changed), NULL);
1196 	inspect_expr = GTK_ENTRY(get_widget("inspect_expr_entry"));
1197 	g_signal_connect(inspect_expr, "changed", G_CALLBACK(on_inspect_entry_changed), NULL);
1198 	inspect_run_apply = GTK_TOGGLE_BUTTON(get_widget("inspect_run_apply"));
1199 	inspect_ok = get_widget("inspect_ok");
1200 	g_signal_connect(inspect_ok, "clicked", G_CALLBACK(on_inspect_ok_button_clicked), NULL);
1201 	gtk_widget_grab_default(inspect_ok);
1202 
1203 	expand_dialog = dialog_connect("expand_dialog");
1204 	expand_start = GTK_SPIN_BUTTON(get_widget("expand_start_spin"));
1205 	expand_count = GTK_SPIN_BUTTON(get_widget("expand_count_spin"));
1206 	expand_automatic = GTK_TOGGLE_BUTTON(get_widget("expand_automatic"));
1207 	gtk_widget_grab_default(get_widget("expand_ok"));
1208 }
1209 
inspect_finalize(void)1210 void inspect_finalize(void)
1211 {
1212 	gtk_widget_destroy(inspect_dialog);
1213 	gtk_widget_destroy(expand_dialog);
1214 	g_free(jump_to_expr);
1215 }
1216