1 /*
2 * bptree.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 * Contains breakpoints GtkTreeView manipulating functions
24 * and handlers for tree view events.
25 * Handlers only collect data from the tree and call
26 * corresponding breaks_set_.... functions, which call
27 * bptree_set... if breakpoint has been changed altered/added/removed
28 */
29
30 #include <stdlib.h>
31 #include <memory.h>
32
33 #include <gtk/gtk.h>
34 #include <gdk/gdkkeysyms.h>
35
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39 #include <geanyplugin.h>
40
41 #include "breakpoints.h"
42 #include "bptree.h"
43 #include "utils.h"
44 #include "dconfig.h"
45 #include "tabs.h"
46 #include "pixbuf.h"
47
48 #include "cell_renderers/cellrendererbreakicon.h"
49 #include "cell_renderers/cellrenderertoggle.h"
50
51 /* Tree view columns */
52 enum
53 {
54 FILEPATH,
55 CONDITION,
56 HITSCOUNT,
57 LINE,
58 ENABLED,
59 LAST_VISIBLE,
60 N_COLUMNS
61 };
62
63 /* tree view and store handles */
64 static GtkWidget *tree = NULL;
65 static GtkTreeModel *model = NULL;
66 static GtkTreeStore *store = NULL;
67
68 /* column cell renderes */
69 static GtkCellRenderer *hcount_renderer;
70 static GtkCellRenderer *condition_renderer;
71
72 /* tells to checkbox click handler whether page is in readonly mode (debug running) */
73 static gboolean readonly = FALSE;
74
75 /* hash table to keep file nodes in the tree */
76 static GHashTable *files;
77
78 /* callback handler */
79 move_to_line_cb on_break_clicked = NULL;
80
81 /*
82 * gets tree row reference for an unsected row at the same depth
83 */
get_unselected_sibling(GtkTreePath * path)84 static GtkTreeRowReference* get_unselected_sibling(GtkTreePath *path)
85 {
86 GtkTreeRowReference *sibling = NULL;
87 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
88
89 /* move down find first unselected sibling */
90 GtkTreeIter titer;
91 gtk_tree_model_get_iter(model, &titer, path);
92 while (gtk_tree_model_iter_next(model, &titer))
93 {
94 if (!gtk_tree_selection_iter_is_selected(selection, &titer))
95 {
96 GtkTreePath *sibling_path = gtk_tree_model_get_path(model, &titer);
97 sibling = gtk_tree_row_reference_new(model, sibling_path);
98 gtk_tree_path_free(sibling_path);
99 break;
100 }
101 }
102
103 if (!sibling)
104 {
105 /* move up find first unselected sibling */
106 GtkTreePath *sibling_path = gtk_tree_path_copy(path);
107 while (gtk_tree_path_prev(sibling_path))
108 {
109 if (!gtk_tree_selection_path_is_selected(selection, sibling_path))
110 {
111 sibling = gtk_tree_row_reference_new(model, sibling_path);
112 break;
113 }
114 }
115 gtk_tree_path_free(sibling_path);
116 }
117
118 return sibling;
119 }
120
121 /*
122 * checks file ENABLED column if all childs are enabled and unchecks otherwise
123 */
update_file_node(GtkTreeIter * file_iter)124 static void update_file_node(GtkTreeIter *file_iter)
125 {
126 GtkTreeIter child;
127 gboolean check = TRUE;
128 if(gtk_tree_model_iter_children(model, &child, file_iter))
129 {
130 do
131 {
132 gboolean enabled;
133 gtk_tree_model_get (
134 model,
135 &child,
136 ENABLED, &enabled,
137 -1);
138
139 if (!enabled)
140 {
141 check = FALSE;
142 break;
143 }
144 }
145 while(gtk_tree_model_iter_next(model, &child));
146 }
147
148 gtk_tree_store_set(store, file_iter, ENABLED, check, -1);
149 }
150
151 /*
152 * shows a tooltip for a file name
153 */
on_query_tooltip(GtkWidget * widget,gint x,gint y,gboolean keyboard_mode,GtkTooltip * tooltip,gpointer user_data)154 static gboolean on_query_tooltip(GtkWidget *widget, gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip, gpointer user_data)
155 {
156 gboolean show = FALSE;
157 int bx, by;
158 GtkTreePath *tpath = NULL;
159 GtkTreeViewColumn *column = NULL;
160
161 gtk_tree_view_convert_widget_to_bin_window_coords(GTK_TREE_VIEW(widget), x, y, &bx, &by);
162 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bx, by, &tpath, &column, NULL, NULL))
163 {
164 if (1 == gtk_tree_path_get_depth(tpath) && column == gtk_tree_view_get_column(GTK_TREE_VIEW(widget), FILEPATH))
165 {
166 GtkTreeIter iter;
167 gchar *path = NULL;
168
169 gtk_tree_model_get_iter(model, &iter, tpath);
170 gtk_tree_model_get(model, &iter, FILEPATH, &path, -1);
171
172 gtk_tooltip_set_text(tooltip, path);
173
174 gtk_tree_view_set_tooltip_row(GTK_TREE_VIEW(widget), tooltip, tpath);
175
176 show = TRUE;
177 }
178 gtk_tree_path_free(tpath);
179 }
180
181 return show;
182 }
183
184 /*
185 * shows only the file name instead of a full path
186 */
on_render_filename(GtkTreeViewColumn * tree_column,GtkCellRenderer * cell,GtkTreeModel * tree_model,GtkTreeIter * iter,gpointer data)187 static void on_render_filename(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell, GtkTreeModel *tree_model,
188 GtkTreeIter *iter, gpointer data)
189 {
190 gchar *path = NULL;
191 GtkTreePath *tpath;
192
193 gtk_tree_model_get(model, iter, FILEPATH, &path, -1);
194
195 tpath = gtk_tree_model_get_path(model, iter);
196 if (1 != gtk_tree_path_get_depth(tpath))
197 {
198 g_object_set(cell, "text", path, NULL);
199 }
200 else
201 {
202 gchar *name = g_path_get_basename(path);
203 g_object_set(cell, "text", name ? name : path, NULL);
204 g_free(name);
205 }
206
207 if (path)
208 {
209 g_free(path);
210 }
211 }
212
213 /*
214 * hides file checkbox for breaks rows
215 */
on_render_enable_for_file(GtkTreeViewColumn * tree_column,GtkCellRenderer * cell,GtkTreeModel * tree_model,GtkTreeIter * iter,gpointer data)216 static void on_render_enable_for_file(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell, GtkTreeModel *tree_model,
217 GtkTreeIter *iter, gpointer data)
218 {
219 GtkTreePath *path = gtk_tree_model_get_path(model, iter);
220 g_object_set(cell, "visible", 1 == gtk_tree_path_get_depth(path), NULL);
221 gtk_tree_path_free(path);
222 }
223
224 /*
225 * hides break pixbuf for file rows
226 */
on_render_enable_break(GtkTreeViewColumn * tree_column,GtkCellRenderer * cell,GtkTreeModel * tree_model,GtkTreeIter * iter,gpointer data)227 static void on_render_enable_break(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell, GtkTreeModel *tree_model,
228 GtkTreeIter *iter, gpointer data)
229 {
230 GtkTreePath *path = gtk_tree_model_get_path(model, iter);
231 g_object_set(cell, "visible", 1 != gtk_tree_path_get_depth(path), NULL);
232 gtk_tree_path_free(path);
233 }
234
235 /*
236 * makes condition and hitscount uneditable and empty for file rows
237 */
on_render(GtkTreeViewColumn * tree_column,GtkCellRenderer * cell,GtkTreeModel * tree_model,GtkTreeIter * iter,gpointer data)238 static void on_render(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell, GtkTreeModel *tree_model,
239 GtkTreeIter *iter, gpointer data)
240 {
241 GtkTreePath *path = gtk_tree_model_get_path(model, iter);
242 if (gtk_tree_path_get_depth(path) == 1)
243 {
244 g_object_set(cell, "text", "", NULL);
245 g_object_set(cell, "editable", FALSE, NULL);
246 }
247 else
248 {
249 g_object_set(cell, "editable", TRUE, NULL);
250 }
251 gtk_tree_path_free(path);
252 }
253
254 /*
255 * GtkTreeView event handlers
256 */
257
258 /*
259 * double click
260 */
on_row_double_click(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * column,gpointer user_data)261 static void on_row_double_click(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data)
262 {
263 GtkTreeIter iter, parent_iter;
264 gchar *file;
265 int line;
266
267 if (1 == gtk_tree_path_get_depth(path))
268 {
269 return;
270 }
271
272 gtk_tree_model_get_iter (
273 model,
274 &iter,
275 path);
276
277 gtk_tree_model_iter_parent(model, &parent_iter, &iter);
278
279 gtk_tree_model_get (
280 model,
281 &parent_iter,
282 FILEPATH, &file,
283 -1);
284
285 gtk_tree_model_get (
286 model,
287 &iter,
288 LINE, &line,
289 -1);
290
291 /* use callback, supplied in bptree_init */
292 on_break_clicked(file, line);
293
294 g_free(file);
295 }
296
297 /*
298 * editing "condition" column value finished
299 */
on_condition_changed(GtkCellRendererText * renderer,gchar * path,gchar * new_text,gpointer user_data)300 static void on_condition_changed(GtkCellRendererText *renderer, gchar *path, gchar *new_text, gpointer user_data)
301 {
302 gchar *file;
303 int line;
304 gchar* oldcondition;
305 GtkTreeIter iter, parent_iter;
306 GtkTreePath *tree_path = gtk_tree_path_new_from_string (path);
307
308 gtk_tree_model_get_iter (
309 model,
310 &iter,
311 tree_path);
312
313 gtk_tree_model_iter_parent(model, &parent_iter, &iter);
314
315 gtk_tree_model_get (
316 model,
317 &parent_iter,
318 FILEPATH, &file,
319 -1);
320
321 gtk_tree_model_get (
322 model,
323 &iter,
324 CONDITION, &oldcondition,
325 LINE, &line,
326 -1);
327
328 if (strcmp(oldcondition, new_text))
329 breaks_set_condition(file, line, new_text);
330
331 gtk_tree_path_free(tree_path);
332 g_free(file);
333 g_free(oldcondition);
334 }
335
336 /*
337 * editing "hitscount" column value finished
338 */
on_hitscount_changed(GtkCellRendererText * renderer,gchar * path,gchar * new_text,gpointer user_data)339 static void on_hitscount_changed(GtkCellRendererText *renderer, gchar *path, gchar *new_text, gpointer user_data)
340 {
341 GtkTreeIter iter, parent_iter;
342 GtkTreePath *tree_path;
343 gchar *file;
344 int line;
345 gint oldcount;
346 int count = atoi(new_text);
347
348 if (!count && strcmp(new_text, "0"))
349 return;
350
351 tree_path = gtk_tree_path_new_from_string (path);
352
353 gtk_tree_model_get_iter (
354 model,
355 &iter,
356 tree_path);
357
358 gtk_tree_model_iter_parent(model, &parent_iter, &iter);
359
360 gtk_tree_model_get (
361 model,
362 &parent_iter,
363 FILEPATH, &file,
364 -1);
365
366 gtk_tree_model_get (
367 model,
368 &iter,
369 HITSCOUNT, &oldcount,
370 LINE, &line,
371 -1);
372
373 if (oldcount != count)
374 breaks_set_hits_count(file, line, count);
375
376 gtk_tree_path_free(tree_path);
377 g_free(file);
378 }
379
380 /*
381 * enable / disable all breaks for a file when it's checkbox icon has been clicked
382 */
on_enable_for_file(GtkCellRendererToggle * cell_renderer,gchar * path,gpointer user_data)383 static void on_enable_for_file(GtkCellRendererToggle *cell_renderer, gchar *path, gpointer user_data)
384 {
385 GtkTreeIter iter;
386 GtkTreePath *tree_path;
387 gboolean current_state;
388
389 /* do not process event is page is readonly (debug is running) */
390 if (readonly)
391 return;
392
393 tree_path = gtk_tree_path_new_from_string (path);
394
395 gtk_tree_model_get_iter (
396 model,
397 &iter,
398 tree_path);
399
400 current_state = gtk_cell_renderer_toggle_get_active(cell_renderer);
401
402 /* check if this is a file row */
403 if(1 == gtk_tree_path_get_depth(tree_path))
404 {
405 gchar *file;
406 gtk_tree_model_get (
407 model,
408 &iter,
409 FILEPATH, &file,
410 -1);
411
412 breaks_set_enabled_for_file(file, !current_state);
413
414 g_free(file);
415 }
416
417 gtk_tree_path_free(tree_path);
418 }
419
420 /*
421 * enable / disable particulary break when it's icon has been clicked
422 */
on_enable_break(CellRendererBreakIcon * cell_renderer,gchar * path,gpointer user_data)423 static void on_enable_break(CellRendererBreakIcon *cell_renderer, gchar *path, gpointer user_data)
424 {
425 GtkTreeIter iter;
426 GtkTreePath *tree_path;
427
428 /* do not process event is page is readonly (debug is running) */
429 if (readonly)
430 return;
431
432 tree_path = gtk_tree_path_new_from_string (path);
433
434 gtk_tree_model_get_iter (
435 model,
436 &iter,
437 tree_path);
438
439 /* check if this is not a file row */
440 if(1 != gtk_tree_path_get_depth(tree_path))
441 {
442 gchar *file;
443 int line;
444 GtkTreeIter parent_iter;
445
446 gtk_tree_model_iter_parent(model, &parent_iter, &iter);
447
448 gtk_tree_model_get (
449 model,
450 &parent_iter,
451 FILEPATH, &file,
452 -1);
453 gtk_tree_model_get (
454 model,
455 &iter,
456 LINE, &line,
457 -1);
458
459 breaks_switch(file, line);
460
461 g_free(file);
462 }
463
464 gtk_tree_path_free(tree_path);
465 }
466
467 /*
468 * key pressed event
469 */
on_key_pressed(GtkWidget * widget,GdkEvent * event,gpointer user_data)470 static gboolean on_key_pressed(GtkWidget *widget, GdkEvent *event, gpointer user_data)
471 {
472 guint keyval = ((GdkEventKey*)event)->keyval;
473 GtkTreeSelection *selection;
474 GList *rows;
475
476 /* do not process event is page is readonly (debug is running) */
477 if (readonly)
478 return FALSE;
479
480 /* get selected rows */
481 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
482 rows = gtk_tree_selection_get_selected_rows(selection, &model);
483 rows = g_list_sort(rows, (GCompareFunc)gtk_tree_path_compare);
484
485 if (keyval == GDK_Delete && rows && g_list_length(rows))
486 {
487 GList *breaks, *iter;
488 GtkTreeRowReference *new_selection = NULL;
489 GtkTreePath *first_path = (GtkTreePath*)rows->data;
490
491 /* "delete selected rows" */
492
493 /* get new selection */
494 if (gtk_tree_path_get_depth(first_path) > 1)
495 {
496 new_selection = get_unselected_sibling(first_path);
497 }
498 if (!new_selection)
499 {
500 GtkTreePath *file_path = gtk_tree_path_copy(first_path);
501 if (gtk_tree_path_get_depth(file_path) > 1)
502 {
503 gtk_tree_path_up(file_path);
504 }
505 new_selection = get_unselected_sibling(file_path);
506 gtk_tree_path_free(file_path);
507 }
508
509 /* collect GList of breakpoints to remove
510 if file row is met - add all unselected breaks to the list as well */
511 breaks = NULL;
512 for (iter = rows; iter; iter = iter->next)
513 {
514 GtkTreePath *path = (GtkTreePath*)iter->data;
515 GtkTreeIter titer;
516
517 gtk_tree_model_get_iter(model, &titer, path);
518
519 if (1 == gtk_tree_path_get_depth(path))
520 {
521 GtkTreeIter citer;
522 gtk_tree_model_iter_children(model, &citer, &titer);
523
524 do
525 {
526 if (!gtk_tree_selection_iter_is_selected(selection, &citer))
527 {
528 gchar *file = NULL;
529 gint line;
530 breakpoint *bp;
531
532 gtk_tree_model_get(model, &titer, FILEPATH, &file, -1);
533 gtk_tree_model_get(model, &citer, LINE, &line, -1);
534
535 bp = breaks_lookup_breakpoint(file, line);
536
537 breaks = g_list_append(breaks, bp);
538
539 g_free(file);
540 }
541 }
542 while(gtk_tree_model_iter_next(model, &citer));
543 }
544 else
545 {
546 GtkTreeIter piter;
547 gchar *file = NULL;
548 gint line;
549 breakpoint *bp;
550
551 gtk_tree_model_iter_parent(model, &piter, &titer);
552
553 gtk_tree_model_get(model, &piter, FILEPATH, &file, -1);
554
555 gtk_tree_model_get(model, &titer, LINE, &line, -1);
556
557 bp = breaks_lookup_breakpoint(file, line);
558
559 breaks = g_list_append(breaks, bp);
560
561 g_free(file);
562 }
563 }
564
565 if (1 == g_list_length(breaks))
566 {
567 breakpoint *bp = (breakpoint*)breaks->data;
568 g_list_free(breaks);
569 breaks_remove(bp->file, bp->line);
570 }
571 else
572 {
573 breaks_remove_list(breaks);
574 }
575
576 if (new_selection)
577 {
578 /* get path to select */
579 GtkTreePath *path = NULL;
580 path = gtk_tree_row_reference_get_path(new_selection);
581
582 gtk_tree_selection_select_path(selection, path);
583 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(widget), path, NULL, TRUE, 0.5, 0.5);
584 gtk_tree_path_free(path);
585
586 gtk_tree_row_reference_free(new_selection);
587 }
588 }
589
590 /* free rows list */
591 g_list_foreach (rows, (GFunc)gtk_tree_path_free, NULL);
592 g_list_free (rows);
593
594 return FALSE;
595 }
596
597 /*
598 * Interface functions
599 */
600
601 /*
602 * init breaks tree view and return it if succesfull
603 * arguments:
604 * cb - callback to call on treeview double click
605 */
bptree_init(move_to_line_cb cb)606 gboolean bptree_init(move_to_line_cb cb)
607 {
608 GtkTreeSelection *selection;
609 GtkTreeViewColumn *column;
610 GtkCellRenderer *renderer;
611
612 /* save double click callback */
613 on_break_clicked = cb;
614
615 /* crete hash table for file nodes */
616 files = g_hash_table_new_full(
617 g_str_hash,
618 g_str_equal,
619 (GDestroyNotify)g_free,
620 (GDestroyNotify)gtk_tree_row_reference_free
621 );
622
623 /* create tree view */
624 store = gtk_tree_store_new (
625 N_COLUMNS,
626 G_TYPE_STRING,
627 G_TYPE_STRING,
628 G_TYPE_INT,
629 G_TYPE_INT,
630 G_TYPE_BOOLEAN,
631 G_TYPE_STRING);
632 model = GTK_TREE_MODEL(store);
633 tree = gtk_tree_view_new_with_model (model);
634 g_object_unref(store);
635
636 /* set tree view properties */
637 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree), 1);
638 gtk_widget_set_has_tooltip(GTK_WIDGET(tree), TRUE);
639 /* multiple selection */
640 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
641 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
642
643 /* connect signals */
644 g_signal_connect(G_OBJECT(tree), "key-press-event", G_CALLBACK (on_key_pressed), NULL);
645 g_signal_connect(G_OBJECT(tree), "row-activated", G_CALLBACK (on_row_double_click), NULL);
646 g_signal_connect(G_OBJECT(tree), "query-tooltip", G_CALLBACK (on_query_tooltip), NULL);
647
648 /* creating columns */
649
650 /* icon, file */
651 renderer = gtk_cell_renderer_text_new ();
652 column = gtk_tree_view_column_new();
653 gtk_tree_view_column_pack_end(column, renderer, TRUE);
654 gtk_tree_view_column_set_cell_data_func(column, renderer, on_render_filename, NULL, NULL);
655
656 /* enable for file */
657 renderer = cell_renderer_toggle_new ();
658 g_signal_connect (G_OBJECT(renderer), "toggled", G_CALLBACK(on_enable_for_file), NULL);
659 gtk_tree_view_column_pack_end(column, renderer, FALSE);
660 gtk_tree_view_column_set_attributes(column, renderer, "active", ENABLED, NULL);
661 gtk_tree_view_column_set_cell_data_func(column, renderer, on_render_enable_for_file, NULL, NULL);
662
663 /* enable breakpoint */
664 renderer = cell_renderer_break_icon_new ();
665 g_signal_connect (G_OBJECT(renderer), "clicked", G_CALLBACK(on_enable_break), NULL);
666
667 g_object_set(renderer, "pixbuf_enabled", (gpointer)break_pixbuf, NULL);
668 g_object_set(renderer, "pixbuf_disabled", (gpointer)break_disabled_pixbuf, NULL);
669 g_object_set(renderer, "pixbuf_conditional", (gpointer)break_condition_pixbuf, NULL);
670 g_object_set(renderer, "pixbuf_file", (gpointer)break_pixbuf, NULL);
671
672 gtk_tree_view_column_pack_end(column, renderer, FALSE);
673 gtk_tree_view_column_set_attributes(column, renderer, "enabled", ENABLED, "condition", CONDITION, "hitscount", HITSCOUNT, NULL);
674 gtk_tree_view_column_set_cell_data_func(column, renderer, on_render_enable_break, NULL, NULL);
675
676 gtk_tree_view_column_set_title(column, _("Location"));
677 gtk_tree_view_column_set_resizable (column, TRUE);
678 gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
679
680 /* condition */
681 condition_renderer = gtk_cell_renderer_text_new ();
682 g_object_set (condition_renderer, "editable", TRUE, NULL);
683 g_object_set (condition_renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
684 g_signal_connect (G_OBJECT (condition_renderer), "edited", G_CALLBACK (on_condition_changed), NULL);
685 column = gtk_tree_view_column_new_with_attributes (_("Condition"), condition_renderer, "text", CONDITION, NULL);
686 gtk_tree_view_column_set_cell_data_func(column, condition_renderer, on_render, NULL, NULL);
687 gtk_tree_view_column_set_resizable (column, TRUE);
688 gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
689
690 /* hits count */
691 hcount_renderer = gtk_cell_renderer_spin_new ();
692 g_object_set (hcount_renderer,
693 "adjustment", gtk_adjustment_new (0.0, 0.0, 100000.0, 1.0, 2.0, 2.0),
694 "digits", 0, NULL);
695 g_signal_connect (G_OBJECT (hcount_renderer), "edited", G_CALLBACK (on_hitscount_changed), NULL);
696 column = gtk_tree_view_column_new_with_attributes (_("Hit count"), hcount_renderer, "text", HITSCOUNT, NULL);
697 gtk_tree_view_column_set_cell_data_func(column, hcount_renderer, on_render, (gpointer)TRUE, NULL);
698 gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
699
700 /* line */
701 renderer = gtk_cell_renderer_text_new ();
702 column = gtk_tree_view_column_new_with_attributes (_("Line"), renderer, "text", LINE, NULL);
703 gtk_tree_view_column_set_visible(column, FALSE);
704 gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
705
706 /* Last invisible column */
707 renderer = gtk_cell_renderer_text_new ();
708 column = gtk_tree_view_column_new_with_attributes ("", renderer, "text", LAST_VISIBLE, NULL);
709 gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
710
711 tab_breaks = gtk_scrolled_window_new (NULL, NULL);
712 gtk_widget_show (tab_breaks);
713 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (tab_breaks), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
714 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (tab_breaks), GTK_SHADOW_NONE);
715
716 gtk_container_add (GTK_CONTAINER (tab_breaks), tree);
717
718 return TRUE;
719 }
720
721 /*
722 * destroy breaks tree and associated data
723 * arguments:
724 */
bptree_destroy(void)725 void bptree_destroy(void)
726 {
727 g_hash_table_destroy(files);
728 }
729
730 /*
731 * enable/disable break
732 * arguments:
733 * bp - breakpoint
734 */
bptree_set_enabled(breakpoint * bp)735 void bptree_set_enabled(breakpoint *bp)
736 {
737 GtkTreeIter parent;
738
739 gtk_tree_store_set(store, &(bp->iter), ENABLED, bp->enabled, -1);
740
741 gtk_tree_model_iter_parent(model, &parent, &(bp->iter));
742 update_file_node(&parent);
743 }
744
745 /*
746 * set breaks hits count
747 * arguments:
748 * bp - breakpoint
749 */
bptree_set_hitscount(breakpoint * bp)750 void bptree_set_hitscount(breakpoint *bp)
751 {
752 gtk_tree_store_set(store, &(bp->iter), HITSCOUNT, bp->hitscount, -1);
753 }
754
755 /*
756 * set breaks condition
757 * arguments:
758 * bp - breakpoint
759 */
bptree_set_condition(breakpoint * bp)760 void bptree_set_condition(breakpoint* bp)
761 {
762 gtk_tree_store_set(store, &(bp->iter), CONDITION, bp->condition, -1);
763 }
764
765 /*
766 * get breaks condition
767 * arguments:
768 * iter - tree view iterator
769 * return value - breaks condition
770 */
bptree_get_condition(breakpoint * bp)771 gchar* bptree_get_condition(breakpoint *bp)
772 {
773 gchar *condition;
774 gtk_tree_model_get (
775 model,
776 &(bp->iter),
777 CONDITION, &condition,
778 -1);
779
780 return condition;
781 }
782
783 /*
784 * set tree view accessible / inaccessible to user input
785 * arguments:
786 * value - new value
787 */
bptree_set_readonly(gboolean value)788 void bptree_set_readonly(gboolean value)
789 {
790 readonly = value;
791 g_object_set (hcount_renderer, "editable", !readonly, NULL);
792 g_object_set (condition_renderer, "editable", !readonly, NULL);
793 }
794
795 /*
796 * add new breakpoint to the tree view
797 * arguments:
798 * bp - breakpoint to add
799 */
bptree_add_breakpoint(breakpoint * bp)800 void bptree_add_breakpoint(breakpoint* bp)
801 {
802 GtkTreeIter file_iter, iter, child, *sibling = NULL;
803 GtkTreeRowReference *file_reference = (GtkTreeRowReference*)g_hash_table_lookup(files, bp->file);
804
805 if (!file_reference)
806 {
807 GtkTreePath *file_path;
808
809 gtk_tree_store_prepend (store, &file_iter, NULL);
810 gtk_tree_store_set (store, &file_iter,
811 FILEPATH, bp->file,
812 ENABLED, TRUE,
813 -1);
814
815 file_path = gtk_tree_model_get_path(model, &file_iter);
816 file_reference = gtk_tree_row_reference_new(model, file_path);
817 gtk_tree_path_free(file_path);
818
819 g_hash_table_insert(files, (gpointer)g_strdup(bp->file),(gpointer)file_reference);
820 }
821 else
822 {
823 GtkTreePath *path = gtk_tree_row_reference_get_path(file_reference);
824 gtk_tree_model_get_iter(model, &file_iter, path);
825 gtk_tree_path_free(path);
826 }
827
828 /* lookup where to insert new row */
829 if(gtk_tree_model_iter_children(model, &child, &file_iter))
830 {
831 do
832 {
833 int line;
834 gtk_tree_model_get (
835 model,
836 &child,
837 LINE, &line,
838 -1);
839 if (line > bp->line)
840 {
841 sibling = &child;
842 break;
843 }
844 }
845 while(gtk_tree_model_iter_next(model, &child));
846 }
847
848 gtk_tree_store_insert_before(store, &iter, &file_iter, sibling);
849 bp->iter = iter;
850
851 bptree_update_breakpoint(bp);
852 }
853
854 /*
855 * update existing breakpoint
856 * arguments:
857 * bp - breakpoint to update
858 */
bptree_update_breakpoint(breakpoint * bp)859 void bptree_update_breakpoint(breakpoint* bp)
860 {
861 gchar *location = g_strdup_printf(_("line %i"), bp->line);
862
863 gtk_tree_store_set (store, &bp->iter,
864 ENABLED, bp->enabled,
865 HITSCOUNT, bp->hitscount,
866 CONDITION, bp->condition,
867 FILEPATH, location,
868 LINE, bp->line,
869 -1);
870
871 g_free(location);
872 }
873
874 /*
875 * remove breakpoint
876 * arguments:
877 * bp - breakpoint to revove
878 */
bptree_remove_breakpoint(breakpoint * bp)879 void bptree_remove_breakpoint(breakpoint* bp)
880 {
881 GtkTreeIter file;
882 gtk_tree_model_iter_parent(model, &file, &(bp->iter));
883
884 gtk_tree_store_remove(store, &(bp->iter));
885
886 if (!gtk_tree_model_iter_n_children(model, &file))
887 {
888 g_hash_table_remove(files, (gpointer)bp->file);
889 gtk_tree_store_remove(store, &file);
890 }
891 else
892 {
893 update_file_node(&file);
894 }
895 }
896
897 /*
898 * updates all file ENABLED checkboxes base on theit children states
899 * arguments:
900 */
bptree_update_file_nodes(void)901 void bptree_update_file_nodes(void)
902 {
903 GtkTreeIter file;
904 if(gtk_tree_model_iter_children(model, &file, NULL))
905 {
906 do
907 {
908 update_file_node(&file);
909 }
910 while(gtk_tree_model_iter_next(model, &file));
911 }
912 }
913