1 /*
2  *		watch_model.c
3  *
4  *      Copyright 2010 Alexander Petukhov <devel(at)apetukhov.ru>
5  *
6  *      This program is free software; you can redistribute it and/or modify
7  *      it under the terms of the GNU General Public License as published by
8  *      the Free Software Foundation; either version 2 of the License, or
9  *      (at your option) any later version.
10  *
11  *      This program is distributed in the hope that it will be useful,
12  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *      GNU General Public License for more details.
15  *
16  *      You should have received a copy of the GNU General Public License
17  *      along with this program; if not, write to the Free Software
18  *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  *      MA 02110-1301, USA.
20  */
21 
22 /*
23  *		Watch tree view model.
24  * 		Functions for handling variables tree - expanding, refreshing etc.
25  */
26 
27 #ifdef HAVE_CONFIG_H
28 	#include "config.h"
29 #endif
30 #include <geanyplugin.h>
31 
32 #include <string.h>
33 #include <gtk/gtk.h>
34 
35 #include "watch_model.h"
36 #include "breakpoint.h"
37 #include "debug_module.h"
38 
39 /* text for the stub item */
40 #define WATCH_CHILDREN_STUB "..."
41 
42 extern dbg_module *active_module;
43 
44 /*
45  * removes all children from "parent"
46  */
remove_children(GtkTreeModel * model,GtkTreeIter * parent)47 inline static void remove_children(GtkTreeModel *model, GtkTreeIter *parent)
48 {
49 	GtkTreeIter child;
50 	if (gtk_tree_model_iter_children(model, &child, parent))
51 	{
52 		while(gtk_tree_store_remove(GTK_TREE_STORE(model), &child))
53 			;
54 	}
55 }
56 
57 /*
58  * adds stub subitem to "parent"
59  */
add_stub(GtkTreeStore * store,GtkTreeIter * parent)60 inline static void add_stub(GtkTreeStore *store, GtkTreeIter *parent)
61 {
62 	/* add subitem */
63 	GtkTreeIter stub;
64 	gtk_tree_store_prepend (store, &stub, parent);
65 	gtk_tree_store_set (store, &stub,
66 		W_NAME, WATCH_CHILDREN_STUB,
67 		W_VALUE, "",
68 		W_TYPE, "",
69 		W_INTERNAL, "",
70 	    W_EXPRESSION, "",
71 		W_STUB, FALSE,
72 		W_CHANGED, FALSE,
73 		W_VT, VT_NONE,
74 		-1);
75 
76 	/* set W_STUB flag */
77 	gtk_tree_store_set (
78 		store,
79 		parent,
80 		W_STUB, TRUE,
81 		-1);
82 }
83 
84 /*
85  * insert all "vars" members to "parent" iterator in the "tree" as new children
86  * mark_changed specifies whether to mark new items as beed changed
87  * expand specifies whether to expand to the added children
88  */
append_variables(GtkTreeView * tree,GtkTreeIter * parent,GList * vars,gboolean mark_changed,gboolean expand)89 inline static void append_variables(GtkTreeView *tree, GtkTreeIter *parent, GList *vars,
90 	gboolean mark_changed, gboolean expand)
91 {
92 	int current_position = 0;
93 	GtkTreeModel *model = gtk_tree_view_get_model(tree);
94 	GtkTreeStore *store = GTK_TREE_STORE(model);
95 	GtkTreeIter child;
96 	GHashTable *ht = NULL;
97 
98 	/* get existing rows */
99 	if (gtk_tree_model_iter_n_children(model, parent))
100 	{
101 		/* collect all existing rows */
102 		gtk_tree_model_iter_children(model, &child, parent);
103 
104 		ht = g_hash_table_new_full(g_str_hash, g_str_equal, (GDestroyNotify)g_free, (GDestroyNotify)gtk_tree_row_reference_free);
105 
106 		do
107 		{
108 			gchar *name = NULL;
109 			gtk_tree_model_get(model, &child, W_NAME, &name, -1);
110 			if (name && strlen(name))
111 			{
112 				GtkTreePath *path = gtk_tree_model_get_path(model, &child);
113 				g_hash_table_insert(ht, name, gtk_tree_row_reference_new(model, path));
114 				gtk_tree_path_free(path);
115 			}
116 		}
117 		while(gtk_tree_model_iter_next(model, &child));
118 	}
119 
120 	while (vars)
121 	{
122 		variable *v = vars->data;
123 
124 		GtkTreeRowReference *reference = NULL;
125 		if (ht && (reference = g_hash_table_lookup (ht, v->name->str)))
126 		{
127 			GtkTreePath *path = gtk_tree_row_reference_get_path(reference);
128 			if (current_position != gtk_tree_path_get_indices(path)[0])
129 			{
130 				/* move a row if not at it's place */
131 				GtkTreeIter iter;
132 				gtk_tree_model_get_iter(model, &iter, path);
133 				if (current_position)
134 				{
135 					GtkTreeIter insert_after;
136 					gtk_tree_model_iter_nth_child(model, &insert_after, parent, current_position - 1);
137 					gtk_tree_store_move_after(store, &iter, &insert_after);
138 				}
139 				else
140 				{
141 					gtk_tree_store_move_after(store, &iter, NULL);
142 				}
143 			}
144 
145 			gtk_tree_path_free(path);
146 		}
147 		else
148 		{
149 			gtk_tree_store_insert(store, &child, parent, current_position);
150 			gtk_tree_store_set (store, &child,
151 				W_NAME, v->name->str,
152 				W_VALUE, v->value->str,
153 				W_TYPE, v->type->str,
154 				W_INTERNAL, v->internal->str,
155 				W_EXPRESSION, v->expression->str,
156 				W_STUB, v->has_children,
157 				W_CHANGED, mark_changed,
158 				W_VT, v->vt,
159 				-1);
160 
161 			/* expand to row if we were asked to */
162 			if (expand)
163 			{
164 				GtkTreePath *path = gtk_tree_model_get_path(model, &child);
165 				gtk_tree_view_expand_row(tree, path, FALSE);
166 				gtk_tree_path_free(path);
167 			}
168 
169 			/* add stub if added child also have children */
170 			if (v->has_children)
171 				add_stub(store, &child);
172 		}
173 
174 		/* move to next variable */
175 		vars = vars->next;
176 		current_position++;
177 	}
178 
179 	if (ht)
180 	{
181 		g_hash_table_destroy(ht);
182 	}
183 }
184 
185 /*
186  * looks up a variable with corresponding name in the list
187  */
lookup_variable(GList * vars,gchar * name)188 inline static GList *lookup_variable(GList *vars, gchar *name)
189 {
190 	GList *var = vars;
191 	while (var)
192 	{
193 		variable *v = (variable*)var->data;
194 		if (!strcmp(v->name->str, name))
195 			break;
196 		var = var->next;
197 	}
198 
199 	return var;
200 }
201 
202 /*
203  * frees variable list: removes all data and destroys list
204  * used for the lists returned by get_children call
205  * who doesn't represent internal debugger module structures
206  */
free_variables_list(GList * vars)207 void free_variables_list(GList *vars)
208 {
209 	g_list_foreach(vars, (GFunc)variable_free, NULL);
210 	g_list_free(vars);
211 }
212 
213 /*
214  * updates iterator according to variable
215  */
update_variable(GtkTreeStore * store,GtkTreeIter * iter,variable * var,gboolean changed)216 static void update_variable(GtkTreeStore *store, GtkTreeIter *iter, variable *var, gboolean changed)
217 {
218 	gtk_tree_store_set (store, iter,
219 		W_NAME, var->name->str,
220 		W_VALUE, var->evaluated ? var->value->str : _("Can't evaluate expression"),
221 		W_TYPE, var->type->str,
222 		W_INTERNAL, var->internal->str,
223 		W_EXPRESSION, var->expression->str,
224 		W_STUB, FALSE,
225 		W_CHANGED, changed,
226 		W_VT, var->vt,
227 		-1);
228 }
229 
230 /*
231  * remove stub item and add vars to parent iterator
232  */
expand_stub(GtkTreeView * tree,GtkTreeIter * parent,GList * vars)233 void expand_stub(GtkTreeView *tree, GtkTreeIter *parent, GList *vars)
234 {
235 	GtkTreeModel *model = gtk_tree_view_get_model(tree);
236 	GtkTreeStore *store = GTK_TREE_STORE(model);
237 	GtkTreeIter stub;
238 	gboolean changed;
239 
240 	/* remember stub iterator */
241 	gtk_tree_model_iter_children(model, &stub, parent);
242 
243 	/* check whether arent has been changed */
244 	gtk_tree_model_get(model, parent,
245 		W_CHANGED, &changed,
246 		-1);
247 
248 	/* add all stuff from "vars" */
249 	append_variables(tree, parent, vars, changed, TRUE);
250 
251 	/* remove stub item */
252 	gtk_tree_store_remove(store, &stub);
253 }
254 
255 /*
256  * change watch specified by "iter" as dscribed in "var"
257  */
change_watch(GtkTreeView * tree,GtkTreeIter * iter,gpointer var)258 void change_watch(GtkTreeView *tree, GtkTreeIter *iter, gpointer var)
259 {
260 	GtkTreeModel *model = gtk_tree_view_get_model(tree);
261 	GtkTreeStore *store = GTK_TREE_STORE(model);
262 
263 	variable *v = (variable*)var;
264 
265 	/* update variable */
266 	update_variable(store, iter, v, FALSE);
267 
268 	/* if item have children - remove them */
269 	if (gtk_tree_model_iter_has_child(model, iter))
270 		remove_children(model, iter);
271 
272 	/* if new watch has children - add stub */
273 	if (v->has_children)
274 		add_stub(store, iter);
275 }
276 
277 /*
278  * update root variables in "tree" according to the "vars" list
279  * if root variables have expanded children - walk through them also
280  */
update_variables(GtkTreeView * tree,GtkTreeIter * parent,GList * vars)281 void update_variables(GtkTreeView *tree, GtkTreeIter *parent, GList *vars)
282 {
283 	/* tree model and store for the given tree */
284 	GtkTreeModel *model = gtk_tree_view_get_model(tree);
285 	GtkTreeStore *store = GTK_TREE_STORE(model);
286 
287 	/* check whether "parent" has children nodes
288 	if NULL == parent - let's try to get iter to the first node in the tree */
289 	GtkTreeIter child;
290 	gboolean haschildren = FALSE;
291 	gboolean parent_changed = FALSE;
292 	if (parent)
293 	{
294 		gtk_tree_model_get (model, parent,
295 			W_CHANGED, &parent_changed,
296 			-1);
297 		haschildren = gtk_tree_model_iter_children(model, &child, parent);
298 	}
299 	else
300 		haschildren = gtk_tree_model_get_iter_first(model, &child);
301 
302 	/* walk through all children of "parent" iterator */
303 	if (haschildren)
304 	{
305 		/* if have children - lets check and update their values */
306 		while (TRUE)
307 		{
308 			gchar *name;
309 			gchar *internal;
310 			gchar *value;
311 			GList *var;
312 			variable *v;
313 			gboolean changed;
314 
315 			/* set variable value
316 			1. get the variable params */
317 
318 			gtk_tree_model_get (
319 				model,
320 				&child,
321 				W_NAME, &name,
322 				W_INTERNAL, &internal,
323 				W_VALUE, &value,
324 				-1);
325 
326 			/* miss empty row in watch tree */
327 			if (!strlen(name))
328 				break;
329 
330 			/* 2. find this path is "vars" list */
331 			var = lookup_variable(vars, name);
332 
333 			/* 3. check if we have found currect iterator */
334 			if (!var)
335 			{
336 				/* if we haven't - remove current and try to move to the next one
337 				in the same level */
338 
339 				/* if gtk_tree_store_remove returns "true" - child is set to the next iterator,
340 				if "false" - it was the last - then, exit the loop */
341 				if (gtk_tree_store_remove(GTK_TREE_STORE(model), &child))
342 					continue;
343 				else
344 					break;
345 			}
346 
347 			/* 4. update variable (type, value) */
348 			v = (variable*)var->data;
349 			changed = parent_changed || strcmp(value, v->value->str);
350 			update_variable(store, &child, v, changed && v->evaluated);
351 
352 			/* 5. if item have children - process them */
353 			if (gtk_tree_model_iter_has_child(model, &child))
354 			{
355 				if (!v->has_children)
356 				{
357 					/* if children are left from previous variable value - remove all children */
358 					remove_children(model, &child);
359 				}
360 				else
361 				{
362 					/* if row isn't expanded - add "..." item
363 					else - process all children */
364 					GtkTreePath *path = gtk_tree_model_get_path(model, &child);
365 					if (!gtk_tree_view_row_expanded(tree, path))
366 					{
367 						/* remove all children */
368 						remove_children(model, &child);
369 						/* add stub item */
370 						add_stub(store, &child);
371 					}
372 					else
373 					{
374 						/* get children for "parent" item */
375 						GList *children = active_module->get_children(v->internal->str);
376 						/* update children */
377 						update_variables(tree, &child, g_list_copy(children));
378 						/* frees children list */
379 						free_variables_list(children);
380 					}
381 					gtk_tree_path_free(path);
382 				}
383 			}
384 			else if (v->has_children)
385 			{
386 				/* if tree item doesn't have children, but variable has
387 				(variable type has changed) - add stub */
388 				add_stub(store, &child);
389 			}
390 
391 			/* 6. free name, expression */
392 			g_free(name);
393 			g_free(internal);
394 			g_free(value);
395 
396 			if (!gtk_tree_model_iter_next(model, &child))
397 				break;
398 		}
399 	}
400 
401 	/* insert items that are left in "vars" list */
402 	append_variables(tree, parent, vars, !parent || parent_changed, TRUE);
403 
404 	/* remove variables left in the list */
405 	g_list_free(vars);
406 }
407 
408 /*
409  * clear all root variables in "tree" removing their children if available
410  */
clear_watch_values(GtkTreeView * tree)411 void clear_watch_values(GtkTreeView *tree)
412 {
413 	GtkTreeModel *model = gtk_tree_view_get_model(tree);
414 	GtkTreeStore *store = GTK_TREE_STORE(model);
415 
416 	GtkTreeIter child;
417 	if(!gtk_tree_model_get_iter_first(model, &child))
418 		return;
419 
420 	do
421 	{
422 		/* if item have children - process them */
423 		if (gtk_tree_model_iter_has_child(model, &child))
424 			remove_children(model, &child);
425 
426 		/* set expression/value/type to empty string */
427 		gtk_tree_store_set (store, &child,
428 			W_VALUE, "",
429 			W_TYPE, "",
430 			W_INTERNAL, "",
431 			W_EXPRESSION, "",
432 			W_STUB, FALSE,
433 			W_CHANGED, FALSE,
434 			-1);
435 	}
436 	while (gtk_tree_model_iter_next(model, &child));
437 }
438 
439 /*
440  * set W_NAME field to "name" and all others to empty string
441  */
variable_set_name_only(GtkTreeStore * store,GtkTreeIter * iter,gchar * name)442 void variable_set_name_only(GtkTreeStore *store, GtkTreeIter *iter, gchar *name)
443 {
444 	/* set value/type/internal to empty string */
445 	gtk_tree_store_set (store, iter,
446 		W_NAME, name,
447 		W_VALUE, "",
448 		W_TYPE, "",
449 		W_INTERNAL, "",
450 		W_EXPRESSION, "",
451 		W_STUB, FALSE,
452 		W_CHANGED, FALSE,
453 		W_VT, VT_WATCH,
454 		-1);
455 }
456 
457 /*
458  * get names of the root items in the tree view
459  */
get_root_items(GtkTreeView * tree)460 GList *get_root_items(GtkTreeView *tree)
461 {
462 	GtkTreeModel *model = gtk_tree_view_get_model(tree);
463 	GtkTreeIter child;
464 	GList *names = NULL;
465 
466 	if(!gtk_tree_model_get_iter_first(model, &child))
467 		return NULL;
468 
469 	do
470 	{
471 		gchar *name;
472 		gtk_tree_model_get (
473 			model,
474 			&child,
475 			W_NAME, &name,
476 			-1);
477 
478 		if (strlen(name))
479 			names = g_list_prepend(names, name);
480 	}
481 	while (gtk_tree_model_iter_next(model, &child));
482 
483 	return g_list_reverse(names);
484 }
485