1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3  * watch.c Copyright (C) 2000 Kh. Naba Kumar Singh
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the Free
7  * Software Foundation; either version 2 of the License, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along with
16  * this program; if not, write to the Free Software Foundation, Inc.,
17  * 59 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 #ifdef HAVE_CONFIG_H
20 #  include <config.h>
21 #endif
22 
23 #include "watch.h"
24 
25 #include "debug_tree.h"
26 #include "utilities.h"
27 
28 #include <glib/gi18n.h>
29 #include <libanjuta/anjuta-debug.h>
30 #include <libanjuta/interfaces/ianjuta-editor-selection.h>
31 #include <libanjuta/interfaces/ianjuta-editor.h>
32 #include <libanjuta/interfaces/ianjuta-document-manager.h>
33 
34 /* Type
35  *---------------------------------------------------------------------------*/
36 
37 struct _ExprWatch
38 {
39 	AnjutaPlugin *plugin;
40 
41 	GtkWidget *scrolledwindow;
42 	DebugTree *debug_tree;
43 	DmaDebuggerQueue *debugger;
44 
45 	/* Menu action */
46 	GtkActionGroup *action_group;
47 	GtkActionGroup *toggle_group;
48 };
49 
50 struct _InspectDialog
51 {
52 	DebugTree *tree;
53 	GtkWidget *treeview;
54 	GtkWidget *dialog;
55 };
56 
57 typedef struct _InspectDialog InspectDialog;
58 
59 /* Widget and signal name found in glade file
60  *---------------------------------------------------------------------------*/
61 
62 #define ADD_WATCH_DIALOG "add_watch_dialog"
63 #define CHANGE_WATCH_DIALOG "change_watch_dialog"
64 #define INSPECT_EVALUATE_DIALOG "watch_dialog"
65 #define VALUE_TREE "watch_value_treeview"
66 #define NAME_ENTRY "add_watch_name_entry"
67 #define VALUE_ENTRY "value_entry"
68 #define AUTO_UPDATE_CHECK "auto_update_check"
69 
70 
71 enum targets {
72 	TARGET_STRING,
73 	TARGET_URL
74 };
75 
76 static const GtkTargetEntry drag_targets[] = {
77 	{"application-x/anjuta", GTK_TARGET_SAME_APP, TARGET_STRING},
78 	{ "STRING",        GTK_TARGET_SAME_APP, TARGET_STRING },
79 	{ "text/plain",    GTK_TARGET_SAME_APP, TARGET_STRING },
80 	{ "text/uri-list", GTK_TARGET_SAME_APP, TARGET_URL }
81 };
82 
83 /* Private functions
84  *---------------------------------------------------------------------------*/
85 
86 static void
debug_tree_inspect_evaluate_dialog(ExprWatch * ew,const gchar * expression)87 debug_tree_inspect_evaluate_dialog (ExprWatch * ew, const gchar* expression)
88 {
89 	GtkBuilder *bxml;
90 	gint reply;
91 	gchar *new_expr;
92 	InspectDialog dlg;
93 	IAnjutaDebuggerVariableObject var = {NULL, NULL, NULL, NULL, FALSE, FALSE, FALSE, -1};
94 
95 	bxml = anjuta_util_builder_new (GLADE_FILE, NULL);
96 	if (!bxml) return;
97 	anjuta_util_builder_get_objects (bxml,
98 	    INSPECT_EVALUATE_DIALOG, &dlg.dialog,
99 	    VALUE_TREE, &dlg.treeview,
100 	    NULL);
101 	g_object_unref (bxml);
102 	gtk_window_set_transient_for (GTK_WINDOW (dlg.dialog), NULL);
103 
104 	/* Create debug tree */
105 	dlg.tree = debug_tree_new_with_view (ANJUTA_PLUGIN (ew->plugin), GTK_TREE_VIEW (dlg.treeview));
106 	if (ew->debugger)
107 		debug_tree_connect (dlg.tree, ew->debugger);
108 	if (expression != NULL)
109 	{
110 		var.expression = (gchar *)expression;
111 		debug_tree_add_watch (dlg.tree, &var, FALSE);
112 	}
113 	else
114 	{
115 		debug_tree_add_dummy (dlg.tree, NULL);
116 	}
117 
118 	for(;;)
119 	{
120 		reply = gtk_dialog_run (GTK_DIALOG (dlg.dialog));
121 		switch (reply)
122 		{
123 		case GTK_RESPONSE_OK:
124 			/* Add in watch window */
125 			new_expr = debug_tree_get_first (dlg.tree);
126 
127 			if ((new_expr != NULL) && (strlen(new_expr) != 0))
128 			{
129 		    	var.expression = new_expr;
130 				debug_tree_add_watch (ew->debug_tree, &var, FALSE);
131 				g_free (new_expr);
132 			}
133 			break;
134 		default:
135 			break;
136 		}
137 		break;
138 	}
139 	debug_tree_free (dlg.tree);
140 	gtk_widget_destroy (dlg.dialog);
141 }
142 
143 static void
debug_tree_add_watch_dialog(ExprWatch * ew,const gchar * expression)144 debug_tree_add_watch_dialog (ExprWatch *ew, const gchar* expression)
145 {
146 	GtkBuilder *bxml;
147 	GtkWidget *dialog;
148 	GtkWidget *name_entry;
149 	GtkWidget *auto_update_check;
150 	gint reply;
151 	IAnjutaDebuggerVariableObject var = {NULL, NULL, NULL, NULL, FALSE, FALSE, FALSE, -1};
152 
153 
154 	bxml = anjuta_util_builder_new (GLADE_FILE, NULL);
155 	if (!bxml) return;
156 	anjuta_util_builder_get_objects (bxml,
157 	    ADD_WATCH_DIALOG, &dialog,
158 	    AUTO_UPDATE_CHECK, &auto_update_check,
159 	    NAME_ENTRY, &name_entry,
160 	    NULL);
161 	g_object_unref (bxml);
162 
163 	gtk_window_set_transient_for (GTK_WINDOW (dialog),
164 								  NULL);
165 
166 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (auto_update_check), TRUE);
167 	gtk_entry_set_text (GTK_ENTRY (name_entry), expression == NULL ? "" : expression);
168 
169 	reply = gtk_dialog_run (GTK_DIALOG (dialog));
170 	if (reply == GTK_RESPONSE_OK)
171 	{
172 		var.expression = (gchar *)gtk_entry_get_text (GTK_ENTRY (name_entry));
173 		debug_tree_add_watch (ew->debug_tree, &var,
174 							  gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (auto_update_check)));
175 	}
176 	gtk_widget_destroy (dialog);
177 }
178 
179 static void
debug_tree_change_watch_dialog(ExprWatch * ew,GtkTreeIter * iter)180 debug_tree_change_watch_dialog (ExprWatch *ew, GtkTreeIter* iter)
181 {
182 #if 0
183  	GladeXML *gxml;
184 	GtkWidget *dialog;
185 	GtkWidget *name_entry;
186 	GtkWidget *value_entry;
187 	gint reply;
188 	TrimmableItem *item = NULL;
189 	GtkTreeModel* model = NULL;
190 
191 	model = gtk_tree_view_get_model(d_tree->view);
192 	gtk_tree_model_get (model, iter, ITEM_COLUMN, &item, -1);
193 
194 	gxml = glade_xml_new (GLADE_FILE, CHANGE_WATCH_DIALOG, NULL);
195 	dialog = glade_xml_get_widget (gxml, CHANGE_WATCH_DIALOG);
196 	gtk_window_set_transient_for (GTK_WINDOW (dialog),
197 								  NULL);
198 	name_entry = glade_xml_get_widget (gxml, NAME_ENTRY);
199 	value_entry = glade_xml_get_widget (gxml, VALUE_ENTRY);
200 	g_object_unref (gxml);
201 
202 	gtk_widget_grab_focus (value_entry);
203 	gtk_entry_set_text (GTK_ENTRY (name_entry), &item->name[1]);
204 	gtk_entry_set_text (GTK_ENTRY (value_entry), item->value);
205 
206 	reply = gtk_dialog_run (GTK_DIALOG (dialog));
207 	if (reply == GTK_RESPONSE_APPLY)
208 	{
209 		debug_tree_evaluate (d_tree, iter, gtk_entry_get_text (GTK_ENTRY (value_entry)));
210 	}
211 	gtk_widget_destroy (dialog);
212 #endif
213 }
214 
215 static void
on_program_exited(ExprWatch * ew)216 on_program_exited (ExprWatch *ew)
217 {
218 	debug_tree_disconnect (ew->debug_tree);
219 
220 	/* Disconnect to other debugger signal */
221 	g_signal_handlers_disconnect_by_func (ew->plugin, G_CALLBACK (on_program_exited), ew);
222 }
223 
224 static void
on_program_started(ExprWatch * ew)225 on_program_started (ExprWatch *ew)
226 {
227 	if (!dma_debugger_queue_is_supported (ew->debugger, HAS_VARIABLE)) return;
228 
229 	debug_tree_connect (ew->debug_tree, ew->debugger);
230 
231 	/* Connect to other debugger signal */
232 	g_signal_connect_swapped (ew->plugin, "program-exited", G_CALLBACK (on_program_exited), ew);
233 }
234 
235 /* Menu call backs
236  *---------------------------------------------------------------------------*/
237 
238 static void
on_debug_tree_inspect(GtkAction * action,gpointer user_data)239 on_debug_tree_inspect (GtkAction *action, gpointer user_data)
240 {
241 	ExprWatch * ew = (ExprWatch *)user_data;
242 	IAnjutaEditor *te = NULL;
243 	gchar *expression = NULL;
244 
245 	/* Get current editor and line */
246 	te = dma_get_current_editor (ANJUTA_PLUGIN (ew->plugin));
247 	if (te == NULL)	return;
248 
249 	expression = ianjuta_editor_selection_get (IANJUTA_EDITOR_SELECTION (te), NULL);
250 	if (expression == NULL)
251 	{
252 		expression = ianjuta_editor_get_current_word (IANJUTA_EDITOR (te), NULL);
253 	}
254 	if (g_regex_match_simple("^\\s*$", expression,G_REGEX_MULTILINE | G_REGEX_DOLLAR_ENDONLY, G_REGEX_MATCH_ANCHORED))
255 	{
256 		expression = NULL;
257 	}
258 
259 	debug_tree_inspect_evaluate_dialog (ew, expression);
260 	g_free (expression);
261 }
262 
263 static void
on_debug_tree_add_watch(GtkAction * action,gpointer user_data)264 on_debug_tree_add_watch (GtkAction *action, gpointer user_data)
265 {
266 	ExprWatch * ew = (ExprWatch *)user_data;
267 
268 	debug_tree_add_watch_dialog (ew, NULL);
269 }
270 
271 static void
on_debug_tree_remove_watch(GtkAction * action,gpointer user_data)272 on_debug_tree_remove_watch (GtkAction *action, gpointer user_data)
273 {
274 	ExprWatch * ew = (ExprWatch *)user_data;
275 	GtkTreeIter iter;
276 
277 	if (debug_tree_get_current (ew->debug_tree, &iter))
278 	{
279 		debug_tree_remove (ew->debug_tree, &iter);
280 	}
281 }
282 
283 static void
on_debug_tree_update_watch(GtkAction * action,gpointer user_data)284 on_debug_tree_update_watch (GtkAction *action, gpointer user_data)
285 {
286 	ExprWatch * ew = (ExprWatch *)user_data;
287 	GtkTreeIter iter;
288 
289 	if (debug_tree_get_current (ew->debug_tree, &iter))
290 	{
291 		debug_tree_update (ew->debug_tree, &iter, TRUE);
292 	}
293 }
294 
295 static void
on_debug_tree_auto_update_watch(GtkAction * action,gpointer user_data)296 on_debug_tree_auto_update_watch (GtkAction *action, gpointer user_data)
297 {
298 	ExprWatch * ew = (ExprWatch *)user_data;
299 	GtkTreeIter iter;
300 
301 	if (debug_tree_get_current (ew->debug_tree, &iter))
302 	{
303 		gboolean state;
304 
305 		state = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
306 		debug_tree_set_auto_update (ew->debug_tree, &iter, state);
307 	}
308 }
309 
310 static void
on_debug_tree_edit_watch(GtkAction * action,gpointer user_data)311 on_debug_tree_edit_watch (GtkAction *action, gpointer user_data)
312 {
313 	ExprWatch * ew = (ExprWatch *)user_data;
314 	GtkTreeIter iter;
315 
316 	if (debug_tree_get_current (ew->debug_tree, &iter))
317 	{
318 		debug_tree_change_watch_dialog (ew, &iter);
319 	}
320 }
321 
322 static void
on_debug_tree_update_all_watch(GtkAction * action,gpointer user_data)323 on_debug_tree_update_all_watch (GtkAction *action, gpointer user_data)
324 {
325 	ExprWatch * ew = (ExprWatch *)user_data;
326 
327 	debug_tree_update_tree (ew->debug_tree);
328 }
329 
330 static void
on_debug_tree_remove_all_watch(GtkAction * action,gpointer user_data)331 on_debug_tree_remove_all_watch (GtkAction *action, gpointer user_data)
332 {
333 	ExprWatch * ew = (ExprWatch *)user_data;
334 
335 	debug_tree_remove_all (ew->debug_tree);
336 }
337 
338 static gboolean
on_debug_tree_button_press(GtkWidget * widget,GdkEventButton * bevent,gpointer user_data)339 on_debug_tree_button_press (GtkWidget *widget, GdkEventButton *bevent, gpointer user_data)
340 {
341 	ExprWatch * ew = (ExprWatch *)user_data;
342 
343 	if (bevent->button == 3)
344 	{
345 		GtkAction *action;
346 		AnjutaUI *ui;
347 		GtkTreeIter iter;
348 		GtkWidget *middle_click_menu;
349 
350 		ui = anjuta_shell_get_ui (ew->plugin->shell, NULL);
351 		action = anjuta_ui_get_action (ui, "ActionGroupWatchToggle", "ActionDmaAutoUpdateWatch");
352 		if (debug_tree_get_current (ew->debug_tree, &iter))
353 		{
354 			gtk_action_set_sensitive (GTK_ACTION (action), TRUE);
355 			gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), debug_tree_get_auto_update (ew->debug_tree, &iter));
356 		}
357 		else
358 		{
359 			gtk_action_set_sensitive (GTK_ACTION (action), FALSE);
360 		}
361 
362 		action = anjuta_ui_get_action (ui, "ActionGroupWatch", "ActionDmaEditWatch");
363 		gtk_action_set_sensitive (GTK_ACTION (action), FALSE);   // FIXME: Not implemented
364 
365 		middle_click_menu = gtk_ui_manager_get_widget (GTK_UI_MANAGER (ui), "/PopupWatch");
366 		g_return_val_if_fail (middle_click_menu != NULL, FALSE);
367 		gtk_menu_popup (GTK_MENU (middle_click_menu), NULL, NULL, NULL, NULL,
368 						bevent->button, bevent->time);
369 	}
370 
371 	return FALSE;
372 }
373 
374 static void
on_debug_tree_drag_data_received(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint timestamp,gpointer user_data)375 on_debug_tree_drag_data_received (GtkWidget        *widget,
376 								  GdkDragContext   *context,
377 								  gint              x,
378 								  gint              y,
379 								  GtkSelectionData *selection_data,
380 								  guint             info,
381 								  guint             timestamp,
382 								  gpointer          user_data)
383 {
384 	const gchar* signal_data = (gchar*) gtk_selection_data_get_data (selection_data);
385 	IAnjutaDebuggerVariableObject var = {NULL, NULL, NULL, NULL, FALSE, FALSE, FALSE, -1};
386 
387 	if (signal_data != NULL)
388 	{
389 		var.expression = (gchar *)signal_data;
390 		debug_tree_add_watch (((ExprWatch *)user_data)->debug_tree, &var, FALSE);
391 	}
392 
393 	gtk_drag_finish (context, FALSE, FALSE, timestamp);
394 
395 	return;
396 }
397 
398 
399 /* Actions table
400  *---------------------------------------------------------------------------*/
401 
402 static GtkActionEntry actions_watch[] = {
403     {
404 		"ActionDmaInspect",                      /* Action name */
405 		GTK_STOCK_DIALOG_INFO,                   /* Stock icon, if any */
406 		N_("Ins_pect/Evaluate…"),              /* Display label */
407 		NULL,                                    /* short-cut */
408 		N_("Inspect or evaluate an expression or variable"), /* Tooltip */
409 		G_CALLBACK (on_debug_tree_inspect) /* action callback */
410     },
411 	{
412 		"ActionDmaAddWatch",
413 		NULL,
414 		N_("Add Watch…"),
415 		NULL,
416 		NULL,
417 		G_CALLBACK (on_debug_tree_add_watch)
418 	},
419 	{
420 		"ActionDmaRemoveWatch",
421 		NULL,
422 		N_("Remove Watch"),
423 		NULL,
424 		NULL,
425 		G_CALLBACK (on_debug_tree_remove_watch)
426 	},
427 	{
428 		"ActionDmaUpdateWatch",
429 		NULL,
430 		N_("Update Watch"),
431 		NULL,
432 		NULL,
433 		G_CALLBACK (on_debug_tree_update_watch)
434 	},
435 	{
436 		"ActionDmaEditWatch",
437 		NULL,
438 		N_("Change Value"),
439 		NULL,
440 		NULL,
441 		G_CALLBACK (on_debug_tree_edit_watch)
442 	},
443 	{
444 		"ActionDmaUpdateAllWatch",
445 		NULL,
446 		N_("Update all"),
447 		NULL,
448 		NULL,
449 		G_CALLBACK (on_debug_tree_update_all_watch)
450 	},
451 	{
452 		"ActionDmaRemoveAllWatch",
453 		NULL,
454 		N_("Remove all"),
455 		NULL,
456 		NULL,
457 		G_CALLBACK (on_debug_tree_remove_all_watch)
458 	}
459 };
460 
461 static GtkToggleActionEntry toggle_watch[] = {
462 	{
463 		"ActionDmaAutoUpdateWatch",               /* Action name */
464 		NULL,                                     /* Stock icon, if any */
465 		N_("Automatic update"),                   /* Display label */
466 		NULL,                                     /* short-cut */
467 		NULL,                                     /* Tooltip */
468 		G_CALLBACK (on_debug_tree_auto_update_watch), /* action callback */
469 		FALSE                                     /* Initial state */
470 	}
471 };
472 
473 static void
create_expr_watch_gui(ExprWatch * ew)474 create_expr_watch_gui (ExprWatch * ew)
475 {
476 	AnjutaUI *ui;
477 
478 	ew->debug_tree = debug_tree_new (ew->plugin);
479 	ew->scrolledwindow = gtk_scrolled_window_new (NULL, NULL);
480 	gtk_widget_show (ew->scrolledwindow);
481 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (ew->scrolledwindow),
482 									GTK_POLICY_AUTOMATIC,
483 									GTK_POLICY_AUTOMATIC);
484 	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (ew->scrolledwindow),
485 										 GTK_SHADOW_IN);
486 	gtk_container_add (GTK_CONTAINER (ew->scrolledwindow), debug_tree_get_tree_widget (ew->debug_tree));
487 
488 	ui = anjuta_shell_get_ui (ew->plugin->shell, NULL);
489 	ew->action_group =
490 	      anjuta_ui_add_action_group_entries (ui, "ActionGroupWatch",
491 											_("Watch operations"),
492 											actions_watch,
493 											G_N_ELEMENTS (actions_watch),
494 											GETTEXT_PACKAGE, TRUE, ew);
495 	ew->toggle_group =
496 		      anjuta_ui_add_toggle_action_group_entries (ui, "ActionGroupWatchToggle",
497 											_("Watch operations"),
498 											toggle_watch,
499 											G_N_ELEMENTS (toggle_watch),
500 											GETTEXT_PACKAGE, TRUE, ew);
501 	g_signal_connect (debug_tree_get_tree_widget (ew->debug_tree), "button-press-event", G_CALLBACK (on_debug_tree_button_press), ew);
502 
503 	gtk_drag_dest_set(debug_tree_get_tree_widget (ew->debug_tree), GTK_DEST_DEFAULT_ALL, drag_targets, sizeof (drag_targets) / sizeof (drag_targets[0]), GDK_ACTION_COPY|GDK_ACTION_MOVE|GDK_ACTION_LINK);
504 	g_signal_connect (debug_tree_get_tree_widget (ew->debug_tree), "drag_data_received", G_CALLBACK (on_debug_tree_drag_data_received), ew);
505 
506 	gtk_widget_show_all (ew->scrolledwindow);
507 }
508 
509 /* Public function
510  *---------------------------------------------------------------------------*/
511 
512 gchar*
expr_watch_find_variable_value(ExprWatch * ew,const gchar * name)513 expr_watch_find_variable_value (ExprWatch *ew, const gchar *name)
514 {
515 	return debug_tree_find_variable_value (ew->debug_tree, name);
516 }
517 
518 /* Callback for saving session
519  *---------------------------------------------------------------------------*/
520 
521 static void
on_session_save(AnjutaShell * shell,AnjutaSessionPhase phase,AnjutaSession * session,ExprWatch * ew)522 on_session_save (AnjutaShell *shell, AnjutaSessionPhase phase, AnjutaSession *session, ExprWatch *ew)
523 {
524 	GList *list;
525 
526 	if (phase != ANJUTA_SESSION_PHASE_NORMAL)
527 		return;
528 
529 	list = debug_tree_get_full_watch_list (ew->debug_tree);
530 	if (list != NULL)
531 		anjuta_session_set_string_list (session, "Debugger", "Watch", list);
532 	g_list_foreach (list, (GFunc)g_free, NULL);
533     g_list_free (list);
534 }
535 
536 static void
on_session_load(AnjutaShell * shell,AnjutaSessionPhase phase,AnjutaSession * session,ExprWatch * ew)537 on_session_load (AnjutaShell *shell, AnjutaSessionPhase phase, AnjutaSession *session, ExprWatch *ew)
538 {
539 	GList *list;
540 
541 	if (phase != ANJUTA_SESSION_PHASE_NORMAL)
542 		return;
543 
544 	debug_tree_remove_all (ew->debug_tree);
545 	list = anjuta_session_get_string_list (session, "Debugger", "Watch");
546 	if (list != NULL)
547 		debug_tree_add_full_watch_list (ew->debug_tree, list);
548 }
549 
550 /* Constructor & Destructor
551  *---------------------------------------------------------------------------*/
552 
553 ExprWatch *
expr_watch_new(AnjutaPlugin * plugin)554 expr_watch_new (AnjutaPlugin *plugin)
555 {
556 	ExprWatch *ew;
557 
558 	ew = g_new0 (ExprWatch, 1);
559 	ew->plugin = plugin;
560 	create_expr_watch_gui (ew);
561 	ew->debugger = dma_debug_manager_get_queue (ANJUTA_PLUGIN_DEBUG_MANAGER (plugin));
562 
563 	/* Connect to Load and Save event */
564 	g_signal_connect (ew->plugin->shell, "save-session",
565 					  G_CALLBACK (on_session_save), ew);
566     	g_signal_connect (ew->plugin->shell, "load-session",
567 					  G_CALLBACK (on_session_load), ew);
568 
569 	/* Add watch window */
570 	anjuta_shell_add_widget (ew->plugin->shell,
571 							 ew->scrolledwindow,
572                              "AnjutaDebuggerWatch", _("Watches"),
573                              "gdb-watch-icon", ANJUTA_SHELL_PLACEMENT_BOTTOM,
574                               NULL);
575 
576 	/* Connect to debugger */
577 	g_signal_connect_swapped (ew->plugin, "program-started", G_CALLBACK (on_program_started), ew);
578 
579 	return ew;
580 }
581 
582 void
expr_watch_destroy(ExprWatch * ew)583 expr_watch_destroy (ExprWatch * ew)
584 {
585 	AnjutaUI *ui;
586 
587 	g_return_if_fail (ew != NULL);
588 
589 	/* Disconnect from Load and Save event */
590 	g_signal_handlers_disconnect_matched (ew->plugin->shell, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, ew);
591 	g_signal_handlers_disconnect_matched (ew->plugin, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, ew);
592 
593 	ui = anjuta_shell_get_ui (ew->plugin->shell, NULL);
594 	anjuta_ui_remove_action_group (ui, ew->action_group);
595 	anjuta_ui_remove_action_group (ui, ew->toggle_group);
596 
597 	debug_tree_free (ew->debug_tree);
598 	gtk_widget_destroy (ew->scrolledwindow);
599 	g_free (ew);
600 }
601