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