1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3     plugin.c
4     Copyright (C) 2008 Sébastien Granjoux
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, write to the Free Software
18     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19 */
20 
21 /*
22  * Plugins functions
23  *
24  *---------------------------------------------------------------------------*/
25 
26 #include <config.h>
27 
28 #include "plugin.h"
29 
30 #include "execute.h"
31 #include "parameters.h"
32 
33 #include <libanjuta/anjuta-debug.h>
34 #include <libanjuta/interfaces/ianjuta-project-manager.h>
35 
36 #include <signal.h>
37 
38 /*---------------------------------------------------------------------------*/
39 
40 #define UI_FILE PACKAGE_DATA_DIR"/ui/anjuta-run-program.xml"
41 
42 #define MAX_RECENT_ITEM	10
43 
44 /* Type defintions
45  *---------------------------------------------------------------------------*/
46 
47 struct _RunProgramPluginClass
48 {
49 	AnjutaPluginClass parent_class;
50 };
51 
52 /* Helper functions
53  *---------------------------------------------------------------------------*/
54 
55 static void
anjuta_session_set_limited_string_list(AnjutaSession * session,const gchar * section,const gchar * key,GList ** value)56 anjuta_session_set_limited_string_list (AnjutaSession *session, const gchar *section, const gchar *key, GList **value)
57 {
58 	GList *node;
59 
60 	while ((node = g_list_nth (*value, MAX_RECENT_ITEM)) != NULL)
61 	{
62 		g_free (node->data);
63 		*value = g_list_delete_link (*value, node);
64 	}
65 	anjuta_session_set_string_list (session, section, key, *value);
66 }
67 
68 /* The value argument is a pointer on a GFile list */
69 static void
anjuta_session_set_limited_relative_file_list(AnjutaSession * session,const gchar * section,const gchar * key,GList ** value)70 anjuta_session_set_limited_relative_file_list (AnjutaSession *session, const gchar *section, const gchar *key, GList **value)
71 {
72 	GList *item;
73 	GList *list = NULL;
74 
75 	while ((item = g_list_nth (*value, MAX_RECENT_ITEM)) != NULL)
76 	{
77 		g_object_unref (G_OBJECT (item->data));
78 		*value = g_list_delete_link (*value, item);
79 	}
80 	for (item = *value; item != NULL; item = g_list_next (item))
81 	{
82 		list = g_list_prepend (list, anjuta_session_get_relative_uri_from_file (session, (GFile *)item->data, NULL));
83 	}
84 	list = g_list_reverse (list);
85 
86 	anjuta_session_set_string_list (session, section, key, list);
87 
88 	g_list_foreach (list, (GFunc)g_free, NULL);
89 	g_list_free (list);
90 }
91 
92 static void
anjuta_session_set_strv(AnjutaSession * session,const gchar * section,const gchar * key,gchar ** value)93 anjuta_session_set_strv (AnjutaSession *session, const gchar *section, const gchar *key, gchar **value)
94 {
95 	GList *list = NULL;
96 
97 	if (value != NULL)
98 	{
99 		for (; *value != NULL; value++)
100 		{
101 			list = g_list_append (list, *value);
102 		}
103 		list = g_list_reverse (list);
104 	}
105 
106 	anjuta_session_set_string_list (session, section, key, list);
107 	g_list_free (list);
108 }
109 
110 static GList*
anjuta_session_get_relative_file_list(AnjutaSession * session,const gchar * section,const gchar * key)111 anjuta_session_get_relative_file_list (AnjutaSession *session, const gchar *section, const gchar *key)
112 {
113 	GList *list;
114 	GList *item;
115 
116  	list = anjuta_session_get_string_list (session, section, key);
117 	for (item = g_list_first (list); item != NULL; item = g_list_next (item))
118 	{
119 		GFile *file;
120 
121 		file = anjuta_session_get_file_from_relative_uri (session, (const gchar *)item->data, NULL);
122 		g_free (item->data);
123 		item->data = file;
124 	}
125 
126 	return list;
127 }
128 
129 static gchar**
anjuta_session_get_strv(AnjutaSession * session,const gchar * section,const gchar * key)130 anjuta_session_get_strv (AnjutaSession *session, const gchar *section, const gchar *key)
131 {
132 	GList *list;
133 	gchar **value = NULL;
134 
135 	list = anjuta_session_get_string_list (session, section, key);
136 
137 	if (list != NULL)
138 	{
139 		gchar **var;
140 		GList *node;
141 
142 		value = g_new (gchar *, g_list_length (list) + 1);
143 		var = value;
144 		for (node = g_list_first (list); node != NULL; node = g_list_next (node))
145 		{
146 			*var = (gchar *)node->data;
147 			++var;
148 		}
149 		*var = NULL;
150 	}
151 
152 	return value;
153 }
154 
155 /* Callback for saving session
156  *---------------------------------------------------------------------------*/
157 
158 static void
on_session_save(AnjutaShell * shell,AnjutaSessionPhase phase,AnjutaSession * session,RunProgramPlugin * self)159 on_session_save (AnjutaShell *shell, AnjutaSessionPhase phase, AnjutaSession *session, RunProgramPlugin *self)
160 {
161 	if (phase != ANJUTA_SESSION_PHASE_NORMAL)
162 		return;
163 
164 	anjuta_session_set_limited_string_list (session, "Execution", "Program arguments", &self->recent_args);
165 	anjuta_session_set_limited_relative_file_list (session, "Execution", "Program uri", &self->recent_target);
166 	anjuta_session_set_int (session, "Execution", "Run in terminal", self->run_in_terminal + 1);
167 	anjuta_session_set_limited_relative_file_list (session,"Execution", "Working directories", &self->recent_dirs);
168 	anjuta_session_set_strv (session, "Execution", "Environment variables", self->environment_vars);
169 }
170 
on_session_load(AnjutaShell * shell,AnjutaSessionPhase phase,AnjutaSession * session,RunProgramPlugin * self)171 static void on_session_load (AnjutaShell *shell, AnjutaSessionPhase phase, AnjutaSession *session, RunProgramPlugin *self)
172 {
173     gint run_in_terminal;
174 
175 	if (phase != ANJUTA_SESSION_PHASE_NORMAL)
176 		return;
177 
178  	if (self->recent_args != NULL)
179  	{
180  		g_list_foreach (self->recent_args, (GFunc)g_free, NULL);
181  		g_list_free (self->recent_args);
182  	}
183  	self->recent_args = anjuta_session_get_string_list (session, "Execution", "Program arguments");
184 
185 	g_list_foreach (self->recent_target, (GFunc)g_object_unref, NULL);
186 	g_list_free (self->recent_target);
187  	self->recent_target = anjuta_session_get_relative_file_list (session, "Execution", "Program uri");
188 
189 	/* The flag is store as 1 == FALSE, 2 == TRUE */
190 	run_in_terminal = anjuta_session_get_int (session, "Execution", "Run in terminal");
191 	if (run_in_terminal == 0)
192 		self->run_in_terminal = TRUE;	/* Default value */
193 	else
194 		self->run_in_terminal = run_in_terminal - 1;
195 
196 	g_list_foreach (self->recent_dirs, (GFunc)g_object_unref, NULL);
197  	g_list_free (self->recent_dirs);
198 	self->recent_dirs = anjuta_session_get_relative_file_list (session, "Execution", "Working directories");
199 	if (self->recent_dirs == NULL)
200 	{
201 		/* Use project directory by default */
202 		GValue value = {0,};
203 
204 		anjuta_shell_get_value (ANJUTA_PLUGIN(self)->shell,
205 		    IANJUTA_PROJECT_MANAGER_PROJECT_ROOT_URI,
206 		    &value,
207 		    NULL);
208 		if (G_VALUE_HOLDS_STRING (&value))
209 		{
210 			self->recent_dirs = g_list_append (NULL, g_file_new_for_uri (g_value_get_string (&value)));
211 		}
212 	}
213 
214 	g_strfreev (self->environment_vars);
215  	self->environment_vars = anjuta_session_get_strv (session, "Execution", "Environment variables");
216 
217 	run_plugin_update_shell_value (self);
218 }
219 
220 /* Callbacks
221  *---------------------------------------------------------------------------*/
222 
223 static void
on_run_program_activate(GtkAction * action,RunProgramPlugin * plugin)224 on_run_program_activate (GtkAction* action, RunProgramPlugin* plugin)
225 {
226 	if (plugin->child != NULL)
227 	{
228        gchar *msg = _("The program is already running.\n"
229                       	"Do you want to stop it before restarting a new instance?");
230 		if (anjuta_util_dialog_boolean_question (GTK_WINDOW ( ANJUTA_PLUGIN (plugin)->shell), TRUE, msg))
231 		{
232 			run_plugin_kill_program (plugin, FALSE);
233 		}
234 	}
235 	if (plugin->recent_target == NULL)
236 	{
237 		if (run_parameters_dialog_or_execute (plugin) != GTK_RESPONSE_APPLY)
238 		{
239 			return;
240 		}
241 	}
242 
243 	run_plugin_run_program(plugin);
244 }
245 
246 static void
on_kill_program_activate(GtkAction * action,RunProgramPlugin * plugin)247 on_kill_program_activate (GtkAction* action, RunProgramPlugin* plugin)
248 {
249 	run_plugin_kill_program (plugin, TRUE);
250 }
251 
252 static void
on_program_parameters_activate(GtkAction * action,RunProgramPlugin * plugin)253 on_program_parameters_activate (GtkAction* action, RunProgramPlugin* plugin)
254 {
255 	/* Run as a modal dialog */
256 	run_parameters_dialog_run (plugin);
257 }
258 
259 static void
on_value_run_program_added(AnjutaPlugin * plugin,const gchar * name,const GValue * value,gpointer data)260 on_value_run_program_added (AnjutaPlugin *plugin, const gchar *name, const GValue *value, gpointer data)
261 {
262 	RunProgramPlugin *self = ANJUTA_PLUGIN_RUN_PROGRAM (plugin);
263 	gchar *uri;
264 
265 	// Update last target if RUN_PROGRAM_URI is changed by another plugin
266 	anjuta_shell_get (plugin->shell,
267 	                  RUN_PROGRAM_URI, G_TYPE_STRING, &uri, NULL);
268 	if (self->recent_target != NULL)
269 	{
270 		GFile *target = g_file_new_for_uri (uri);
271 
272 		if (!g_file_equal ((GFile *)self->recent_target->data, target))
273 		{
274 			// Update target uri
275 			g_object_unref ((GObject *)self->recent_target->data);
276 			self->recent_target->data = g_object_ref (target);
277 		}
278 		g_object_unref (target);
279 	}
280 	g_free (uri);
281 }
282 
283 /* Actions table
284  *---------------------------------------------------------------------------*/
285 
286 static GtkActionEntry actions_run[] = {
287 	{
288 		"ActionMenuRun",	/* Action name */
289 		NULL,				/* Stock icon, if any */
290 		N_("_Run"),		    /* Display label */
291 		NULL,				/* short-cut */
292 		NULL,				/* Tooltip */
293 		NULL				/* action callback */
294 	},
295 	{
296 		"ActionRunProgram",
297 		GTK_STOCK_EXECUTE,
298 		N_("Execute"),
299 		"F3",
300 		N_("Run program without debugger"),
301 		G_CALLBACK (on_run_program_activate)
302 	},
303 	{
304 		"ActionStopProgram",
305 		GTK_STOCK_STOP,
306 		N_("Stop Program"),
307 		NULL,
308 		N_("Kill program"),
309 		G_CALLBACK (on_kill_program_activate)
310 	},
311 	{
312 		"ActionProgramParameters",
313 		NULL,
314 		N_("Program Parameters…"),
315 		NULL,
316 		N_("Set current program, arguments, etc."),
317 		G_CALLBACK (on_program_parameters_activate)
318 	},
319 };
320 
321 /* AnjutaPlugin functions
322  *---------------------------------------------------------------------------*/
323 
324 static gboolean
run_plugin_activate(AnjutaPlugin * plugin)325 run_plugin_activate (AnjutaPlugin *plugin)
326 {
327 	RunProgramPlugin *self = ANJUTA_PLUGIN_RUN_PROGRAM (plugin);
328 	AnjutaUI *ui;
329 
330 	DEBUG_PRINT ("%s", "Run Program Plugin: Activating plugin…");
331 
332 	/* Connect to session signal */
333 	g_signal_connect (plugin->shell, "save-session",
334 					  G_CALLBACK (on_session_save), self);
335     g_signal_connect (plugin->shell, "load-session",
336 					  G_CALLBACK (on_session_load), self);
337 
338 	/* Add watches */
339 	self->program_watch = anjuta_plugin_add_watch (ANJUTA_PLUGIN (self),
340 	                       RUN_PROGRAM_URI,
341 	                       on_value_run_program_added,
342 	                       NULL,
343 	                       NULL);
344 
345 	/* Add actions */
346 	ui = anjuta_shell_get_ui (plugin->shell, NULL);
347 	self->action_group = anjuta_ui_add_action_group_entries (ui,
348 									"ActionGroupRun", _("Run operations"),
349 									actions_run, G_N_ELEMENTS (actions_run),
350 									GETTEXT_PACKAGE, TRUE, self);
351 
352 	self->uiid = anjuta_ui_merge (ui, UI_FILE);
353 
354 	run_plugin_update_menu_sensitivity (self);
355 
356 	return TRUE;
357 }
358 
359 static gboolean
run_plugin_deactivate(AnjutaPlugin * plugin)360 run_plugin_deactivate (AnjutaPlugin *plugin)
361 {
362 	RunProgramPlugin *self = ANJUTA_PLUGIN_RUN_PROGRAM (plugin);
363 	AnjutaUI *ui;
364 
365 	DEBUG_PRINT ("%s", "Run Program Plugin: Deactivating plugin…");
366 
367 	ui = anjuta_shell_get_ui (plugin->shell, NULL);
368 	anjuta_ui_remove_action_group (ui, self->action_group);
369 
370 	anjuta_ui_unmerge (ui, self->uiid);
371 
372 	anjuta_plugin_remove_watch (plugin, self->program_watch, FALSE);
373 
374 	g_signal_handlers_disconnect_by_func (plugin->shell, G_CALLBACK (on_session_save), self);
375     g_signal_handlers_disconnect_by_func (plugin->shell, G_CALLBACK (on_session_load), self);
376 
377 
378 	return TRUE;
379 }
380 
381 /* GObject functions
382  *---------------------------------------------------------------------------*/
383 
384 /* Used in dispose and finalize */
385 static gpointer parent_class;
386 
387 static void
run_plugin_instance_init(GObject * obj)388 run_plugin_instance_init (GObject *obj)
389 {
390 	RunProgramPlugin *self = ANJUTA_PLUGIN_RUN_PROGRAM (obj);
391 
392 	self->recent_target = NULL;
393 	self->recent_args = NULL;
394 	self->recent_dirs = NULL;
395 	self->environment_vars = NULL;
396 
397 	self->child = NULL;
398 
399 	self->build_uri = NULL;
400 	self->terminal = NULL;
401 }
402 
403 /* dispose is used to unref object created with instance_init */
404 
405 static void
run_plugin_dispose(GObject * obj)406 run_plugin_dispose (GObject *obj)
407 {
408 	RunProgramPlugin *plugin = ANJUTA_PLUGIN_RUN_PROGRAM (obj);
409 
410 	/* Warning this function could be called several times */
411 
412 	if (plugin->terminal != NULL)
413 	{
414 		g_object_remove_weak_pointer (G_OBJECT (plugin->terminal),
415 		                           (void **)&plugin->terminal);
416 		plugin->terminal = NULL;
417 	}
418 
419 	run_free_all_children (plugin);
420 
421 	G_OBJECT_CLASS (parent_class)->dispose (obj);
422 }
423 
424 static void
run_plugin_finalize(GObject * obj)425 run_plugin_finalize (GObject *obj)
426 {
427 	RunProgramPlugin *self = ANJUTA_PLUGIN_RUN_PROGRAM (obj);
428 
429 	g_list_foreach (self->recent_target, (GFunc)g_object_unref, NULL);
430 	g_list_free (self->recent_target);
431 	g_list_foreach (self->recent_args, (GFunc)g_free, NULL);
432 	g_list_free (self->recent_args);
433 	g_list_foreach (self->recent_dirs, (GFunc)g_object_unref, NULL);
434 	g_list_free (self->recent_dirs);
435 	g_strfreev (self->environment_vars);
436 
437 	G_OBJECT_CLASS (parent_class)->finalize (obj);
438 }
439 
440 /* finalize used to free object created with instance init is not used */
441 
442 static void
run_plugin_class_init(GObjectClass * klass)443 run_plugin_class_init (GObjectClass *klass)
444 {
445 	AnjutaPluginClass *plugin_class = ANJUTA_PLUGIN_CLASS (klass);
446 
447 	parent_class = g_type_class_peek_parent (klass);
448 
449 	plugin_class->activate = run_plugin_activate;
450 	plugin_class->deactivate = run_plugin_deactivate;
451 	klass->dispose = run_plugin_dispose;
452 	klass->finalize = run_plugin_finalize;
453 }
454 
455 /* AnjutaPlugin declaration
456  *---------------------------------------------------------------------------*/
457 
458 ANJUTA_PLUGIN_BEGIN (RunProgramPlugin, run_plugin);
459 ANJUTA_PLUGIN_END;
460 
461 ANJUTA_SIMPLE_PLUGIN (RunProgramPlugin, run_plugin);
462 
463 /* Public functions
464  *---------------------------------------------------------------------------*/
465 
466 void
run_plugin_update_shell_value(RunProgramPlugin * plugin)467 run_plugin_update_shell_value (RunProgramPlugin *plugin)
468 {
469 	gchar *dir_uri;
470 	gchar *target_uri;
471 
472 	/* Update Anjuta shell value */
473 	target_uri = plugin->recent_target == NULL ? NULL : g_file_get_uri ((GFile *)plugin->recent_target->data);
474 	dir_uri = plugin->recent_dirs == NULL ? NULL : g_file_get_uri ((GFile *)plugin->recent_dirs->data);
475 	anjuta_shell_add (ANJUTA_PLUGIN (plugin)->shell,
476 					 	RUN_PROGRAM_URI, G_TYPE_STRING, target_uri,
477 						RUN_PROGRAM_ARGS, G_TYPE_STRING, plugin->recent_args == NULL ? NULL : plugin->recent_args->data,
478 					    RUN_PROGRAM_DIR, G_TYPE_STRING, dir_uri,
479 					  	RUN_PROGRAM_ENV, G_TYPE_STRV, plugin->environment_vars == NULL ? NULL : plugin->environment_vars,
480 						RUN_PROGRAM_NEED_TERM, G_TYPE_BOOLEAN, plugin->run_in_terminal,
481 					  	NULL);
482 	g_free (dir_uri);
483 	g_free (target_uri);
484 }
485 
486 void
run_plugin_update_menu_sensitivity(RunProgramPlugin * plugin)487 run_plugin_update_menu_sensitivity (RunProgramPlugin *plugin)
488 {
489 	GtkAction *action;
490 	action = gtk_action_group_get_action (plugin->action_group, "ActionStopProgram");
491 
492 	gtk_action_set_sensitive (action, plugin->child != NULL);
493 }
494 
495