Merge branch 'master' into git-shell
[anjuta.git] / libanjuta / anjuta-profile-manager.c
blob6c275f4fc5474da9307f6fe19c2334c038caa10f
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 * anjuta-profile-manager.c
4 * Copyright (C) Naba Kumar <naba@gnome.org>
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.
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.
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
21 /**
22 * SECTION:anjuta-profile-manager
23 * @short_description: Managers a stack of plugins profiles
24 * @see_also: #AnjutaPluginManager, #AnjutaProfile
25 * @stability: Unstable
26 * @include: libanjuta/anjuta-profile-manager.h
28 * Anjuta uses up to two profiles. A "no project" profile is used when no
29 * project is loaded a project profile when one is loaded.
30 * If a second project is loaded, it is loaded in another instance of Anjuta.
31 * When a project is closed, Anjuta goes back to the "no project" profile.
33 * The profile manager can be in a frozen state where you can push or
34 * pop a profile from the stack without triggering a change of the profile.
37 #include <string.h>
38 #include <libxml/parser.h>
39 #include <libxml/tree.h>
41 #include <libanjuta/anjuta-debug.h>
42 #include <libanjuta/anjuta-marshal.h>
43 #include "anjuta-profile-manager.h"
45 enum
47 PROP_0,
48 PROP_PLUGIN_MANAGER,
49 PROP_PROFILES
52 enum
54 PROFILE_PUSHED,
55 PROFILE_POPPED,
56 PROFILE_DESCOPED,
57 PROFILE_SCOPED,
58 LAST_SIGNAL
61 struct _AnjutaProfileManagerPriv
63 AnjutaPluginManager *plugin_manager;
64 GList *profiles;
66 /* Pending queue. Profiles are queued until freeze count becomes 0 */
67 GList *profiles_queue;
69 /* Freeze count. Pending profiles are loaded when it reaches 0 */
70 gint freeze_count;
73 static GObjectClass* parent_class = NULL;
74 static guint profile_manager_signals[LAST_SIGNAL] = { 0 };
76 static void
77 on_plugin_activated (AnjutaPluginManager *plugin_manager,
78 AnjutaPluginDescription *plugin_desc,
79 GObject *plugin_object,
80 AnjutaProfileManager *profile_manager)
82 AnjutaProfileManagerPriv *priv;
83 priv = profile_manager->priv;
85 if (priv->profiles)
87 /* Add it current profile */
88 gboolean exclude;
90 if (!anjuta_plugin_description_get_boolean (plugin_desc, "Anjuta Plugin", "ExcludeFromSession", &exclude) || !exclude)
92 anjuta_profile_add_plugin (ANJUTA_PROFILE (priv->profiles->data),
93 plugin_desc);
98 static void
99 on_plugin_deactivated (AnjutaPluginManager *plugin_manager,
100 AnjutaPluginDescription *plugin_desc,
101 GObject *plugin_object,
102 AnjutaProfileManager *profile_manager)
104 AnjutaProfileManagerPriv *priv;
105 priv = profile_manager->priv;
107 if (priv->profiles)
109 /* Remove from current profile */
110 anjuta_profile_remove_plugin (ANJUTA_PROFILE (priv->profiles->data),
111 plugin_desc);
115 static void
116 anjuta_profile_manager_init (AnjutaProfileManager *object)
118 object->priv = g_new0 (AnjutaProfileManagerPriv, 1);
121 static void
122 anjuta_profile_manager_finalize (GObject *object)
124 AnjutaProfileManagerPriv *priv;
125 priv = ANJUTA_PROFILE_MANAGER (object)->priv;
126 if (priv->profiles)
128 g_list_foreach (priv->profiles, (GFunc)g_object_unref, NULL);
129 g_list_free (priv->profiles);
130 priv->profiles = NULL;
132 if (priv->profiles_queue)
134 g_list_foreach (priv->profiles_queue, (GFunc)g_object_unref, NULL);
135 g_list_free (priv->profiles_queue);
136 priv->profiles_queue = NULL;
139 G_OBJECT_CLASS (parent_class)->finalize (object);
142 static void
143 anjuta_profile_manager_set_property (GObject *object, guint prop_id,
144 const GValue *value, GParamSpec *pspec)
146 AnjutaProfileManagerPriv *priv;
147 g_return_if_fail (ANJUTA_IS_PROFILE_MANAGER (object));
148 priv = ANJUTA_PROFILE_MANAGER (object)->priv;
150 switch (prop_id)
152 case PROP_PLUGIN_MANAGER:
153 g_return_if_fail (ANJUTA_IS_PLUGIN_MANAGER (g_value_get_object (value)));
154 priv->plugin_manager = g_value_get_object (value);
155 g_signal_connect (priv->plugin_manager, "plugin-activated",
156 G_CALLBACK (on_plugin_activated), object);
157 g_signal_connect (priv->plugin_manager, "plugin-deactivated",
158 G_CALLBACK (on_plugin_deactivated), object);
159 break;
160 default:
161 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
162 break;
166 static void
167 anjuta_profile_manager_get_property (GObject *object, guint prop_id,
168 GValue *value, GParamSpec *pspec)
170 AnjutaProfileManagerPriv *priv;
171 g_return_if_fail (ANJUTA_IS_PROFILE_MANAGER (object));
172 priv = ANJUTA_PROFILE_MANAGER (object)->priv;
174 switch (prop_id)
176 case PROP_PLUGIN_MANAGER:
177 g_value_set_object (value, priv->plugin_manager);
178 break;
179 case PROP_PROFILES:
180 g_value_set_pointer (value, priv->profiles);
181 break;
182 default:
183 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
184 break;
188 static void
189 anjuta_profile_manager_class_init (AnjutaProfileManagerClass *klass)
191 GObjectClass* object_class = G_OBJECT_CLASS (klass);
192 parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
194 object_class->finalize = anjuta_profile_manager_finalize;
195 object_class->set_property = anjuta_profile_manager_set_property;
196 object_class->get_property = anjuta_profile_manager_get_property;
198 g_object_class_install_property (object_class,
199 PROP_PLUGIN_MANAGER,
200 g_param_spec_object ("plugin-manager",
201 "Plugin Manager",
202 "The plugin manager to use for profile plugins",
203 ANJUTA_TYPE_PLUGIN_MANAGER,
204 G_PARAM_READABLE |
205 G_PARAM_WRITABLE |
206 G_PARAM_CONSTRUCT));
208 * AnjutaProfileManager::profile-pushed:
209 * @profile_manager: a #AnjutaProfileManager object.
210 * @profile: the new #AnjutaProfile added.
212 * Emitted when a profile is added in the stack. If the profile manager is
213 * not frozen, the current profile will be unloaded and the new one
214 * will be loaded.
216 profile_manager_signals[PROFILE_PUSHED] =
217 g_signal_new ("profile-pushed",
218 G_OBJECT_CLASS_TYPE (klass),
219 G_SIGNAL_RUN_FIRST,
220 G_STRUCT_OFFSET (AnjutaProfileManagerClass,
221 profile_pushed),
222 NULL, NULL,
223 anjuta_cclosure_marshal_VOID__OBJECT,
224 G_TYPE_NONE, 1,
225 ANJUTA_TYPE_PROFILE);
228 * AnjutaProfileManager::profile-popped:
229 * @profile_manager: a #AnjutaProfileManager object.
230 * @profile: the current removed #AnjutaProfile.
232 * Emitted when a profile is removed from the stack. If the profile manager
233 * is not frozen, the current profile will be unloaded and the previous one
234 * will be loaded.
236 profile_manager_signals[PROFILE_POPPED] =
237 g_signal_new ("profile-popped",
238 G_OBJECT_CLASS_TYPE (klass),
239 G_SIGNAL_RUN_FIRST,
240 G_STRUCT_OFFSET (AnjutaProfileManagerClass,
241 profile_popped),
242 NULL, NULL,
243 anjuta_cclosure_marshal_VOID__OBJECT,
244 G_TYPE_NONE, 1,
245 ANJUTA_TYPE_PROFILE);
248 * AnjutaProfileManager::profile-descoped:
249 * @profile_manager: a #AnjutaProfileManager object.
250 * @profile: the old unloaded #AnjutaProfile.
252 * Emitted when a profile will be unloaded.
254 profile_manager_signals[PROFILE_DESCOPED] =
255 g_signal_new ("profile-descoped",
256 G_OBJECT_CLASS_TYPE (klass),
257 G_SIGNAL_RUN_FIRST,
258 G_STRUCT_OFFSET (AnjutaProfileManagerClass,
259 profile_descoped),
260 NULL, NULL,
261 anjuta_cclosure_marshal_VOID__OBJECT,
262 G_TYPE_NONE, 1,
263 ANJUTA_TYPE_PROFILE);
266 * AnjutaProfileManager::profile-scoped:
267 * @profile_manager: a #AnjutaProfileManager object.
268 * @profile: the current loaded #AnjutaProfile.
270 * Emitted when a new profile is loaded.
272 profile_manager_signals[PROFILE_SCOPED] =
273 g_signal_new ("profile-scoped",
274 G_OBJECT_CLASS_TYPE (klass),
275 G_SIGNAL_RUN_FIRST,
276 G_STRUCT_OFFSET (AnjutaProfileManagerClass,
277 profile_scoped),
278 NULL, NULL,
279 anjuta_cclosure_marshal_VOID__OBJECT,
280 G_TYPE_NONE, 1,
281 ANJUTA_TYPE_PROFILE);
284 GType
285 anjuta_profile_manager_get_type (void)
287 static GType our_type = 0;
289 if(our_type == 0)
291 static const GTypeInfo our_info =
293 sizeof (AnjutaProfileManagerClass), /* class_size */
294 (GBaseInitFunc) NULL, /* base_init */
295 (GBaseFinalizeFunc) NULL, /* base_finalize */
296 (GClassInitFunc) anjuta_profile_manager_class_init, /* class_init */
297 (GClassFinalizeFunc) NULL, /* class_finalize */
298 NULL /* class_data */,
299 sizeof (AnjutaProfileManager), /* instance_size */
300 0, /* n_preallocs */
301 (GInstanceInitFunc) anjuta_profile_manager_init, /* instance_init */
302 NULL /* value_table */
305 our_type = g_type_register_static (G_TYPE_OBJECT, "AnjutaProfileManager",
306 &our_info, 0);
309 return our_type;
313 * anjuta_profile_manager_new:
314 * @plugin_manager: the #AnjutaPluginManager used by all profiles.
316 * Create a new profile manager.
318 * Return value: the new #AnjutaProfileManager object.
320 AnjutaProfileManager*
321 anjuta_profile_manager_new (AnjutaPluginManager *plugin_manager)
323 GObject *obj;
325 obj = g_object_new (ANJUTA_TYPE_PROFILE_MANAGER, "plugin-manager",
326 plugin_manager, NULL);
327 return ANJUTA_PROFILE_MANAGER (obj);
330 static gboolean
331 anjuta_profile_manager_load_profile (AnjutaProfileManager *profile_manager,
332 AnjutaProfile *profile,
333 AnjutaProfile *previous_profile,
334 GError **error)
336 AnjutaProfileManagerPriv *priv;
337 GList *active_plugins, *node;
338 GList *plugins_to_activate, *plugins_to_deactivate;
339 GList *selected_plugins;
341 GHashTable *active_plugins_hash, *plugins_to_activate_hash;
343 priv = profile_manager->priv;
345 /* Disable profile synchronization while the profile is being activated */
346 g_signal_handlers_block_by_func (priv->plugin_manager,
347 G_CALLBACK (on_plugin_activated),
348 profile_manager);
349 g_signal_handlers_block_by_func (priv->plugin_manager,
350 G_CALLBACK (on_plugin_deactivated),
351 profile_manager);
353 /* Emit pre-change for the last profile */
354 if (previous_profile)
356 g_signal_emit_by_name (profile_manager, "profile-descoped",
357 previous_profile);
360 /* Prepare plugins to activate */
361 plugins_to_activate_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
363 /* Select plugins in the profile */
364 if (profile)
365 selected_plugins = anjuta_profile_get_plugins (profile);
366 else
367 selected_plugins = NULL;
369 node = selected_plugins;
370 while (node)
372 g_hash_table_insert (plugins_to_activate_hash, node->data, node->data);
373 node = g_list_next (node);
376 /* Prepare active plugins hash */
377 active_plugins =
378 anjuta_plugin_manager_get_active_plugins (priv->plugin_manager);
379 active_plugins_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
380 node = active_plugins;
381 while (node)
383 g_hash_table_insert (active_plugins_hash, node->data, node->data);
384 node = g_list_next (node);
387 plugins_to_deactivate = NULL;
389 /* Prepare plugins to deactiveate that are already active, but are
390 * not requested to be active
392 node = active_plugins;
393 while (node)
395 if (!g_hash_table_lookup (plugins_to_activate_hash, node->data))
397 plugins_to_deactivate = g_list_prepend (plugins_to_deactivate,
398 node->data);
400 node = g_list_next (node);
402 plugins_to_deactivate = g_list_reverse (plugins_to_deactivate);
404 /* Deactivate plugins */
405 node = plugins_to_deactivate;
406 while (node)
408 AnjutaPluginDescription *desc;
409 GObject *plugin_object;
410 gchar *plugin_id = NULL;
412 desc = (AnjutaPluginDescription *)node->data;
413 anjuta_plugin_description_get_string (desc, "Anjuta Plugin",
414 "Location", &plugin_id);
415 g_assert (plugin_id != NULL);
417 /* DEBUG_PRINT ("Profile: deactivating %s", plugin_id); */
419 plugin_object =
420 anjuta_plugin_manager_get_plugin_by_id (priv->plugin_manager,
421 plugin_id);
422 g_assert (plugin_object != NULL);
424 anjuta_plugin_manager_unload_plugin (priv->plugin_manager,
425 plugin_object);
426 g_free (plugin_id);
427 node = g_list_next (node);
430 /* Prepare the plugins to activate */
431 plugins_to_activate = NULL;
432 node = selected_plugins;
433 while (node)
435 if (!g_hash_table_lookup (active_plugins_hash, node->data))
436 plugins_to_activate = g_list_prepend (plugins_to_activate,
437 node->data);
438 node = g_list_next (node);
441 /* Now activate the plugins */
442 if (plugins_to_activate)
444 /* Activate them */
445 plugins_to_activate = g_list_reverse (plugins_to_activate);
446 anjuta_plugin_manager_activate_plugins (priv->plugin_manager,
447 plugins_to_activate);
450 g_list_free (plugins_to_activate);
451 g_list_free (active_plugins);
453 g_hash_table_destroy (plugins_to_activate_hash);
454 g_hash_table_destroy (active_plugins_hash);
456 /* Enable profile synchronization */
457 g_signal_handlers_unblock_by_func (priv->plugin_manager,
458 G_CALLBACK (on_plugin_activated),
459 profile_manager);
460 g_signal_handlers_unblock_by_func (priv->plugin_manager,
461 G_CALLBACK (on_plugin_deactivated),
462 profile_manager);
463 g_signal_emit_by_name (profile_manager, "profile-scoped", profile);
464 return TRUE;
467 static gboolean
468 anjuta_profile_manager_queue_profile (AnjutaProfileManager *profile_manager,
469 AnjutaProfile *profile,
470 GError **error)
472 AnjutaProfileManagerPriv *priv;
474 priv = profile_manager->priv;
475 priv->profiles_queue = g_list_prepend (priv->profiles_queue,
476 profile);
477 /* If there is no freeze load profile now */
478 if (priv->freeze_count <= 0)
480 AnjutaProfile *previous_profile = NULL;
482 if (priv->profiles)
483 previous_profile = priv->profiles->data;
485 /* Push queued profiles in stack */
486 priv->profiles = g_list_concat (priv->profiles_queue, priv->profiles);
487 priv->profiles_queue = NULL;
489 return anjuta_profile_manager_load_profile (profile_manager,
490 ANJUTA_PROFILE (priv->profiles->data),
491 previous_profile,
492 error);
494 else
496 return FALSE;
501 * anjuta_profile_manager_push:
502 * @profile_manager: the #AnjutaProfileManager object.
503 * @profile: the new #AnjutaProfile.
504 * @error: error propagation and reporting.
506 * Add a new profile at the top of the profile manager stack. If the profile
507 * manager is not frozen, this new profile will be loaded immediatly and
508 * become the current profile.
510 * Return value: TRUE on success, FALSE otherwise.
512 gboolean
513 anjuta_profile_manager_push (AnjutaProfileManager *profile_manager,
514 AnjutaProfile *profile, GError **error)
516 AnjutaProfileManagerPriv *priv;
518 g_return_val_if_fail (ANJUTA_IS_PROFILE_MANAGER (profile_manager), FALSE);
519 priv = profile_manager->priv;
521 /* Emit profile push signal */
522 g_signal_emit_by_name (profile_manager, "profile-pushed",
523 profile);
525 return anjuta_profile_manager_queue_profile (profile_manager, profile,
526 error);
530 * anjuta_profile_manager_pop:
531 * @profile_manager: the #AnjutaProfileManager object.
532 * @profile_name: the name of the profile to remove.
533 * @error: error propagation and reporting.
535 * Remove a profile from the profile manager stack. If the manager is not
536 * frozen, only the current profile can be removed. It will be unloaded and
537 * the previous profile will be loaded.
538 * If the manager is frozen, the current profile or the last pushed profile
539 * can be removed.
541 * Return value: TRUE on success, FALSE otherwise.
543 gboolean
544 anjuta_profile_manager_pop (AnjutaProfileManager *profile_manager,
545 const gchar *profile_name, GError **error)
547 AnjutaProfileManagerPriv *priv;
548 AnjutaProfile *profile;
549 g_return_val_if_fail (ANJUTA_IS_PROFILE_MANAGER (profile_manager), FALSE);
551 priv = profile_manager->priv;
553 /* First check in the queue */
554 if (priv->profiles_queue)
556 profile = priv->profiles_queue->data;
557 g_return_val_if_fail (strcmp (anjuta_profile_get_name (profile),
558 profile_name) == 0, FALSE);
559 priv->profiles_queue = g_list_remove (priv->profiles_queue, profile);
561 g_signal_emit_by_name (profile_manager, "profile-popped",
562 profile);
564 g_object_unref (profile);
565 return TRUE;
568 /* Then check in the current stack */
569 if (priv->profiles)
571 profile = priv->profiles->data;
572 g_return_val_if_fail (strcmp (anjuta_profile_get_name (profile),
573 profile_name) == 0, FALSE);
574 priv->profiles = g_list_remove (priv->profiles, profile);
576 g_signal_emit_by_name (profile_manager, "profile-popped",
577 profile);
579 /* Restore the next profile in the stack */
580 if (priv->profiles)
582 return anjuta_profile_manager_load_profile (profile_manager,
583 ANJUTA_PROFILE (priv->profiles->data),
584 profile,
585 error);
587 else
589 return anjuta_profile_manager_load_profile (profile_manager,
590 NULL, profile,
591 error);
593 g_object_unref (profile);
596 g_warning ("No profiles in the stack. Can not pop out any profile: %s",
597 profile_name);
598 return FALSE;
602 * anjuta_profile_manager_freeze:
603 * @profile_manager: the #AnjutaProfileManager object.
605 * Freeze the plugin manager. In this state, plugins can be added and removed
606 * from the stack without triggering any change in the current profile. It is
607 * possible to freeze the manager several times but it will be back in its normal
608 * state only after as much call of anjuta_profile_manager_thaw().
610 void
611 anjuta_profile_manager_freeze (AnjutaProfileManager *profile_manager)
613 AnjutaProfileManagerPriv *priv;
614 g_return_if_fail (ANJUTA_IS_PROFILE_MANAGER (profile_manager));
615 priv = profile_manager->priv;
616 priv->freeze_count++;
620 * anjuta_profile_manager_thaw:
621 * @profile_manager: the #AnjutaProfileManager object.
622 * @error: error propagation and reporting.
624 * Put back the plugin manager in its normal mode after calling
625 * anjuta_profile_manager_freeze(). It will load a new profile if one has been
626 * added while the manager was frozen.
628 * Return value: TRUE on success, FALSE otherwise.
630 gboolean
631 anjuta_profile_manager_thaw (AnjutaProfileManager *profile_manager,
632 GError **error)
634 AnjutaProfileManagerPriv *priv;
635 g_return_val_if_fail (ANJUTA_IS_PROFILE_MANAGER (profile_manager), FALSE);
636 priv = profile_manager->priv;
638 if (priv->freeze_count > 0)
639 priv->freeze_count--;
641 if (priv->freeze_count <= 0 && priv->profiles_queue)
643 AnjutaProfile *previous_profile = NULL;
645 if (priv->profiles)
646 previous_profile = priv->profiles->data;
648 /* Push queued profiles in stack */
649 priv->profiles = g_list_concat (priv->profiles_queue, priv->profiles);
650 priv->profiles_queue = NULL;
652 /* Load the profile */
653 return anjuta_profile_manager_load_profile (profile_manager,
654 ANJUTA_PROFILE (priv->profiles->data),
655 previous_profile,
656 error);
658 else
660 return FALSE;
665 * anjuta_profile_manager_get_current :
666 * @profile_manager: A #AnjutaProfileManager object.
668 * Return the current profile.
670 * Return value: a #AnjutaProfile object or NULL if the profile stack is empty.
672 AnjutaProfile*
673 anjuta_profile_manager_get_current (AnjutaProfileManager *profile_manager)
675 g_return_val_if_fail (ANJUTA_IS_PROFILE_MANAGER (profile_manager), NULL);
676 if (profile_manager->priv->profiles_queue)
677 return ANJUTA_PROFILE (profile_manager->priv->profiles_queue->data);
678 else if (profile_manager->priv->profiles)
679 return ANJUTA_PROFILE (profile_manager->priv->profiles->data);
680 else
681 return NULL;