1 /*
2  *
3  *		dconfig.c
4  *
5  *      Copyright 2011 Alexander Petukhov <devel(at)apetukhov.ru>
6  *
7  *      This program is free software; you can redistribute it and/or modify
8  *      it under the terms of the GNU General Public License as published by
9  *      the Free Software Foundation; either version 2 of the License, or
10  *      (at your option) any later version.
11  *
12  *      This program is distributed in the hope that it will be useful,
13  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *      GNU General Public License for more details.
16  *
17  *      You should have received a copy of the GNU General Public License
18  *      along with this program; if not, write to the Free Software
19  *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20  *      MA 02110-1301, USA.
21  */
22 
23 /*
24  *		Plugin panel and debug session configs
25  */
26 
27 #include <unistd.h>
28 #include <sys/stat.h>
29 
30 #include <glib/gstdio.h>
31 
32 #ifdef HAVE_CONFIG_H
33 	#include "config.h"
34 #endif
35 #include <geanyplugin.h>
36 extern GeanyData *geany_data;
37 
38 #include "dconfig.h"
39 #include "tabs.h"
40 #include "breakpoints.h"
41 #include "debug.h"
42 #include "watch_model.h"
43 #include "wtree.h"
44 #include "tpage.h"
45 #include "bptree.h"
46 
47 /* keyfile debug group name */
48 #define DEBUGGER_GROUP "debugger"
49 /* saving interval */
50 #define SAVING_INTERVAL 2000000
51 
52 /* check button for a configure dialog */
53 static GtkWidget *save_to_project_btn = NULL;
54 
55 /* plugin config file path */
56 static gchar *plugin_config_path = NULL;
57 
58 /* current debug session store */
59 static debug_store dstore = DEBUG_STORE_PLUGIN;
60 
61 /* GKeyFile's for a project and plugin config */
62 static GKeyFile *keyfile_plugin = NULL;
63 static GKeyFile *keyfile_project = NULL;
64 
65 /* flag that indicates that debug session is being loaded to controls
66  * to prevent change state to modified from GUI callbacks */
67 static gboolean debug_config_loading = FALSE;
68 
69 /* saving thread staff */
70 static GMutex change_config_mutex;
71 static GCond cond;
72 static GThread *saving_thread;
73 
74 /* flags that indicate that part of a config has been changed and
75  * is going to be saved on the savng thread */
76 static gboolean debug_config_changed = FALSE;
77 static gboolean panel_config_changed = FALSE;
78 
79 /*
80  *	creates a copy of a specified keyfile
81  */
create_copy_keyfile(GKeyFile * keyfile)82 static GKeyFile *create_copy_keyfile(GKeyFile *keyfile)
83 {
84 	GKeyFile *copy;
85 	gchar *data;
86 	gsize length;
87 
88 	data = g_key_file_to_data(keyfile, &length, NULL);
89 	copy = g_key_file_new();
90 	g_key_file_load_from_data(copy, data, length, G_KEY_FILE_NONE, NULL);
91 	g_free(data);
92 
93 	return copy;
94 }
95 
96 /*
97  * loads debug session from a keyfile and updates GUI
98  */
debug_load_from_keyfile(GKeyFile * keyfile)99 static void debug_load_from_keyfile(GKeyFile *keyfile)
100 {
101 	gchar *value;
102 	int i, count;
103 
104 	debug_config_loading = TRUE;
105 
106 	/* target */
107 	tpage_set_target(value = g_key_file_get_string(keyfile, DEBUGGER_GROUP, "target", NULL));
108 	g_free(value);
109 	/* debugger */
110 	tpage_set_debugger(value = g_key_file_get_string(keyfile, DEBUGGER_GROUP, "debugger", NULL));
111 	g_free(value);
112 	/* arguments */
113 	tpage_set_commandline(value = g_key_file_get_string(keyfile, DEBUGGER_GROUP, "arguments", NULL));
114 	g_free(value);
115 
116 	/* environment */
117 	count = g_key_file_get_integer(keyfile, DEBUGGER_GROUP, "envvar_count", NULL);
118 	for (i = 0; i < count; i++)
119 	{
120 		gchar *env_name_id = g_strdup_printf("envvar_%i_name", i);
121 		gchar *env_value_id = g_strdup_printf("envvar_%i_value", i);
122 		gchar *name = g_key_file_get_string(keyfile, DEBUGGER_GROUP, env_name_id, NULL);
123 
124 		value = g_key_file_get_string(keyfile, DEBUGGER_GROUP, env_value_id, NULL);
125 
126 		tpage_add_environment(name, value);
127 
128 		g_free(name);
129 		g_free(value);
130 
131 		g_free(env_name_id);
132 		g_free(env_value_id);
133 	}
134 
135 	/* watches */
136 	count = g_key_file_get_integer(keyfile, DEBUGGER_GROUP, "watches_count", NULL);
137 	for (i = 0; i < count; i++)
138 	{
139 		gchar *watch_id = g_strdup_printf("watch_%i", i);
140 		wtree_add_watch(value = g_key_file_get_string(keyfile, DEBUGGER_GROUP, watch_id, NULL));
141 		g_free(value);
142 		g_free(watch_id);
143 	}
144 
145 	/* breakpoints */
146 	count = g_key_file_get_integer(keyfile, DEBUGGER_GROUP, "breaks_count", NULL);
147 	for (i = 0; i < count; i++)
148 	{
149 		gchar *break_file_id = g_strdup_printf("break_%i_file", i);
150 		gchar *break_line_id = g_strdup_printf("break_%i_line", i);
151 		gchar *break_condition_id = g_strdup_printf("break_%i_condition", i);
152 		gchar *break_hits_id = g_strdup_printf("break_%i_hits_count", i);
153 		gchar *break_enabled_id = g_strdup_printf("break_%i_enabled", i);
154 
155 		gchar *file = g_key_file_get_string(keyfile, DEBUGGER_GROUP, break_file_id, NULL);
156 		int line = g_key_file_get_integer(keyfile, DEBUGGER_GROUP, break_line_id, NULL);
157 		gchar *condition = g_key_file_get_string(keyfile, DEBUGGER_GROUP, break_condition_id, NULL);
158 		int hits_count = g_key_file_get_integer(keyfile, DEBUGGER_GROUP, break_hits_id, NULL);
159 		gboolean enabled = g_key_file_get_boolean(keyfile, DEBUGGER_GROUP, break_enabled_id, NULL);
160 
161 		breaks_add(file, line, condition, enabled, hits_count);
162 
163 		g_free(break_file_id);
164 		g_free(break_line_id);
165 		g_free(break_condition_id);
166 		g_free(break_hits_id);
167 		g_free(break_enabled_id);
168 
169 		g_free(file);
170 		g_free(condition);
171 	}
172 	bptree_update_file_nodes();
173 
174 	debug_config_loading = FALSE;
175 }
176 
177 /*
178  * saves debug session to a keyfile using values from GUI
179  */
save_to_keyfile(GKeyFile * keyfile)180 static void save_to_keyfile(GKeyFile *keyfile)
181 {
182 	GList *_env, *watches, *_breaks, *iter;
183 	int env_index, watch_index, bp_index;
184 
185 	g_key_file_remove_group(keyfile, DEBUGGER_GROUP, NULL);
186 
187 	g_key_file_set_string(keyfile, DEBUGGER_GROUP, "target", tpage_get_target());
188 	g_key_file_set_string(keyfile, DEBUGGER_GROUP, "debugger", tpage_get_debugger());
189 	g_key_file_set_string(keyfile, DEBUGGER_GROUP, "arguments", tpage_get_commandline());
190 
191 	/* environment */
192 	_env = tpage_get_environment();
193 	g_key_file_set_integer(keyfile, DEBUGGER_GROUP, "envvar_count", g_list_length(_env) / 2);
194 	iter = _env;
195 	env_index = 0;
196 	while(iter)
197 	{
198 		gchar *name, *value;
199 		gchar *env_name_id = g_strdup_printf("envvar_%i_name", env_index);
200 		gchar *env_value_id = g_strdup_printf("envvar_%i_value", env_index);
201 
202 		name = (gchar*)iter->data;
203 		iter = iter->next;
204 		value = (gchar*)iter->data;
205 
206 		g_key_file_set_string(keyfile, DEBUGGER_GROUP, env_name_id, name);
207 		g_key_file_set_string(keyfile, DEBUGGER_GROUP, env_value_id, value);
208 
209 		g_free(env_name_id);
210 		g_free(env_value_id);
211 
212 		env_index++;
213 		iter = iter->next;
214 	}
215 	g_list_foreach(_env, (GFunc)g_free, NULL);
216 	g_list_free(_env);
217 
218 	/* watches */
219 	watches = wtree_get_watches();
220 	g_key_file_set_integer(keyfile, DEBUGGER_GROUP, "watches_count", g_list_length(watches));
221 	watch_index = 0;
222 	for (iter = watches; iter; iter = iter->next)
223 	{
224 		gchar *watch = (gchar*)iter->data;
225 		gchar *watch_id = g_strdup_printf("watch_%i", watch_index);
226 
227 		g_key_file_set_string(keyfile, DEBUGGER_GROUP, watch_id, watch);
228 
229 		g_free(watch_id);
230 
231 		watch_index++;
232 	}
233 	g_list_foreach(watches, (GFunc)g_free, NULL);
234 	g_list_free(watches);
235 
236 	/* breakpoints */
237 	_breaks = breaks_get_all();
238 	g_key_file_set_integer(keyfile, DEBUGGER_GROUP, "breaks_count", g_list_length(_breaks));
239 	bp_index = 0;
240 	for (iter = _breaks; iter; iter = iter->next)
241 	{
242 		breakpoint *bp = (breakpoint*)iter->data;
243 
244 		gchar *break_file_id = g_strdup_printf("break_%i_file", bp_index);
245 		gchar *break_line_id = g_strdup_printf("break_%i_line", bp_index);
246 		gchar *break_condition_id = g_strdup_printf("break_%i_condition", bp_index);
247 		gchar *break_hits_id = g_strdup_printf("break_%i_hits_count", bp_index);
248 		gchar *break_enabled_id = g_strdup_printf("break_%i_enabled", bp_index);
249 
250 		g_key_file_set_string(keyfile, DEBUGGER_GROUP, break_file_id, bp->file);
251 		g_key_file_set_integer(keyfile, DEBUGGER_GROUP, break_line_id, bp->line);
252 		g_key_file_set_string(keyfile, DEBUGGER_GROUP, break_condition_id, bp->condition);
253 		g_key_file_set_integer(keyfile, DEBUGGER_GROUP, break_hits_id, bp->hitscount);
254 		g_key_file_set_boolean(keyfile, DEBUGGER_GROUP, break_enabled_id, bp->enabled);
255 
256 		g_free(break_file_id);
257 		g_free(break_line_id);
258 		g_free(break_condition_id);
259 		g_free(break_hits_id);
260 		g_free(break_enabled_id);
261 
262 		bp_index++;
263 	}
264 	g_list_free(_breaks);
265 }
266 
267 /*
268  * function for config files background saving
269  */
saving_thread_func(gpointer data)270 static gpointer saving_thread_func(gpointer data)
271 {
272 	gint64 interval;
273 	g_mutex_lock(&change_config_mutex);
274 	do
275 	{
276 		if (
277 			panel_config_changed ||
278 			(debug_config_changed && DEBUG_STORE_PLUGIN == dstore)
279 		)
280 		{
281 			gchar *config_data;
282 
283 			/* if all saving is going to be done to a plugin keyfile */
284 			if (debug_config_changed)
285 			{
286 				save_to_keyfile(keyfile_plugin);
287 				debug_config_changed = FALSE;
288 			}
289 
290 			config_data = g_key_file_to_data(keyfile_plugin, NULL, NULL);
291 			g_file_set_contents(plugin_config_path, config_data, -1, NULL);
292 			g_free(config_data);
293 
294 			panel_config_changed = FALSE;
295 		}
296 
297 		if (debug_config_changed && DEBUG_STORE_PROJECT == dstore)
298 		{
299 			gchar *config_data;
300 
301 			/* if debug is saved into a project and has been changed */
302 			save_to_keyfile(keyfile_project);
303 
304 			config_data = g_key_file_to_data(keyfile_project, NULL, NULL);
305 			g_file_set_contents(geany_data->app->project->file_name, config_data, -1, NULL);
306 			g_free(config_data);
307 
308 			debug_config_changed = FALSE;
309 		}
310 
311 		interval = g_get_monotonic_time() + SAVING_INTERVAL;
312 	}
313 	while (!g_cond_wait_until(&cond, &change_config_mutex, interval));
314 	g_mutex_unlock(&change_config_mutex);
315 
316 	return NULL;
317 }
318 
319 /*
320  * set "debug changed" flag to save it on "saving_thread" thread
321  */
config_set_debug_changed(void)322 void config_set_debug_changed(void)
323 {
324 	if (!debug_config_loading)
325 	{
326 		g_mutex_lock(&change_config_mutex);
327 		debug_config_changed = TRUE;
328 		g_mutex_unlock(&change_config_mutex);
329 	}
330 }
331 
332 /*
333  *	set one or several panel config values
334  */
config_set_panel(int config_part,gpointer config_value,...)335 void config_set_panel(int config_part, gpointer config_value, ...)
336 {
337 	va_list ap;
338 
339 	g_mutex_lock(&change_config_mutex);
340 
341 	va_start(ap, config_value);
342 
343 	while(config_part)
344 	{
345 		switch (config_part)
346 		{
347 			case CP_TABBED_MODE:
348 			{
349 				g_key_file_set_boolean(keyfile_plugin, "tabbed_mode", "enabled", *((gboolean*)config_value));
350 				break;
351 			}
352 			case CP_OT_TABS:
353 			{
354 				int *array = (int*)config_value;
355 				g_key_file_set_integer_list(keyfile_plugin, "one_panel_mode", "tabs", array + 1, array[0]);
356 				break;
357 			}
358 			case CP_OT_SELECTED:
359 			{
360 				g_key_file_set_integer(keyfile_plugin, "one_panel_mode", "selected_tab_index", *((int*)config_value));
361 				break;
362 			}
363 			case CP_TT_LTABS:
364 			{
365 				int *array = (int*)config_value;
366 				g_key_file_set_integer_list(keyfile_plugin, "two_panels_mode", "left_tabs", array + 1, array[0]);
367 				break;
368 			}
369 			case CP_TT_LSELECTED:
370 			{
371 				g_key_file_set_integer(keyfile_plugin, "two_panels_mode", "left_selected_tab_index", *((int*)config_value));
372 				break;
373 			}
374 			case CP_TT_RTABS:
375 			{
376 				int *array = (int*)config_value;
377 				g_key_file_set_integer_list(keyfile_plugin, "two_panels_mode", "right_tabs", array + 1, array[0]);
378 				break;
379 			}
380 			case CP_TT_RSELECTED:
381 			{
382 				g_key_file_set_integer(keyfile_plugin, "two_panels_mode", "right_selected_tab_index", *((int*)config_value));
383 				break;
384 			}
385 		}
386 
387 		config_part = va_arg(ap, int);
388 		if (config_part)
389 		{
390 			config_value = va_arg(ap, gpointer);
391 		}
392 	}
393 
394 	va_end(ap);
395 
396 	panel_config_changed = TRUE;
397 	g_mutex_unlock(&change_config_mutex);
398 }
399 
400 /*
401  *	set default debug session values to a keyfile
402  */
config_set_debug_defaults(GKeyFile * keyfile)403 static void config_set_debug_defaults(GKeyFile *keyfile)
404 {
405 	g_key_file_set_string(keyfile, DEBUGGER_GROUP, "target", "");
406 	g_key_file_set_string(keyfile, DEBUGGER_GROUP, "debugger", "");
407 	g_key_file_set_string(keyfile, DEBUGGER_GROUP, "arguments", "");
408 
409 	g_key_file_set_integer(keyfile, DEBUGGER_GROUP, "envvar_count", 0);
410 	g_key_file_set_integer(keyfile, DEBUGGER_GROUP, "watches_count", 0);
411 	g_key_file_set_integer(keyfile, DEBUGGER_GROUP, "breaks_count", 0);
412 }
413 
414 /*
415  *	set default panel config values in a GKeyFile
416  */
config_set_panel_defaults(GKeyFile * keyfile)417 static void config_set_panel_defaults(GKeyFile *keyfile)
418 {
419 	int all_tabs[] = { TID_TARGET, TID_BREAKS, TID_AUTOS, TID_WATCH, TID_STACK, TID_TERMINAL, TID_MESSAGES };
420 	int left_tabs[] = { TID_TARGET, TID_BREAKS, TID_AUTOS, TID_WATCH };
421 	int right_tabs[] = { TID_STACK, TID_TERMINAL, TID_MESSAGES };
422 
423 	g_key_file_set_boolean(keyfile_plugin, "tabbed_mode", "enabled", FALSE);
424 	/* all tabs */
425 	g_key_file_set_integer_list(keyfile, "one_panel_mode", "tabs", all_tabs, sizeof(all_tabs) / sizeof(int));
426 	g_key_file_set_integer(keyfile, "one_panel_mode", "selected_tab_index", 0);
427 	/* left tabs */
428 	g_key_file_set_integer_list(keyfile, "two_panels_mode", "left_tabs", left_tabs, sizeof(left_tabs) / sizeof(int));
429 	g_key_file_set_integer(keyfile, "two_panels_mode", "left_selected_tab_index", 0);
430 	/* right tabs */
431 	g_key_file_set_integer_list(keyfile, "two_panels_mode", "right_tabs", right_tabs, sizeof(right_tabs) / sizeof(int));
432 	g_key_file_set_integer(keyfile, "two_panels_mode", "right_selected_tab_index", 0);
433 
434 	g_key_file_set_boolean(keyfile, "saving_settings", "save_to_project", FALSE);
435 }
436 
437 /*
438  *	initialize
439  */
config_init(void)440 void config_init(void)
441 {
442 	/* read config */
443 	gchar *config_dir = g_build_path(G_DIR_SEPARATOR_S, geany_data->app->configdir, "plugins", "debugger", NULL);
444 	plugin_config_path = g_build_path(G_DIR_SEPARATOR_S, config_dir, "debugger.conf", NULL);
445 
446 	g_mkdir_with_parents(config_dir, S_IRUSR | S_IWUSR | S_IXUSR);
447 	g_free(config_dir);
448 
449 	keyfile_plugin = g_key_file_new();
450 	if (!g_key_file_load_from_file(keyfile_plugin, plugin_config_path, G_KEY_FILE_NONE, NULL))
451 	{
452 		gchar *data;
453 
454 		config_set_panel_defaults(keyfile_plugin);
455 		data = g_key_file_to_data(keyfile_plugin, NULL, NULL);
456 		g_file_set_contents(plugin_config_path, data, -1, NULL);
457 		g_free(data);
458 	}
459 
460 	g_mutex_init(&change_config_mutex);
461 	g_cond_init(&cond);
462 	saving_thread = g_thread_new(NULL, saving_thread_func, NULL);
463 }
464 
465 /*
466  *	destroy
467  */
config_destroy(void)468 void config_destroy(void)
469 {
470 	g_cond_signal(&cond);
471 	g_thread_join(saving_thread);
472 
473 	g_mutex_clear(&change_config_mutex);
474 	g_cond_clear(&cond);
475 
476 	g_free(plugin_config_path);
477 
478 	g_key_file_free(keyfile_plugin);
479 	if(keyfile_project)
480 	{
481 		g_key_file_free(keyfile_project);
482 		keyfile_project = NULL;
483 	}
484 }
485 
486 /*
487  *	config parts getters
488  */
489 /* saving option */
config_get_save_to_project(void)490 gboolean config_get_save_to_project(void)
491 {
492 	return g_key_file_get_boolean(keyfile_plugin, "saving_settings", "save_to_project", NULL);
493 }
494 /* panel config */
config_get_tabbed(void)495 gboolean config_get_tabbed(void)
496 {
497 	return g_key_file_get_boolean(keyfile_plugin, "tabbed_mode", "enabled", NULL);
498 }
config_get_tabs(gsize * length)499 int* config_get_tabs(gsize *length)
500 {
501 	return g_key_file_get_integer_list(keyfile_plugin, "one_panel_mode", "tabs", length, NULL);
502 }
config_get_selected_tab_index(void)503 int config_get_selected_tab_index(void)
504 {
505 	return g_key_file_get_integer(keyfile_plugin, "one_panel_mode", "selected_tab_index", NULL);
506 }
config_get_left_tabs(gsize * length)507 int* config_get_left_tabs(gsize *length)
508 {
509 	return g_key_file_get_integer_list(keyfile_plugin, "two_panels_mode", "left_tabs", length, NULL);
510 }
config_get_left_selected_tab_index(void)511 int config_get_left_selected_tab_index(void)
512 {
513 	return g_key_file_get_integer(keyfile_plugin, "two_panels_mode", "left_selected_tab_index", NULL);
514 }
config_get_right_tabs(gsize * length)515 int* config_get_right_tabs(gsize *length)
516 {
517 	return g_key_file_get_integer_list(keyfile_plugin, "two_panels_mode", "right_tabs", length, NULL);
518 }
config_get_right_selected_tab_index(void)519 int	config_get_right_selected_tab_index(void)
520 {
521 	return g_key_file_get_integer(keyfile_plugin, "two_panels_mode", "right_selected_tab_index", NULL);
522 }
523 
524 /*
525  *	update GUI fron the store specified
526  *  also handles default values insertion in a keyfile if debug section doesn't exist
527  */
config_set_debug_store(debug_store store)528 void config_set_debug_store(debug_store store)
529 {
530 	GKeyFile *keyfile;
531 
532 	dstore = store;
533 
534 	tpage_clear();
535 	wtree_remove_all();
536 	breaks_remove_all();
537 
538 	keyfile = DEBUG_STORE_PROJECT == dstore ? keyfile_project : keyfile_plugin;
539 	if (!g_key_file_has_group(keyfile, DEBUGGER_GROUP))
540 	{
541 		gchar *data, *file;
542 
543 		config_set_debug_defaults(keyfile);
544 		data = g_key_file_to_data(keyfile, NULL, NULL);
545 
546 		file = DEBUG_STORE_PROJECT == dstore ? geany_data->app->project->file_name : plugin_config_path;
547 		g_file_set_contents(file, data, -1, NULL);
548 
549 		g_free(data);
550 	}
551 
552 	debug_load_from_keyfile(keyfile);
553 }
554 
555 /*
556  *	updates keyfile_project from a current geany project path
557  */
config_update_project_keyfile(void)558 void config_update_project_keyfile(void)
559 {
560 	if (keyfile_project)
561 	{
562 		g_key_file_free(keyfile_project);
563 	}
564 	keyfile_project = g_key_file_new();
565 	g_key_file_load_from_file(keyfile_project, geany_data->app->project->file_name, G_KEY_FILE_NONE, NULL);
566 }
567 
568 /*
569  *	project open handler
570  */
config_on_project_open(GObject * obj,GKeyFile * config,gpointer user_data)571 void config_on_project_open(GObject *obj, GKeyFile *config, gpointer user_data)
572 {
573 	config_update_project_keyfile();
574 
575 	if (config_get_save_to_project())
576 	{
577 		config_set_debug_store(DEBUG_STORE_PROJECT);
578 	}
579 }
580 
581 /*
582  *	project close handler
583  */
config_on_project_close(GObject * obj,gpointer user_data)584 void config_on_project_close(GObject *obj, gpointer user_data)
585 {
586 	if (config_get_save_to_project())
587 	{
588 		if (DBS_IDLE != debug_get_state())
589 		{
590 			/* stop a debugger and ait for it to be stopped */
591 			debug_stop();
592 
593 			while (DBS_IDLE != debug_get_state())
594 			{
595 				g_main_context_iteration(NULL, FALSE);
596 			}
597 		}
598 
599 		config_set_debug_store(DEBUG_STORE_PLUGIN);
600 	}
601 }
602 
603 /*
604  *	project save handler
605  * 	handles ne project creation and updatng a project using project properties dialog
606  */
config_on_project_save(GObject * obj,GKeyFile * config,gpointer user_data)607 void config_on_project_save(GObject *obj, GKeyFile *config, gpointer user_data)
608 {
609 	if (config_get_save_to_project())
610 	{
611 		if (!g_key_file_has_group(config, DEBUGGER_GROUP))
612 		{
613 			/* no debug group, creating a new project */
614 			dstore = DEBUG_STORE_PROJECT;
615 
616 			/* clear values taken from a plugin */
617 			tpage_clear();
618 			wtree_remove_all();
619 			breaks_remove_all();
620 
621 			/* set default debug values */
622 			config_set_debug_defaults(config);
623 		}
624 
625 		/* update local keyfile */
626 		if (keyfile_project)
627 		{
628 			g_key_file_free(keyfile_project);
629 		}
630 		keyfile_project = create_copy_keyfile(config);
631 	}
632 }
633 
634 /*
635  *	a configure dialog has been closed
636  */
on_configure_response(GtkDialog * dialog,gint response,gpointer user_data)637 static void on_configure_response(GtkDialog* dialog, gint response, gpointer user_data)
638 {
639 	gboolean newvalue = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(save_to_project_btn));
640 	if (newvalue ^ config_get_save_to_project())
641 	{
642 		g_key_file_set_boolean(keyfile_plugin, "saving_settings", "save_to_project", newvalue);
643 
644 		g_mutex_lock(&change_config_mutex);
645 		panel_config_changed = TRUE;
646 		g_mutex_unlock(&change_config_mutex);
647 
648 		if (geany_data->app->project)
649 		{
650 			if (DBS_IDLE != debug_get_state())
651 			{
652 				debug_stop();
653 
654 				while (DBS_IDLE != debug_get_state())
655 				{
656 					g_main_context_iteration(NULL, FALSE);
657 				}
658 			}
659 
660 			config_set_debug_store(newvalue ? DEBUG_STORE_PROJECT : DEBUG_STORE_PLUGIN);
661 		}
662 	}
663 }
664 
665 /*
666  *	"plugin_configure" signal handler
667  */
config_plugin_configure(GtkDialog * dialog)668 GtkWidget *config_plugin_configure(GtkDialog *dialog)
669 {
670 #if GTK_CHECK_VERSION(3, 0, 0)
671 	GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
672 	GtkWidget *_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
673 #else
674 	GtkWidget *vbox = gtk_vbox_new(FALSE, 6);
675 	GtkWidget *_hbox = gtk_hbox_new(FALSE, 6);
676 #endif
677 
678 	save_to_project_btn = gtk_check_button_new_with_label(_("Save debug session data to a project"));
679 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(save_to_project_btn), config_get_save_to_project());
680 
681 	gtk_box_pack_start(GTK_BOX(_hbox), save_to_project_btn, TRUE, TRUE, 0);
682 	gtk_box_pack_start(GTK_BOX(vbox), _hbox, FALSE, FALSE, 0);
683 
684 	gtk_widget_show_all(vbox);
685 
686 	g_signal_connect(dialog, "response", G_CALLBACK(on_configure_response), NULL);
687 
688 	return vbox;
689 }
690