1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3     locals.c
4     Copyright (C) 2000  Kh. Naba Kumar Singh
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 laterdversion.
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, MA  02110-1301  USA
19 */
20 #ifdef HAVE_CONFIG_H
21 #  include <config.h>
22 #endif
23 
24 #include "locals.h"
25 
26 #include "debug_tree.h"
27 
28 #include "queue.h"
29 
30 /*#define DEBUG*/
31 #include <libanjuta/anjuta-debug.h>
32 
33 
34 typedef struct _DmaThreadLocal
35 {
36 	GtkTreeModel *model;
37 	gint thread;
38 	guint frame;
39 } DmaThreadLocal;
40 
41 typedef struct _DmaThreadAndFrame
42 {
43 	gint thread;
44 	guint frame;
45 } DmaThreadAndFrame;
46 
47 struct _Locals
48 {
49 	AnjutaPlugin *plugin;
50 	DmaDebuggerQueue *debugger;
51 
52 	GtkWidget *main_w;
53 	DebugTree *debug_tree;
54 
55 	DmaThreadLocal* current;
56 	GList *list;
57 };
58 
59 static void
locals_updated(const gpointer data,gpointer user_data,GError * error)60 locals_updated (const gpointer data, gpointer user_data, GError *error)
61 {
62 	const GList *list = (const GList *)data;
63 	Locals *self = (Locals*) user_data;
64 
65 	g_return_if_fail (self != NULL);
66 
67 	if (error != NULL)
68 		return;
69 
70 	debug_tree_replace_list (self->debug_tree, list);
71 }
72 
73 /* Private functions
74  *---------------------------------------------------------------------------*/
75 
76 static void
create_locals_gui(Locals * self)77 create_locals_gui (Locals *self)
78 {
79 	g_return_if_fail (self->debug_tree == NULL);
80 	g_return_if_fail (self->main_w == NULL);
81 
82 	self->debug_tree = debug_tree_new (self->plugin);
83 	debug_tree_connect (self->debug_tree, self->debugger);
84 
85 	/* Create local window */
86 	GtkWidget *main_w;
87 
88 	main_w = gtk_scrolled_window_new (NULL, NULL);
89 	gtk_widget_show (main_w);
90 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (main_w),
91 									GTK_POLICY_AUTOMATIC,
92 									GTK_POLICY_AUTOMATIC);
93 	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (main_w),
94 										 GTK_SHADOW_IN);
95 	gtk_container_add (GTK_CONTAINER (main_w), debug_tree_get_tree_widget (self->debug_tree));
96 	gtk_widget_show_all (main_w);
97 	self->main_w = main_w;
98 
99 	anjuta_shell_add_widget (self->plugin->shell,
100 							 self->main_w,
101 							 /* This is the list of local variables. */
102 							 "AnjutaDebuggerLocals", _("Locals"),
103 							 "gdb-locals-icon", ANJUTA_SHELL_PLACEMENT_BOTTOM,
104 							 NULL);
105 }
106 
107 static void
destroy_locals_gui(Locals * self)108 destroy_locals_gui (Locals *self)
109 {
110 	if (self->debug_tree != NULL)
111 	{
112 		debug_tree_free (self->debug_tree);
113 		self->debug_tree = NULL;
114 	}
115 
116 	if (self->main_w != NULL)
117 	{
118 		gtk_widget_destroy (GTK_WIDGET (self->main_w));
119 		self->main_w = NULL;
120 	}
121 }
122 
123 static void
on_clear_locals(gpointer data,gpointer user_data)124 on_clear_locals (gpointer data, gpointer user_data)
125 {
126 	DmaThreadLocal *frame = (DmaThreadLocal *) data;
127 	Locals *self = (Locals *)user_data;
128 
129 	debug_tree_remove_model (self->debug_tree, frame->model);
130 	g_object_unref (G_OBJECT (frame->model));
131 	g_free (frame);
132 }
133 
134 static void
dma_thread_clear_all_locals(Locals * self)135 dma_thread_clear_all_locals (Locals *self)
136 {
137 	/* Clear all GtkListStore */
138 	g_list_foreach (self->list, (GFunc)on_clear_locals, self);
139 	g_list_free (self->list);
140 
141 	self->current = NULL;
142 	self->list = NULL;
143 }
144 
145 static gboolean
on_find_local(gconstpointer a,gconstpointer b)146 on_find_local (gconstpointer a, gconstpointer b)
147 {
148 	const DmaThreadLocal *frame = (const DmaThreadLocal *)a;
149 	const DmaThreadAndFrame *comp = (const DmaThreadAndFrame *)b;
150 
151 	return !((frame->thread == comp->thread) && (frame->frame == comp->frame));
152 }
153 
154 static DmaThreadLocal *
dma_thread_find_local(Locals * self,gint thread,guint frame)155 dma_thread_find_local (Locals *self, gint thread, guint frame)
156 {
157 	GList *list;
158 	DmaThreadAndFrame comp = {thread, frame};
159 
160 	list = g_list_find_custom (self->list, (gconstpointer) &comp, on_find_local);
161 
162 	return list == NULL ? NULL : (DmaThreadLocal *)list->data;
163 }
164 
165 static void
dma_thread_add_local(Locals * self,GtkTreeModel * model,gint thread,guint frame)166 dma_thread_add_local (Locals *self, GtkTreeModel *model, gint thread, guint frame)
167 {
168 	DmaThreadLocal *local;
169 
170 	local = g_new (DmaThreadLocal, 1);
171 	local->thread = thread;
172 	local->frame = frame;
173 	local->model = model;
174 	g_object_ref (G_OBJECT (model));
175 
176 	self->list = g_list_append (self->list, local);
177 	self->current = local;
178 }
179 
180 /* Private functions
181  *---------------------------------------------------------------------------*/
182 
locals_update(Locals * self,gint thread)183 static void locals_update (Locals *self, gint thread)
184 {
185 	GList *list;
186 	DmaThreadLocal *frame;
187 
188 	/* Delete all local tree except the main one. It allow to keep the selected
189 	 * and the expanded item in the common case. */
190 	self->current = NULL;
191 	for (list = g_list_first (self->list); list != NULL;)
192 	{
193 		frame = (DmaThreadLocal *)list->data;
194 
195 		if ((frame->thread == thread) && (frame->frame == 0))
196 		{
197 			self->current = frame;
198 			debug_tree_set_model (self->debug_tree, frame->model);
199 			list = g_list_next (list);
200 		}
201 		else
202 		{
203 			GList *next;
204 
205 			debug_tree_remove_model (self->debug_tree, frame->model);
206 			g_object_unref (G_OBJECT (frame->model));
207 			g_free (frame);
208 			next = g_list_next (list);
209 			self->list = g_list_delete_link (self->list, list);
210 			list = next;
211 		}
212 	}
213 
214 	/* Add new frame if needed */
215 	if (self->current == NULL)
216 	{
217 		dma_thread_add_local (self, debug_tree_get_model (self->debug_tree), thread, 0);
218 	}
219 
220 	/* Update all variables in all tree, so including watches */
221 	debug_tree_update_all (self->debugger);
222 
223 	/* List new local variables and display them in local window */
224 	dma_queue_list_local (self->debugger, locals_updated, self);
225 }
226 
227 static void
locals_change_frame(Locals * self,guint frame,gint thread)228 locals_change_frame (Locals *self, guint frame, gint thread)
229 {
230 	DmaThreadLocal *local;
231 
232 	/* Check if we really need a change */
233 	if ((self->current != NULL) && (self->current->thread == thread) && (self->current->frame == frame)) return;
234 
235 	local = dma_thread_find_local (self, thread, frame);
236 	if (local != NULL)
237 	{
238 		/* Find frame already read */
239 		self->current = local;
240 		debug_tree_set_model (self->debug_tree, self->current->model);
241 
242 		return;
243 	}
244 
245 	debug_tree_new_model (self->debug_tree);
246 	dma_thread_add_local (self, debug_tree_get_model (self->debug_tree), thread, frame);
247 
248 	/* List new local variables and display them in local window */
249 	dma_queue_list_local (self->debugger, locals_updated, self);
250 }
251 
252 static void
on_program_moved(Locals * self,guint pid,gint thread)253 on_program_moved (Locals *self, guint pid, gint thread)
254 {
255 	locals_update (self, thread);
256 }
257 
258 static void
on_frame_changed(Locals * self,guint frame,gint thread)259 on_frame_changed (Locals *self, guint frame, gint thread)
260 {
261 	/* Change thread and frame*/
262 	locals_change_frame (self, frame, thread);
263 }
264 
265 static void
on_program_exited(Locals * self)266 on_program_exited (Locals *self)
267 {
268 	/* Disconnect signals */
269 	g_signal_handlers_disconnect_by_func (self->plugin, on_program_exited, self);
270 	g_signal_handlers_disconnect_by_func (self->plugin, on_program_moved, self);
271 	g_signal_handlers_disconnect_by_func (self->plugin, on_frame_changed, self);
272 
273 	dma_thread_clear_all_locals (self);
274 
275 	destroy_locals_gui (self);
276 }
277 
278 static void
on_program_started(Locals * self)279 on_program_started (Locals *self)
280 {
281 	if (!dma_debugger_queue_is_supported (self->debugger, HAS_VARIABLE)) return;
282 
283 	create_locals_gui (self);
284 
285 	g_signal_connect_swapped (self->plugin, "program-exited", G_CALLBACK (on_program_exited), self);
286 	g_signal_connect_swapped (self->plugin, "program-moved", G_CALLBACK (on_program_moved), self);
287 	g_signal_connect_swapped (self->plugin, "frame-changed", G_CALLBACK (on_frame_changed), self);
288 }
289 
290 /* Public function
291  *---------------------------------------------------------------------------*/
292 
293 gchar*
locals_find_variable_value(Locals * l,const gchar * name)294 locals_find_variable_value (Locals *l, const gchar *name)
295 {
296 	return debug_tree_find_variable_value (l->debug_tree, name);
297 }
298 
299 /* Constructor & Destructor
300  *---------------------------------------------------------------------------*/
301 
302 Locals *
locals_new(DebugManagerPlugin * plugin)303 locals_new (DebugManagerPlugin *plugin)
304 {
305 	Locals *self = g_new0 (Locals, 1);
306 
307 	self->plugin = ANJUTA_PLUGIN (plugin);
308 	self->debugger = dma_debug_manager_get_queue (plugin);
309 
310 	g_signal_connect_swapped (self->plugin, "program-started", G_CALLBACK (on_program_started), self);
311 
312 	return self;
313 }
314 
315 void
locals_free(Locals * self)316 locals_free (Locals *self)
317 {
318 	g_return_if_fail (self != NULL);
319 
320 	g_signal_handlers_disconnect_matched (self->plugin, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
321 
322 	dma_thread_clear_all_locals (self);
323 
324 	/* Destroy gui */
325 	destroy_locals_gui (self);
326 
327 	g_free (self);
328 }
329