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