1 /*
2  *  scope.c
3  *
4  *  Copyright 2012 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
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, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23 
24 #include <stdlib.h>
25 
26 #include "common.h"
27 
28 #include <gp_gtkcompat.h>
29 
30 GeanyPlugin *geany_plugin;
31 GeanyData *geany_data;
32 
33 PLUGIN_VERSION_CHECK(224)
34 
35 PLUGIN_SET_TRANSLATABLE_INFO(LOCALEDIR, GETTEXT_PACKAGE, _("Scope Debugger"),
36 	_("Relatively simple GDB front-end."), "0.94",
37 	"Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>, LarsGit223")
38 
39 /* Keybinding(s) */
40 enum
41 {	/* Note: must match debug_menu_keys/items */
42 	SET_PROGRAM_KB,
43 	RUN_CONTINUE_KB,
44 	GOTO_CURSOR_KB,
45 	GOTO_SOURCE_KB,
46 	STEP_INTO_KB,
47 	STEP_OVER_KB,
48 	STEP_OUT_KB,
49 	TERMINATE_KB,
50 	BREAKPOINT_KB,
51 	GDB_COMMAND_KB,
52 #ifdef G_OS_UNIX
53 	SHOW_TERMINAL_KB,
54 #endif
55 	EVALUATE_KB,
56 	WATCH_KB,
57 	INSPECT_KB,
58 	COUNT_KB
59 };
60 
61 static MenuKey debug_menu_keys[EVALUATE_KB] =
62 {
63 	{ "setup_program", N_("Setup program") },
64 	{ "run_continue",  N_("Run/continue") },
65 	{ "goto_cursor",   N_("Run to cursor") },
66 	{ "goto_source",   N_("Run to source") },
67 	{ "step_into",     N_("Step into") },
68 	{ "step_over",     N_("Step over") },
69 	{ "step_out",      N_("Step out") },
70 	{ "terminate",     N_("Terminate") },
71 	{ "breakpoint",    N_("Toggle breakpoint") },
72 #ifdef G_OS_UNIX
73 	{ "gdb_command",   N_("GDB command") },
74 	{ "show_terminal", N_("Show terminal") }
75 #else
76 	{ "gdb_command",   N_("GDB command") }
77 #endif
78 };
79 
on_scope_gdb_command(G_GNUC_UNUSED const MenuItem * menu_item)80 static void on_scope_gdb_command(G_GNUC_UNUSED const MenuItem *menu_item)
81 {
82 	view_command_line(NULL, NULL, NULL, FALSE);
83 }
84 
on_scope_reset_markers(G_GNUC_UNUSED const MenuItem * menu_item)85 static void on_scope_reset_markers(G_GNUC_UNUSED const MenuItem *menu_item)
86 {
87 	utils_remark(document_get_current());
88 }
89 
on_scope_cleanup_files(G_GNUC_UNUSED const MenuItem * menu_item)90 static void on_scope_cleanup_files(G_GNUC_UNUSED const MenuItem *menu_item)
91 {
92 	guint i = 0;
93 
94 	foreach_document(i)
95 	{
96 		if (utils_attrib(documents[i], SCOPE_OPEN))
97 			document_close(documents[i]);
98 	}
99 }
100 
on_scope_recent_item(G_GNUC_UNUSED const MenuItem * menu_item)101 static void on_scope_recent_item(G_GNUC_UNUSED const MenuItem *menu_item)
102 {
103 }
104 
105 /* Menu item(s) */
106 #define DS_RUNNABLE (DS_INACTIVE | DS_DEBUG | DS_HANGING)
107 #define DS_JUMPABLE (DS_DEBUG | DS_EXTRA_2)
108 #define DS_SOURCABLE (DS_DEBUG | DS_EXTRA_3)
109 #define DS_STEPABLE (DS_DEBUG | DS_EXTRA_1)
110 #define DS_BREAKABLE (DS_NOT_BUSY | DS_EXTRA_2)
111 #define DS_RECENT (DS_INACTIVE | DS_EXTRA_4)
112 
113 static MenuItem debug_menu_items[] =
114 {
115 	{ "program_setup",       on_program_setup,       0,            NULL, NULL },
116 	{ "debug_run_continue",  on_debug_run_continue,  DS_RUNNABLE,  NULL, NULL },
117 	{ "debug_goto_cursor",   on_debug_goto_cursor,   DS_JUMPABLE,  NULL, NULL },
118 	{ "debug_goto_source",   on_debug_goto_source,   DS_SOURCABLE, NULL, NULL },
119 	{ "debug_step_into",     on_debug_step_into,     DS_STEPABLE,  NULL, NULL },
120 	{ "debug_step_over",     on_debug_step_over,     DS_STEPABLE,  NULL, NULL },
121 	{ "debug_step_out",      on_debug_step_out,      DS_DEBUG,     NULL, NULL },
122 	{ "debug_terminate",     on_debug_terminate,     DS_ACTIVE,    NULL, NULL },
123 	{ "break_toggle",        on_break_toggle,        DS_BREAKABLE, NULL, NULL },
124 	{ "scope_gdb_command",   on_scope_gdb_command,   DS_ACTIVE,    NULL, NULL },
125 #ifdef G_OS_UNIX
126 	{ "terminal_show",       on_terminal_show,       0,            NULL, NULL },
127 #endif
128 	{ "scope_reset_markers", on_scope_reset_markers, 0,            NULL, NULL },
129 	{ "scope_cleanup_files", on_scope_cleanup_files, 0,            NULL, NULL },
130 	{ "scope_recent_item",   on_scope_recent_item,   DS_RECENT,    NULL, NULL },
131 	{ NULL, NULL, 0, NULL, NULL }
132 };
133 
debug_menu_extra_state(void)134 static guint debug_menu_extra_state(void)
135 {
136 	GeanyDocument *doc = document_get_current();
137 
138 	return ((thread_state >= THREAD_AT_SOURCE) << DS_INDEX_1) |
139 		((doc && utils_source_document(doc)) << DS_INDEX_2) |
140 		((thread_state == THREAD_AT_ASSEMBLER) << DS_INDEX_3) |
141 		(recent_menu_items() << DS_INDEX_4);
142 }
143 
144 static MenuInfo debug_menu_info = { debug_menu_items, debug_menu_extra_state, 0 };
145 
on_scope_key(guint key_id)146 static void on_scope_key(guint key_id)
147 {
148 	menu_item_execute(&debug_menu_info, debug_menu_items + key_id, FALSE);
149 }
150 
151 typedef struct _ToolItem
152 {
153 	gint index;
154 	const char *icon[2];
155 	GtkWidget *widget;
156 	const char *tooltip_text;
157 } ToolItem;
158 
159 static ToolItem toolbar_items[] =
160 {
161 	{ RUN_CONTINUE_KB, { "small_run_continue_icon", "large_run_continue_icon" }, NULL, N_("Run/continue")      },
162 	{ GOTO_CURSOR_KB,  { "small_goto_cursor_icon",  "large_goto_cursor_icon"  }, NULL, N_("Run to cursor")     },
163 	{ GOTO_SOURCE_KB,  { "small_goto_source_icon",  "large_goto_source_icon"  }, NULL, N_("Run to source")     },
164 	{ STEP_INTO_KB,    { "small_step_into_icon",    "large_step_into_icon"    }, NULL, N_("Step into")         },
165 	{ STEP_OVER_KB,    { "small_step_over_icon",    "large_step_over_icon"    }, NULL, N_("Step over")         },
166 	{ STEP_OUT_KB,     { "small_step_out_icon",     "large_step_out_icon"     }, NULL, N_("Step out")          },
167 	{ TERMINATE_KB,    { "small_terminate_icon",    "large_terminate_icon"    }, NULL, N_("Terminate")         },
168 	{ BREAKPOINT_KB,   { "small_breakpoint_icon",   "large_breakpoint_icon",  }, NULL, N_("Toggle breakpoint") },
169 	{ -1, { NULL, NULL }, NULL, NULL }
170 };
171 
on_toolbar_button_clicked(G_GNUC_UNUSED GtkToolButton * toolbutton,gpointer gdata)172 static void on_toolbar_button_clicked(G_GNUC_UNUSED GtkToolButton *toolbutton, gpointer gdata)
173 {
174 	menu_item_execute(&debug_menu_info, debug_menu_items + GPOINTER_TO_INT(gdata), TRUE);
175 }
176 
on_toolbar_reconfigured(GtkToolItem * tool_item,ToolItem * item)177 static void on_toolbar_reconfigured(GtkToolItem *tool_item, ToolItem *item)
178 {
179 	GtkToolShell *shell = GTK_TOOL_SHELL(gtk_widget_get_parent(item->widget));
180 	gboolean large = gtk_tool_shell_get_icon_size(shell) > GTK_ICON_SIZE_MENU;
181 	gchar *tooltip = NULL;
182 
183 	if (gtk_tool_shell_get_style(shell) == GTK_TOOLBAR_ICONS)
184 	{
185 		GtkMenuItem *menu_item = GTK_MENU_ITEM(debug_menu_items[item->index].widget);
186 		tooltip = g_strdup(gtk_menu_item_get_label(menu_item));
187 		utils_str_remove_chars(tooltip, "_");
188 	}
189 
190 	gtk_tool_item_set_tooltip_text(tool_item, tooltip);
191 	g_free(tooltip);
192 	gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(tool_item),
193 		get_widget(item->icon[large]));
194 }
195 
toolbar_update_state(DebugState state)196 static void toolbar_update_state(DebugState state)
197 {
198 	static DebugState last_state = 0;
199 	state |= debug_menu_extra_state();
200 
201 	if (state != last_state)
202 	{
203 		ToolItem *item;
204 
205 		for (item = toolbar_items; item->index != -1; item++)
206 		{
207 			gtk_widget_set_sensitive(item->widget,
208 				menu_item_matches_state(debug_menu_items + item->index, state));
209 		}
210 
211 		last_state = state;
212 	}
213 }
214 
215 static GtkStatusbar *geany_statusbar;
216 static GtkWidget *debug_statusbar;
217 static GtkLabel *debug_state_label;
218 
statusbar_update_state(DebugState state)219 void statusbar_update_state(DebugState state)
220 {
221 	static DebugState last_state = DS_INACTIVE;
222 
223 	if (thread_state == THREAD_AT_ASSEMBLER)
224 		state = DS_EXTRA_1;
225 
226 	if (state != last_state)
227 	{
228 		static const char *const states[] = { N_("Busy"), N_("Ready"), N_("Debug"),
229 			N_("Hang"), N_("Assem"), N_("Load"), NULL };
230 		guint i;
231 
232 		for (i = 0; states[i]; i++)
233 			if (state & (DS_BUSY << i))
234 				break;
235 
236 		gtk_label_set_text(debug_state_label, _(states[i]));
237 
238 		if (state == DS_INACTIVE)
239 		{
240 			gtk_widget_hide(debug_statusbar);
241 		#if GTK_CHECK_VERSION(3, 0, 0)
242 			gtk_window_set_has_resize_grip(GTK_WINDOW(geany->main_widgets->window), TRUE);
243 		#else
244 			gtk_statusbar_set_has_resize_grip(geany_statusbar, TRUE);
245 		#endif
246 		}
247 		else if (last_state == DS_INACTIVE)
248 		{
249 		#if GTK_CHECK_VERSION(3, 0, 0)
250 			gtk_window_set_has_resize_grip(GTK_WINDOW(geany->main_widgets->window), FALSE);
251 		#else
252 			gtk_statusbar_set_has_resize_grip(geany_statusbar, FALSE);
253 		#endif
254 			gtk_widget_show(debug_statusbar);
255 		}
256 
257 		last_state = state;
258 	}
259 }
260 
261 static guint blink_id = 0;
262 
plugin_unblink(G_GNUC_UNUSED gpointer gdata)263 static gboolean plugin_unblink(G_GNUC_UNUSED gpointer gdata)
264 {
265 	gtk_widget_set_state(debug_statusbar, GTK_STATE_NORMAL);
266 	blink_id = 0;
267 	return FALSE;
268 }
269 
plugin_blink(void)270 void plugin_blink(void)
271 {
272 	if (pref_visual_beep_length)
273 	{
274 		if (blink_id)
275 			g_source_remove(blink_id);
276 		else
277 			gtk_widget_set_state(debug_statusbar, GTK_STATE_SELECTED);
278 
279 		blink_id = plugin_timeout_add(geany_plugin, pref_visual_beep_length * 10,
280 			plugin_unblink, NULL);
281 	}
282 }
283 
plugin_beep(void)284 void plugin_beep(void)
285 {
286 	if (geany_data->prefs->beep_on_errors)
287 		gdk_beep();
288 	else
289 		plugin_blink();
290 }
291 
update_state(DebugState state)292 void update_state(DebugState state)
293 {
294 	menu_update_state(state);
295 	program_update_state(state);
296 	toolbar_update_state(state);
297 	statusbar_update_state(state);
298 	views_update_state(state);
299 }
300 
on_document_new(G_GNUC_UNUSED GObject * obj,GeanyDocument * doc,G_GNUC_UNUSED gpointer gdata)301 static void on_document_new(G_GNUC_UNUSED GObject *obj, GeanyDocument *doc,
302 	G_GNUC_UNUSED gpointer gdata)
303 {
304 	prefs_apply(doc);
305 }
306 
on_document_open(G_GNUC_UNUSED GObject * obj,GeanyDocument * doc,G_GNUC_UNUSED gpointer gdata)307 static void on_document_open(G_GNUC_UNUSED GObject *obj, GeanyDocument *doc,
308 	G_GNUC_UNUSED gpointer gdata)
309 {
310 	prefs_apply(doc);
311 	breaks_mark(doc);
312 
313 	if (debug_state() != DS_INACTIVE)
314 		threads_mark(doc);
315 }
316 
settings_saved(gpointer gdata)317 static gboolean settings_saved(gpointer gdata)
318 {
319 	guint i = 0;
320 
321 	foreach_document(i)
322 	{
323 		documents[i]->readonly = scintilla_send_message(documents[i]->editor->sci,
324 			SCI_GETREADONLY, 0, 0);
325 	}
326 
327 	if (gdata)
328 	{
329 		conterm_load_config();
330 		conterm_apply_config();
331 	}
332 
333 	return FALSE;
334 }
335 
schedule_settings_saved(gboolean conterm)336 static void schedule_settings_saved(gboolean conterm)
337 {
338 	guint i = 0;
339 
340 	plugin_idle_add(geany_plugin, settings_saved, GINT_TO_POINTER(conterm));
341 
342 	foreach_document(i)
343 	{
344 		if (utils_attrib(documents[i], SCOPE_LOCK))
345 			documents[i]->readonly = FALSE;
346 	}
347 }
348 
on_settings_save(G_GNUC_UNUSED GObject * obj,G_GNUC_UNUSED GKeyFile * keyfile,G_GNUC_UNUSED gpointer gdata)349 static void on_settings_save(G_GNUC_UNUSED GObject *obj, G_GNUC_UNUSED GKeyFile *keyfile,
350 	G_GNUC_UNUSED gpointer gdata)
351 {
352 	configure_panel();
353 	schedule_settings_saved(TRUE);
354 }
355 
on_editor_notify(G_GNUC_UNUSED GObject * obj,GeanyEditor * editor,SCNotification * nt,G_GNUC_UNUSED gpointer gdata)356 static gboolean on_editor_notify(G_GNUC_UNUSED GObject *obj, GeanyEditor *editor,
357 	SCNotification *nt, G_GNUC_UNUSED gpointer gdata)
358 {
359 	GeanyDocument *doc = editor->document;
360 
361 	if (nt->nmhdr.code == SCN_MODIFIED && nt->linesAdded && utils_source_document(doc))
362 	{
363 		gboolean active = debug_state() != DS_INACTIVE;
364 		ScintillaObject *sci = editor->sci;
365 		gint start = sci_get_line_from_position(sci, nt->position);
366 
367 		if (active)
368 			threads_delta(sci, doc->real_path, start, nt->linesAdded);
369 
370 		breaks_delta(sci, doc->real_path, start, nt->linesAdded, active);
371 	}
372 
373 	return FALSE;
374 }
375 
on_document_filetype_set(G_GNUC_UNUSED GObject * obj,GeanyDocument * doc,G_GNUC_UNUSED GeanyFiletype * filetype_old,G_GNUC_UNUSED gpointer gdata)376 static void on_document_filetype_set(G_GNUC_UNUSED GObject *obj, GeanyDocument *doc,
377 	G_GNUC_UNUSED GeanyFiletype *filetype_old, G_GNUC_UNUSED gpointer gdata)
378 {
379 	DebugState state = debug_state();
380 	utils_lock_unlock(doc, state != DS_INACTIVE && utils_source_document(doc));
381 	toolbar_update_state(state);
382 }
383 
on_document_activate(G_GNUC_UNUSED GObject * obj,G_GNUC_UNUSED GeanyDocument * doc,G_GNUC_UNUSED gpointer user_data)384 static void on_document_activate(G_GNUC_UNUSED GObject *obj, G_GNUC_UNUSED GeanyDocument *doc,
385 	G_GNUC_UNUSED gpointer user_data)
386 {
387 	toolbar_update_state(debug_state());
388 }
389 
on_project_open(G_GNUC_UNUSED GObject * obj,G_GNUC_UNUSED GKeyFile * config)390 static void on_project_open(G_GNUC_UNUSED GObject *obj, G_GNUC_UNUSED GKeyFile *config)
391 {
392 	program_context_changed();
393 }
394 
change_context(G_GNUC_UNUSED gpointer gdata)395 static gboolean change_context(G_GNUC_UNUSED gpointer gdata)
396 {
397 	program_context_changed();
398 	return FALSE;
399 }
400 
on_project_close(G_GNUC_UNUSED GObject * obj,G_GNUC_UNUSED GKeyFile * config)401 static void on_project_close(G_GNUC_UNUSED GObject *obj, G_GNUC_UNUSED GKeyFile *config)
402 {
403 	plugin_idle_add(geany_plugin, change_context, NULL);
404 }
405 
on_geany_startup_complete(G_GNUC_UNUSED GObject * obj,G_GNUC_UNUSED gpointer gdata)406 static void on_geany_startup_complete(G_GNUC_UNUSED GObject *obj, G_GNUC_UNUSED gpointer gdata)
407 {
408 	if (!geany->app->project)
409 		program_context_changed();
410 }
411 
on_build_start(G_GNUC_UNUSED GObject * obj,G_GNUC_UNUSED gpointer gdata)412 static void on_build_start(G_GNUC_UNUSED GObject *obj, G_GNUC_UNUSED gpointer gdata)
413 {
414 	if (debug_state() != DS_INACTIVE && dialogs_show_question(_("Build action activated. "
415 		"Terminate debugging?")))
416 	{
417 		on_debug_terminate(NULL);
418 	}
419 }
420 
421 typedef struct _ScopeCallback  /* we don't want callbacks on builder init failure */
422 {
423 	const char *name;
424 	GCallback callback;
425 } ScopeCallback;
426 
427 static const ScopeCallback scope_callbacks[] =
428 {
429 	{ "document-new",             G_CALLBACK(on_document_new) },
430 	{ "document-open",            G_CALLBACK(on_document_open) },
431 	{ "document-reload",          G_CALLBACK(on_document_open) },
432 	{ "save-settings",            G_CALLBACK(on_settings_save) },
433 	{ "editor-notify",            G_CALLBACK(on_editor_notify) },
434 	{ "document-filetype-set",    G_CALLBACK(on_document_filetype_set) },
435 	{ "document-activate",        G_CALLBACK(on_document_activate) },
436 	{ "document-save",            G_CALLBACK(on_document_activate) },
437 	{ "project-open",             G_CALLBACK(on_project_open) },
438 	{ "project-close",            G_CALLBACK(on_project_close) },
439 	{ "geany-startup-complete",   G_CALLBACK(on_geany_startup_complete) },
440 	{ "build-start",              G_CALLBACK(on_build_start) },
441 	{ NULL, NULL }
442 };
443 
444 static GtkBuilder *builder;
445 
get_object(const char * name)446 GObject *get_object(const char *name)
447 {
448 #ifdef G_DISABLE_ASSERT
449 	return gtk_builder_get_object(builder, name);
450 #else  /* G_DISABLE_ASSERT */
451 	GObject *object = gtk_builder_get_object(builder, name);
452 
453 	if (!object)
454 	{
455 		fprintf(stderr, "Scope: object %s is missing\n", name);
456 		abort();
457 	}
458 
459 	return object;
460 #endif  /* G_DISABLE_ASSERT */
461 }
462 
get_widget(const char * name)463 GtkWidget *get_widget(const char *name)
464 {
465 #ifdef G_DISABLE_ASSERT
466 	return GTK_WIDGET(get_object(name));
467 #else  /* !SC_DISABLE_ASSERT */
468 	GObject *object = get_object(name);
469 
470 	if (!GTK_IS_WIDGET(object))
471 	{
472 		fprintf(stderr, "Scope: object %s is not a widget\n", name);
473 		abort();
474 	}
475 
476 	return GTK_WIDGET(object);
477 #endif  /* G_DISABLE_ASSERT */
478 }
479 
configure_toolbar(void)480 void configure_toolbar(void)
481 {
482 	guint item;
483 	ToolItem *tool_item = toolbar_items;
484 
485 	for (item = 0; tool_item->index != -1; item++, tool_item++)
486 		gtk_widget_set_visible(tool_item->widget, pref_show_toolbar_items & (1 << item));
487 }
488 
plugin_help()489 void plugin_help()
490 {
491 	char *helpfile = g_build_filename(PLUGINHTMLDOCDIR, "scope.html", NULL);
492 	utils_open_browser(helpfile);
493 	g_free(helpfile);
494 }
495 
496 #define DEBUG_MENU_ITEM_POS 7
497 static GtkWidget *debug_item;
498 static GtkWidget *debug_panel;
499 
open_debug_panel(void)500 void open_debug_panel(void)
501 {
502 	GtkNotebook *notebook = GTK_NOTEBOOK(geany->main_widgets->message_window_notebook);
503 	msgwin_switch_tab(gtk_notebook_page_num(notebook, debug_panel), TRUE);
504 	gtk_widget_grab_focus(debug_panel);
505 }
506 
configure_panel(void)507 void configure_panel(void)
508 {
509 	gboolean short_tab_names = pref_panel_tab_pos == GTK_POS_LEFT ||
510 		pref_panel_tab_pos == GTK_POS_RIGHT ||
511 		geany_data->interface_prefs->msgwin_orientation == GTK_ORIENTATION_HORIZONTAL;
512 
513 	gtk_label_set_label(GTK_LABEL(get_widget("program_terminal_label")),
514 		short_tab_names ? _("Program") : _("Program Terminal"));
515 	gtk_label_set_label(GTK_LABEL(get_widget("break_view_label")),
516 		short_tab_names ? _("Breaks") : _("Breakpoints"));
517 	gtk_label_set_label(GTK_LABEL(get_widget("debug_console_label")),
518 		short_tab_names ? _("Console") : _("Debug Console"));
519 
520 	gtk_notebook_set_tab_pos(GTK_NOTEBOOK(debug_panel), pref_panel_tab_pos);
521 }
522 
get_data_dir_path(const gchar * filename)523 static gchar *get_data_dir_path(const gchar *filename)
524 {
525 	gchar *prefix = NULL;
526 	gchar *path;
527 
528 #ifdef G_OS_WIN32
529 	prefix = g_win32_get_package_installation_directory_of_module(NULL);
530 #elif defined(__APPLE__)
531 	if (g_getenv("GEANY_PLUGINS_SHARE_PATH"))
532 		return g_build_filename(g_getenv("GEANY_PLUGINS_SHARE_PATH"),
533 								PLUGIN, filename, NULL);
534 #endif
535 	path = g_build_filename(prefix ? prefix : "", PLUGINDATADIR, filename, NULL);
536 	g_free(prefix);
537 	return path;
538 }
539 
plugin_init(G_GNUC_UNUSED GeanyData * gdata)540 void plugin_init(G_GNUC_UNUSED GeanyData *gdata)
541 {
542 	GeanyKeyGroup *scope_key_group;
543 #if GTK_CHECK_VERSION(3, 0, 0)
544 	char *gladefile = get_data_dir_path("scope_gtk3.glade");
545 #else
546 	char *gladefile = get_data_dir_path("scope.glade");
547 #endif
548 	GError *gerror = NULL;
549 	GtkWidget *menubar1 = ui_lookup_widget(geany->main_widgets->window, "menubar1");
550 	guint item;
551 	const MenuKey *menu_key = debug_menu_keys;
552 	ToolItem *tool_item = toolbar_items;
553 	const ScopeCallback *scb;
554 
555 	scope_key_group = plugin_set_key_group(geany_plugin, "scope", COUNT_KB, NULL);
556 	builder = gtk_builder_new();
557 	gtk_builder_set_translation_domain(builder, GETTEXT_PACKAGE);
558 	scp_tree_store_register_dynamic();
559 
560 	if (!gtk_builder_add_from_file(builder, gladefile, &gerror))
561 	{
562 		msgwin_status_add(_("Scope: %s."), gerror->message);
563 		g_warning(_("Scope: %s."), gerror->message);
564 		g_error_free(gerror);
565 		g_object_unref(builder);
566 		builder = NULL;
567 	}
568 
569 	g_free(gladefile);
570 	if (!builder)
571 		return;
572 
573 	/* interface */
574 #ifndef G_OS_UNIX
575 	gtk_widget_hide(get_widget("terminal_show"));
576 #endif
577 	debug_item = get_widget("debug_item");
578 	if (menubar1)
579 	{
580 		GList *children = gtk_container_get_children(GTK_CONTAINER(menubar1));
581 		GtkWidget *menu_build1 = ui_lookup_widget(menubar1, "menu_build1");
582 
583 		gtk_menu_shell_insert(GTK_MENU_SHELL(menubar1), debug_item,
584 			menu_build1 ? g_list_index(children, menu_build1) + 1 : DEBUG_MENU_ITEM_POS);
585 	}
586 	else
587 		gtk_container_add(GTK_CONTAINER(geany->main_widgets->tools_menu), debug_item);
588 
589 	menu_connect("debug_menu", &debug_menu_info, NULL);
590 	ui_add_document_sensitive(get_widget("scope_reset_markers"));
591 	ui_add_document_sensitive(get_widget("scope_cleanup_files"));
592 
593 	for (item = 0; item < EVALUATE_KB; item++, menu_key++)
594 	{
595 		keybindings_set_item(scope_key_group, item, on_scope_key, 0, 0, menu_key->name,
596 			_(menu_key->label), debug_menu_items[item].widget);
597 	}
598 
599 	geany_statusbar = GTK_STATUSBAR(gtk_widget_get_parent(geany->main_widgets->progressbar));
600 	debug_statusbar = get_widget("debug_statusbar");
601 	debug_state_label = GTK_LABEL(get_widget("debug_state_label"));
602 	gtk_box_pack_end(GTK_BOX(geany_statusbar), debug_statusbar, FALSE, FALSE, 0);
603 
604 	debug_panel = get_widget("debug_panel");
605 	gtk_notebook_append_page(GTK_NOTEBOOK(geany->main_widgets->message_window_notebook),
606 		debug_panel, get_widget("debug_label"));
607 
608 	/* startup */
609 	program_init();
610 	prefs_init();
611 	conterm_init();
612 	inspect_init();
613 	register_init();
614 	parse_init();
615 	utils_init();
616 	debug_init();
617 	views_init();
618 	thread_init();
619 	break_init();
620 	watch_init();
621 	stack_init();
622 	local_init();
623 	memory_init();
624 	menu_init();
625 	menu_set_popup_keybindings(scope_key_group, item);
626 
627 	for (item = 0; tool_item->index != -1; item++, tool_item++)
628 	{
629 		GtkMenuItem *menu_item = GTK_MENU_ITEM(debug_menu_items[tool_item->index].widget);
630 		GtkToolItem *button = gtk_tool_button_new(NULL, gtk_menu_item_get_label(menu_item));
631 
632 		gtk_widget_set_tooltip_text (GTK_WIDGET (button), _(tool_item->tooltip_text));
633 		gtk_tool_button_set_use_underline(GTK_TOOL_BUTTON(button),
634 			gtk_menu_item_get_use_underline(menu_item));
635 		g_signal_connect(button, "clicked", G_CALLBACK(on_toolbar_button_clicked),
636 			GINT_TO_POINTER(tool_item->index));
637 		g_signal_connect(button, "toolbar-reconfigured",
638 			G_CALLBACK(on_toolbar_reconfigured), tool_item);
639 		tool_item->widget = GTK_WIDGET(button);
640 		plugin_add_toolbar_item(geany_plugin, button);
641 	}
642 
643 	toolbar_update_state(DS_INACTIVE);
644 	views_update_state(DS_INACTIVE);
645 	configure_toolbar();
646 
647 	g_signal_connect(debug_panel, "switch-page", G_CALLBACK(on_view_changed), NULL);
648 	for (scb = scope_callbacks; scb->name; scb++)
649 		plugin_signal_connect(geany_plugin, NULL, scb->name, FALSE, scb->callback, NULL);
650 }
651 
plugin_cleanup(void)652 void plugin_cleanup(void)
653 {
654 	ToolItem *item;
655 
656 	if (!builder)
657 		return;
658 
659 	gtk_widget_destroy(debug_item);
660 	gtk_widget_destroy(debug_panel);
661 
662 	for (item = toolbar_items; item->index != -1; item++)
663 		gtk_widget_destroy(item->widget);
664 
665 	/* shutdown */
666 	tooltip_finalize();
667 	program_finalize();
668 	conterm_finalize();
669 	registers_finalize();
670 	inspect_finalize();
671 	thread_finalize();
672 	break_finalize();
673 	memory_finalize();
674 	menu_finalize();
675 	views_finalize();
676 	utils_finalize();
677 	parse_finalize();
678 	prefs_finalize();
679 	debug_finalize();
680 
681 	gtk_widget_destroy(debug_statusbar);
682 	g_object_unref(builder);
683 }
684