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