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