1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3  * anjuta-profile.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.
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  * SECTION:anjuta-profile
23  * @short_description: Profile is a collection of plugins
24  * @see_also: #AnjutaProfileManager, #AnjutaPlugin
25  * @stability: Unstable
26  * @include: libanjuta/anjuta-profile.h
27  *
28  * A anjuta profile contains the list of all plugins used in one Anjuta session.
29  * It is possible to add and remove plugins,
30  * check if one is included or get the whole list. The plugins list can be saved
31  * into a xml file and loaded from it.
32  *
33  * A profile in an Anjuta session includes plugins from up to 3 different xml
34  * sources:
35  *	<variablelist>
36  *    <varlistentry>
37  *    <term>$prefix/share/anjuta/profiles/default.profile</term>
38  *    <listitem>
39  *    <para>
40  *        This contains the system plugins. It is loaded in special system
41  *        profile and contains mandatory plugins for Anjuta. These plugins
42  *        cannot be unloaded. Anjuta can load a different profile using the -P
43  *        command line option.
44  *    </para>
45  *    </listitem>
46  *    </varlistentry>
47  *    <varlistentry>
48  *    <term>$project_dir/$project_name.anjuta</term>
49  *    <listitem>
50  *    <para>
51  *        This contains the project plugins. It lists mandatory plugins for the
52  *        project. This file is version controlled and distributed with the source
53  *        code. Every user working on the project uses the same one. If there
54  *        is no project loaded, no project plugins are loaded.
55  *    </para>
56  *    </listitem>
57  *    </varlistentry>
58  *    <varlistentry>
59  *    <term>$project_dir/.anjuta/default.profile</term>
60  *    <listitem>
61  *    <para>
62  *        This contains the user plugins. This is the only list of plugins
63  *        which is updated when the user add or remove one plugin.
64  *        If there is no project loaded, the user home directory is used
65  *        instead of the project directory but this list is used only in this case.
66  *        There is no global user plugins list.
67  *    </para>
68  *    </listitem>
69  *    </varlistentry>
70  * </variablelist>
71  */
72 
73 #include <glib/gi18n.h>
74 #include <glib.h>
75 #include <string.h>
76 #include <libxml/parser.h>
77 #include <libxml/tree.h>
78 
79 #include "anjuta-profile.h"
80 #include "anjuta-marshal.h"
81 #include "anjuta-debug.h"
82 
83 enum
84 {
85 	PROP_0,
86 	PROP_PLUGIN_MANAGER,
87 	PROP_PROFILE_NAME,
88 	PROP_SYNC_FILE,
89 };
90 
91 enum
92 {
93 	PLUGIN_ADDED,
94 	PLUGIN_REMOVED,
95 	CHANGED,
96 	DESCOPED,
97 	SCOPED,
98 	LAST_SIGNAL
99 };
100 
101 typedef struct _AnjutaProfileXml AnjutaProfileXml;
102 
103 struct _AnjutaProfileXml
104 {
105 	GFile *file;
106 	xmlDocPtr doc;
107 	gboolean exclude_from_sync;
108 	AnjutaProfileXml *next;
109 };
110 
111 
112 struct _AnjutaProfilePriv
113 {
114 	gchar *name;
115 	AnjutaPluginManager *plugin_manager;
116 	GHashTable *plugins_to_load;
117 	GHashTable *plugins_to_exclude_from_sync;
118 	GList *plugins_to_disable;
119 	GList *configuration;
120 	GList *config_keys;
121 	GFile *sync_file;
122 	AnjutaProfileXml *xml;
123 };
124 
125 static GObjectClass* parent_class = NULL;
126 static guint profile_signals[LAST_SIGNAL] = { 0 };
127 
128 GQuark
anjuta_profile_error_quark(void)129 anjuta_profile_error_quark (void)
130 {
131 	static GQuark quark = 0;
132 
133 	if (quark == 0) {
134 		quark = g_quark_from_static_string ("anjuta-profile-quark");
135 	}
136 
137 	return quark;
138 }
139 
140 static void
anjuta_profile_init(AnjutaProfile * object)141 anjuta_profile_init (AnjutaProfile *object)
142 {
143 	object->priv = g_new0 (AnjutaProfilePriv, 1);
144 	object->priv->plugins_to_load = g_hash_table_new (g_direct_hash,
145 	                                                  g_direct_equal);
146 	object->priv->plugins_to_exclude_from_sync = g_hash_table_new (g_direct_hash,
147 	                                                               g_direct_equal);
148 }
149 
150 static void
anjuta_profile_finalize(GObject * object)151 anjuta_profile_finalize (GObject *object)
152 {
153 	AnjutaProfilePriv *priv = ANJUTA_PROFILE (object)->priv;
154 	g_free (priv->name);
155 	g_hash_table_destroy (priv->plugins_to_load);
156 	g_hash_table_destroy (priv->plugins_to_exclude_from_sync);
157 	g_list_free (priv->plugins_to_disable);
158 	g_list_free_full (priv->config_keys, (GDestroyNotify)g_free);
159 	g_list_free (priv->configuration);
160 
161 	while (priv->xml != NULL)
162 	{
163 		AnjutaProfileXml *next;
164 
165 		next = priv->xml->next;
166 		g_object_unref (priv->xml->file);
167 		g_free (priv->xml);
168 		priv->xml = next;
169 	}
170 
171 	G_OBJECT_CLASS (parent_class)->finalize (object);
172 }
173 
174 static void
anjuta_profile_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)175 anjuta_profile_set_property (GObject *object, guint prop_id,
176 							 const GValue *value, GParamSpec *pspec)
177 {
178 	AnjutaProfilePriv *priv = ANJUTA_PROFILE (object)->priv;
179 
180 	g_return_if_fail (ANJUTA_IS_PROFILE (object));
181 
182 	switch (prop_id)
183 	{
184 	case PROP_PLUGIN_MANAGER:
185 		priv->plugin_manager = g_value_get_object (value);
186 		break;
187 	case PROP_PROFILE_NAME:
188 		g_return_if_fail (g_value_get_string (value) != NULL);
189 		g_free (priv->name);
190 		priv->name = g_strdup (g_value_get_string (value));
191 		break;
192 	case PROP_SYNC_FILE:
193 		if (priv->sync_file)
194 				g_object_unref (priv->sync_file);
195 		priv->sync_file = g_value_dup_object (value);
196 		break;
197 	default:
198 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
199 		break;
200 	}
201 }
202 
203 static void
anjuta_profile_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)204 anjuta_profile_get_property (GObject *object, guint prop_id,
205 							 GValue *value, GParamSpec *pspec)
206 {
207 	AnjutaProfilePriv *priv = ANJUTA_PROFILE (object)->priv;
208 
209 	g_return_if_fail (ANJUTA_IS_PROFILE (object));
210 
211 	switch (prop_id)
212 	{
213 	case PROP_PLUGIN_MANAGER:
214 		g_value_set_object (value, priv->plugin_manager);
215 		break;
216 	case PROP_PROFILE_NAME:
217 		g_value_set_string (value, priv->name);
218 		break;
219 	case PROP_SYNC_FILE:
220 		g_value_set_object (value, priv->sync_file);
221 		break;
222 	default:
223 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
224 		break;
225 	}
226 }
227 
228 static void
anjuta_profile_plugin_added(AnjutaProfile * self,AnjutaPluginHandle * plugin)229 anjuta_profile_plugin_added (AnjutaProfile *self,
230 							 AnjutaPluginHandle *plugin)
231 {
232 }
233 
234 static void
anjuta_profile_plugin_removed(AnjutaProfile * self,AnjutaPluginHandle * plugin)235 anjuta_profile_plugin_removed (AnjutaProfile *self,
236 							   AnjutaPluginHandle *plugin)
237 {
238 }
239 
240 static void
anjuta_profile_changed(AnjutaProfile * self)241 anjuta_profile_changed (AnjutaProfile *self)
242 {
243 	GError *error = NULL;
244 	anjuta_profile_sync (self, &error);
245 	if (error)
246 	{
247 		g_warning ("Failed to synchronize plugins profile '%s': %s",
248 				   self->priv->name, error->message);
249 		g_error_free (error);
250 	}
251 }
252 
253 static void
anjuta_profile_class_init(AnjutaProfileClass * klass)254 anjuta_profile_class_init (AnjutaProfileClass *klass)
255 {
256 	GObjectClass* object_class = G_OBJECT_CLASS (klass);
257 	parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
258 
259 	object_class->finalize = anjuta_profile_finalize;
260 	object_class->set_property = anjuta_profile_set_property;
261 	object_class->get_property = anjuta_profile_get_property;
262 
263 	klass->plugin_added = anjuta_profile_plugin_added;
264 	klass->plugin_removed = anjuta_profile_plugin_removed;
265 	klass->changed = anjuta_profile_changed;
266 
267 	g_object_class_install_property (object_class,
268 	                                 PROP_PLUGIN_MANAGER,
269 	                                 g_param_spec_object ("plugin-manager",
270 											  _("Plugin Manager"),
271 											  _("The plugin manager to use for resolving plugins"),
272 											  ANJUTA_TYPE_PLUGIN_MANAGER,
273 											  G_PARAM_READABLE |
274 											  G_PARAM_WRITABLE |
275 											  G_PARAM_CONSTRUCT));
276 	g_object_class_install_property (object_class,
277 	                                 PROP_PROFILE_NAME,
278 	                                 g_param_spec_string ("profile-name",
279 											  _("Profile Name"),
280 											  _("Name of the plugin profile"),
281 											  NULL,
282 											  G_PARAM_READABLE |
283 											  G_PARAM_WRITABLE |
284 											  G_PARAM_CONSTRUCT));
285 	g_object_class_install_property (object_class,
286 	                                 PROP_SYNC_FILE,
287 	                                 g_param_spec_object ("sync-file",
288 											  _("Synchronization file"),
289 											  _("File to syncronize the profile XML"),
290 											  G_TYPE_FILE,
291 											  G_PARAM_READABLE |
292 											  G_PARAM_WRITABLE |
293 											  G_PARAM_CONSTRUCT));
294 
295 	/**
296 	 * AnjutaProfile::plugin-added:
297 	 * @profile: a #AnjutaProfile object.
298 	 * @plugin: the new plugin as a #AnjutaPluginHandle.
299 	 *
300 	 * Emitted when a plugin is added in the list.
301 	 */
302 	profile_signals[PLUGIN_ADDED] =
303 		g_signal_new ("plugin-added",
304 		              G_OBJECT_CLASS_TYPE (klass),
305 		              G_SIGNAL_RUN_FIRST,
306 		              G_STRUCT_OFFSET (AnjutaProfileClass, plugin_added),
307 		              NULL, NULL,
308 		              anjuta_cclosure_marshal_VOID__POINTER,
309 		              G_TYPE_NONE, 1,
310 		              G_TYPE_POINTER);
311 
312 	/**
313 	 * AnjutaProfile::plugin-removed:
314 	 * @profile: a #AnjutaProfile object.
315 	 * @plugin: the removed plugin as a #AnjutaPluginHandle.
316 	 *
317 	 * Emitted when a plugin is removed from the list.
318 	 */
319 	profile_signals[PLUGIN_REMOVED] =
320 		g_signal_new ("plugin-removed",
321 		              G_OBJECT_CLASS_TYPE (klass),
322 		              G_SIGNAL_RUN_FIRST,
323 		              G_STRUCT_OFFSET (AnjutaProfileClass, plugin_removed),
324 		              NULL, NULL,
325 		              anjuta_cclosure_marshal_VOID__POINTER,
326 		              G_TYPE_NONE, 1,
327 		              G_TYPE_POINTER);
328 
329 	/**
330 	 * AnjutaProfile::changed:
331 	 * @profile: a #AnjutaProfile object.
332 	 *
333 	 * Emitted when a plugin is added or removed from the list.
334 	 */
335 	profile_signals[CHANGED] =
336 		g_signal_new ("changed",
337 		              G_OBJECT_CLASS_TYPE (klass),
338 		              G_SIGNAL_RUN_FIRST,
339 		              G_STRUCT_OFFSET (AnjutaProfileClass, changed),
340 		              NULL, NULL,
341 		              anjuta_cclosure_marshal_VOID__VOID,
342 		              G_TYPE_NONE, 0);
343 
344 	/**
345 	 * AnjutaProfile::profile-descoped:
346 	 * @profile: the old unloaded #AnjutaProfile
347 	 *
348 	 * Emitted when a profile will be unloaded.
349 	 */
350 	profile_signals[DESCOPED] =
351 		g_signal_new ("descoped",
352 		              G_OBJECT_CLASS_TYPE (klass),
353 		              G_SIGNAL_RUN_FIRST,
354 		              G_STRUCT_OFFSET (AnjutaProfileClass,
355 									   descoped),
356 		              NULL, NULL,
357 					  g_cclosure_marshal_VOID__VOID,
358 		              G_TYPE_NONE, 0);
359 
360 	/**
361 	 * AnjutaProfileManager::profile-scoped:
362 	 * @profile_manager: a #AnjutaProfileManager object.
363 	 * @profile: the current loaded #AnjutaProfile.
364 	 *
365 	 * Emitted when a new profile is loaded.
366 	 */
367 	profile_signals[SCOPED] =
368 		g_signal_new ("scoped",
369 		              G_OBJECT_CLASS_TYPE (klass),
370 		              G_SIGNAL_RUN_FIRST,
371 		              G_STRUCT_OFFSET (AnjutaProfileClass,
372 									   scoped),
373 		              NULL, NULL,
374 					  g_cclosure_marshal_VOID__VOID,
375 		              G_TYPE_NONE, 0);
376 }
377 
378 GType
anjuta_profile_get_type(void)379 anjuta_profile_get_type (void)
380 {
381 	static GType our_type = 0;
382 
383 	if(our_type == 0)
384 	{
385 		static const GTypeInfo our_info =
386 		{
387 			sizeof (AnjutaProfileClass), /* class_size */
388 			(GBaseInitFunc) NULL, /* base_init */
389 			(GBaseFinalizeFunc) NULL, /* base_finalize */
390 			(GClassInitFunc) anjuta_profile_class_init, /* class_init */
391 			(GClassFinalizeFunc) NULL, /* class_finalize */
392 			NULL /* class_data */,
393 			sizeof (AnjutaProfile), /* instance_size */
394 			0, /* n_preallocs */
395 			(GInstanceInitFunc) anjuta_profile_init, /* instance_init */
396 			NULL /* value_table */
397 		};
398 
399 		our_type = g_type_register_static (G_TYPE_OBJECT, "AnjutaProfile",
400 		                                   &our_info, 0);
401 	}
402 
403 	return our_type;
404 }
405 
406 static void
on_plugin_activated(AnjutaPluginManager * plugin_manager,AnjutaPluginHandle * plugin_handle,GObject * plugin_object,AnjutaProfile * profile)407 on_plugin_activated (AnjutaPluginManager *plugin_manager,
408 					 AnjutaPluginHandle *plugin_handle,
409 					 GObject *plugin_object,
410 					 AnjutaProfile *profile)
411 {
412 	/* Add it current profile */
413 	gboolean exclude;
414 	AnjutaPluginDescription *desc;
415 
416 	desc = anjuta_plugin_handle_get_description (plugin_handle);
417 	if (!anjuta_plugin_description_get_boolean (desc, "Anjuta Plugin", "ExcludeFromSession", &exclude) || !exclude)
418 	{
419 		anjuta_profile_add_plugin (profile, plugin_handle);
420 	}
421 }
422 
423 static void
on_plugin_deactivated(AnjutaPluginManager * plugin_manager,AnjutaPluginHandle * plugin_handle,GObject * plugin_object,AnjutaProfile * profile)424 on_plugin_deactivated (AnjutaPluginManager *plugin_manager,
425 					   AnjutaPluginHandle *plugin_handle,
426 					   GObject *plugin_object,
427 					   AnjutaProfile *profile)
428 {
429 	/* Remove from current profile */
430 	anjuta_profile_remove_plugin (profile, plugin_handle);
431 }
432 
433 /**
434  * anjuta_profile_new:
435  * @name: the new profile name.
436  * @plugin_manager: the #AnjutaPluginManager used by this profile.
437  *
438  * Create a new profile.
439  *
440  * Return value: the new #AnjutaProfile object.
441  */
442 AnjutaProfile*
anjuta_profile_new(const gchar * name,AnjutaPluginManager * plugin_manager)443 anjuta_profile_new (const gchar* name, AnjutaPluginManager *plugin_manager)
444 {
445 	GObject *profile;
446 	profile = g_object_new (ANJUTA_TYPE_PROFILE, "profile-name", name,
447 							"plugin-manager", plugin_manager, NULL);
448 	return ANJUTA_PROFILE (profile);
449 }
450 
451 /**
452  * anjuta_profile_get_name:
453  * @profile: a #AnjutaProfile object.
454  *
455  * Get the profile name.
456  *
457  * Return value: the profile name.
458  */
459 const gchar*
anjuta_profile_get_name(AnjutaProfile * profile)460 anjuta_profile_get_name (AnjutaProfile *profile)
461 {
462 	AnjutaProfilePriv *priv;
463 	g_return_val_if_fail (ANJUTA_IS_PROFILE (profile), NULL);
464 	priv = ANJUTA_PROFILE (profile)->priv;
465 	return priv->name;
466 }
467 
468 /**
469  * anjuta_profile_add_plugin:
470  * @profile: a #AnjutaProfile object.
471  * @plugin: a #AnjutaPluginHandle.
472  *
473  * Add one plugin into the profile plugin list.
474  */
475 void
anjuta_profile_add_plugin(AnjutaProfile * profile,AnjutaPluginHandle * plugin)476 anjuta_profile_add_plugin (AnjutaProfile *profile,
477 						   AnjutaPluginHandle *plugin)
478 {
479 	AnjutaProfilePriv *priv;
480 	g_return_if_fail (ANJUTA_IS_PROFILE (profile));
481 	g_return_if_fail (plugin != NULL);
482 	priv = ANJUTA_PROFILE (profile)->priv;
483 	if (g_hash_table_lookup (priv->plugins_to_load, plugin) == NULL)
484 	{
485 		g_hash_table_add (priv->plugins_to_load, plugin);
486 		g_signal_emit_by_name (profile, "plugin-added", plugin);
487 		g_signal_emit_by_name (profile, "changed");
488 	}
489 }
490 
491 /**
492  * anjuta_profile_remove_plugin:
493  * @profile: a #AnjutaProfile object.
494  * @plugin: a #AnjutaPluginHandle.
495  *
496  * Remove one plugin from the profile plugin list.
497  */
498 void
anjuta_profile_remove_plugin(AnjutaProfile * profile,AnjutaPluginHandle * plugin)499 anjuta_profile_remove_plugin (AnjutaProfile *profile,
500 							  AnjutaPluginHandle *plugin)
501 {
502 	AnjutaProfilePriv *priv;
503 	g_return_if_fail (ANJUTA_IS_PROFILE (profile));
504 	g_return_if_fail (plugin != NULL);
505 	priv = ANJUTA_PROFILE (profile)->priv;
506 	if (g_hash_table_remove (priv->plugins_to_load, plugin))
507 	{
508 		g_hash_table_remove (priv->plugins_to_exclude_from_sync, plugin);
509 		g_signal_emit_by_name (profile, "plugin-removed", plugin);
510 		g_signal_emit_by_name (profile, "changed");
511 	}
512 }
513 
514 /**
515  * anjuta_profile_has_plugin:
516  * @profile: a #AnjutaProfile object
517  * @plugin: a #AnjutaPluginHandle
518  *
519  * Check if a plugin is included in the profile plugin list.
520  *
521  * Return value: %TRUE if the plugin is included in the list.
522  */
523 gboolean
anjuta_profile_has_plugin(AnjutaProfile * profile,AnjutaPluginHandle * plugin)524 anjuta_profile_has_plugin (AnjutaProfile *profile,
525 						   AnjutaPluginHandle *plugin)
526 {
527 	AnjutaProfilePriv *priv;
528 	g_return_val_if_fail (ANJUTA_IS_PROFILE (profile), FALSE);
529 	g_return_val_if_fail (plugin != NULL, FALSE);
530 	priv = ANJUTA_PROFILE (profile)->priv;
531 
532 	return g_hash_table_lookup (priv->plugins_to_load, plugin) != NULL;
533 }
534 
535 static gboolean
anjuta_profile_configure_plugins(AnjutaProfile * profile,GList * handles_list,GList * config_list)536 anjuta_profile_configure_plugins (AnjutaProfile *profile,
537                                   GList *handles_list,
538                                   GList *config_list)
539 {
540 	AnjutaProfilePriv *priv;
541 	GList *item;
542 	GList *config;
543 
544 	g_return_val_if_fail (ANJUTA_IS_PROFILE (profile), FALSE);
545 
546 	priv = ANJUTA_PROFILE (profile)->priv;
547 	for (config = config_list, item = handles_list; item != NULL; item = g_list_next (item), config = g_list_next (config))
548 	{
549 		GList *plugin;
550 		GList *set;
551 
552 		for (plugin = g_list_first ((GList *)item->data); plugin != NULL; plugin = g_list_next (plugin))
553 		{
554 			AnjutaPluginHandle *handle = ANJUTA_PLUGIN_HANDLE (plugin->data);
555 			AnjutaPluginDescription *desc;
556 
557 			desc = anjuta_plugin_handle_get_description (handle);
558 			for (set = g_list_first ((GList *)config->data); set != NULL; set = g_list_next (set))
559 			{
560 				gchar *group = (gchar *)set->data;
561 				gchar *key = group + strlen (group) + 1;
562 				gchar *value = key + strlen (key) + 1;
563 
564 				anjuta_plugin_description_override (desc, group, key, value);
565 				priv->configuration = g_list_prepend (priv->configuration, group);
566 				priv->configuration = g_list_prepend (priv->configuration, handle);
567 			}
568 		}
569 		for (set = g_list_first ((GList *)config->data); set != NULL; set = g_list_delete_link (set, set))
570 		{
571 			priv->config_keys = g_list_prepend (priv->config_keys, set->data);
572 		}
573 	}
574 	g_list_free (config_list);
575 
576 	return TRUE;
577 }
578 
579 
580 static gboolean
anjuta_profile_unconfigure_plugins(AnjutaProfile * profile)581 anjuta_profile_unconfigure_plugins (AnjutaProfile *profile)
582 {
583 	AnjutaProfilePriv *priv;
584 	GList *item;
585 
586 	g_return_val_if_fail (ANJUTA_IS_PROFILE (profile), FALSE);
587 
588 	priv = ANJUTA_PROFILE (profile)->priv;
589 	for (item = g_list_first (priv->configuration); item != NULL; item = g_list_delete_link (item, item))
590 	{
591 		AnjutaPluginHandle *handle = ANJUTA_PLUGIN_HANDLE (item->data);
592 		AnjutaPluginDescription *desc;
593 		gchar *group;
594 		gchar *key;
595 
596 		item = g_list_delete_link (item, item);
597 		group = (gchar *)(item->data);
598 		key = group + strlen (group) + 1;
599 
600 		desc = anjuta_plugin_handle_get_description (handle);
601 		anjuta_plugin_description_remove (desc, group, key);
602 	}
603 	priv->configuration = NULL;
604 	g_list_free_full (priv->config_keys, (GDestroyNotify)g_free);
605 	priv->config_keys = NULL;
606 
607 	return TRUE;
608 }
609 
610 static GList*
anjuta_profile_select_plugins(AnjutaProfile * profile,GList * handles_list)611 anjuta_profile_select_plugins (AnjutaProfile *profile,
612 							   GList *handles_list)
613 {
614 	GList *selected_plugins = NULL;
615 	GList *node = handles_list;
616 	AnjutaProfilePriv *priv;
617 
618 	priv = profile->priv;
619 
620 	while (node)
621 	{
622 		GList *descs = node->data;
623 		if (g_list_length (descs) == 1)
624 		{
625 			selected_plugins = g_list_prepend (selected_plugins, descs->data);
626 		}
627 		else
628 		{
629 			AnjutaPluginHandle* handle;
630 			handle = anjuta_plugin_manager_select (priv->plugin_manager,
631 			                                       _("Select a plugin"),
632 			                                       _("Please select a plugin from the list"),
633 			                                       descs);
634 			if (handle)
635 				selected_plugins = g_list_prepend (selected_plugins, handle);
636 		}
637 		node = g_list_next (node);
638 	}
639 	return g_list_reverse (selected_plugins);
640 }
641 
642 
643 /* Read profile from XML
644  *---------------------------------------------------------------------------*/
645 
646 /* Error during parsing */
647 static gboolean
set_parse_error(GError ** error,GFile * file)648 set_parse_error (GError **error, GFile*file)
649 {
650 	gchar *uri = g_file_get_uri (file);
651 
652 	g_error_free (*error);
653 	*error = g_error_new (ANJUTA_PROFILE_ERROR,
654 	                      ANJUTA_PROFILE_ERROR_URI_READ_FAILED,
655 	                      _("Failed to read '%s': XML parse error. "
656 	                        "Invalid or corrupted Anjuta plugins profile."),
657 	                      uri);
658 	g_free (uri);
659 
660 	return FALSE;
661 }
662 
663 static xmlDocPtr
load_profile_from_xml(GFile * file,GError ** error)664 load_profile_from_xml (GFile *file, GError **error)
665 {
666 	gchar *read_buf;
667 	gsize size;
668 	xmlDocPtr xml_doc;
669 
670 	/* Read xml file */
671 	if (!g_file_load_contents (file, NULL, &read_buf, &size, NULL, error))
672 	{
673 		return NULL;
674 	}
675 
676 	/* Parse xml file */
677 	xml_doc = xmlParseMemory (read_buf, size);
678 	g_free (read_buf);
679 	if (xml_doc != NULL)
680 	{
681 		xmlNodePtr xml_root;
682 
683 		xml_root = xmlDocGetRootElement(xml_doc);
684 		if (xml_root ||
685 			(xml_root->name) ||
686 			xmlStrEqual(xml_root->name, (const xmlChar *)"anjuta"))
687 		{
688 			return xml_doc;
689 		}
690 		xmlFreeDoc(xml_doc);
691 	}
692 	set_parse_error (error, file);
693 
694 	return NULL;
695 }
696 
697 static GList *
parse_set(xmlNodePtr xml_node,GFile * file,GError ** error)698 parse_set (xmlNodePtr xml_node, GFile *file, GError **error)
699 {
700 	GList *config = NULL;
701 	gboolean parse_error = FALSE;
702 	xmlNodePtr xml_require_node;
703 
704 	/* Read attribute conditions */
705 	for (xml_require_node = xml_node->xmlChildrenNode;
706 	     xml_require_node;
707 	     xml_require_node = xml_require_node->next)
708 	{
709 		xmlChar *group;
710 		xmlChar *attrib;
711 		xmlChar *value;
712 
713 		if (!xml_require_node->name ||
714 		    !xmlStrEqual (xml_require_node->name,
715 		                  (const xmlChar*)"set"))
716 		{
717 			continue;
718 		}
719 		group = xmlGetProp (xml_require_node,
720 		                    (const xmlChar *)"group");
721 		attrib = xmlGetProp(xml_require_node,
722 		                    (const xmlChar *)"attribute");
723 		value = xmlGetProp(xml_require_node,
724 		                   (const xmlChar *)"value");
725 
726 		if (group && attrib && value)
727 		{
728 			GString *str;
729 
730 			str = g_string_new ((const gchar *)group);
731 			g_string_append_c (str, '\0');
732 			g_string_append (str, (const gchar *)attrib);
733 			g_string_append_c (str, '\0');
734 			g_string_append (str, (const gchar *)value);
735 
736 			config = g_list_prepend (config, g_string_free (str, FALSE));
737 		}
738 		else
739 		{
740 			parse_error = TRUE;
741 			g_warning ("XML parse error: group, attribute and value should be defined in set");
742 		}
743 		if (group) xmlFree (group);
744 		if (attrib) xmlFree (attrib);
745 		if (value) xmlFree (value);
746 		if (parse_error) break;
747 	}
748 
749 	if (parse_error)
750 	{
751 		set_parse_error (error, file);
752 	}
753 
754 	return g_list_reverse (config);
755 }
756 
757 
758 static GList *
parse_requires(xmlNodePtr xml_node,AnjutaPluginManager * plugin_manager,GFile * file,GError ** error)759 parse_requires (xmlNodePtr xml_node, AnjutaPluginManager *plugin_manager, GFile *file, GError **error)
760 {
761 	GList *plugin_handles = NULL;
762 	GList *groups = NULL;
763 	GList *attribs = NULL;
764 	GList *values = NULL;
765 	gboolean parse_error = FALSE;
766 	xmlNodePtr xml_require_node;
767 
768 	/* Read attribute conditions */
769 	for (xml_require_node = xml_node->xmlChildrenNode;
770 	     xml_require_node;
771 	     xml_require_node = xml_require_node->next)
772 	{
773 		xmlChar *group;
774 		xmlChar *attrib;
775 		xmlChar *value;
776 
777 		if (!xml_require_node->name ||
778 		    !xmlStrEqual (xml_require_node->name,
779 		                  (const xmlChar*)"require"))
780 		{
781 			continue;
782 		}
783 		group = xmlGetProp (xml_require_node,
784 		                    (const xmlChar *)"group");
785 		attrib = xmlGetProp(xml_require_node,
786 		                    (const xmlChar *)"attribute");
787 		value = xmlGetProp(xml_require_node,
788 		                   (const xmlChar *)"value");
789 
790 		if (group && attrib && value)
791 		{
792 			groups = g_list_prepend (groups, group);
793 			attribs = g_list_prepend (attribs, attrib);
794 			values = g_list_prepend (values, value);
795 		}
796 		else
797 		{
798 			if (group) xmlFree (group);
799 			if (attrib) xmlFree (attrib);
800 			if (value) xmlFree (value);
801 			parse_error = TRUE;
802 			g_warning ("XML parse error: group, attribute and value should be defined in require");
803 			break;
804 		}
805 	}
806 
807 	if (parse_error)
808 	{
809 		set_parse_error (error, file);
810 	}
811 	else
812 	{
813 		if (g_list_length (groups) == 0)
814 		{
815 			parse_error = TRUE;
816 			g_warning ("XML Error: No attributes to match given");
817 		}
818 		else
819 		{
820 			plugin_handles =
821 				anjuta_plugin_manager_list_query (plugin_manager,
822 				                                  groups,
823 				                                  attribs,
824 				                                  values);
825 		}
826 	}
827 	g_list_free_full (groups, (GDestroyNotify)xmlFree);
828 	g_list_free_full (attribs, (GDestroyNotify)xmlFree);
829 	g_list_free_full (values, (GDestroyNotify)xmlFree);
830 
831 
832 	return plugin_handles;
833 }
834 
835 /* Read filter */
836 static GList*
parse_filter(GList ** set_list,xmlNodePtr xml_root,AnjutaPluginManager * plugin_manager,GFile * file,GError ** error)837 parse_filter (GList **set_list, xmlNodePtr xml_root, AnjutaPluginManager *plugin_manager, GFile *file, GError **error)
838 {
839 	xmlNodePtr xml_node;
840 	GError *parse_error = NULL;
841 	GList *handles_list = NULL;
842 
843 	for (xml_node = xml_root->xmlChildrenNode; xml_node; xml_node = xml_node->next)
844 	{
845 		GList *plugin_handles = NULL;
846 		GList *set;
847 
848 		if (!xml_node->name ||
849 			!xmlStrEqual (xml_node->name, (const xmlChar*)"filter"))
850 		{
851 			continue;
852 		}
853 
854 		/* Get all plugins fullfiling filter requirements */
855 		plugin_handles = parse_requires (xml_node, plugin_manager, file, &parse_error);
856 		if (parse_error != NULL)
857 		{
858 			g_propagate_error (error, parse_error);
859 			break;
860 		}
861 		handles_list = g_list_prepend (handles_list, plugin_handles);
862 
863 		set = parse_set (xml_node, file, &parse_error);
864 		if (parse_error != NULL)
865 		{
866 			g_propagate_error (error, parse_error);
867 			break;
868 		}
869 		*set_list = g_list_prepend (*set_list, set);
870 	}
871 
872 	return handles_list;
873 }
874 
875 /* Read plugins, return a list of plugin list */
876 static GList *
parse_plugins(GList ** set_list,xmlNodePtr xml_root,AnjutaPluginManager * plugin_manager,GFile * file,GError ** error)877 parse_plugins (GList **set_list, xmlNodePtr xml_root, AnjutaPluginManager *plugin_manager, GFile *file, GError **error)
878 {
879 	xmlNodePtr xml_node;
880 	GError *parse_error = NULL;
881 	GList *handles_list = NULL;
882 	GList *not_found_names = NULL;
883 	GList *not_found_urls = NULL;
884 
885 	/* Read plugin list */
886 	for (xml_node = xml_root->xmlChildrenNode; xml_node; xml_node = xml_node->next)
887 	{
888 		xmlChar *name, *url, *mandatory_text;
889 		gboolean mandatory;
890 		GList *plugin_handles = NULL;
891 
892 		if (!xml_node->name ||
893 		    !xmlStrEqual (xml_node->name, (const xmlChar*)"plugin"))
894 		{
895 			continue;
896 		}
897 
898 		name = xmlGetProp (xml_node, (const xmlChar*)"name");
899 		url = xmlGetProp (xml_node, (const xmlChar*)"url");
900 
901 		/* Ensure that both name is given */
902 		if (!name)
903 		{
904 			g_warning ("XML error: Plugin name should be present in plugin tag");
905 			set_parse_error (&parse_error, file);
906 			break;
907 		}
908 		if (!url)
909 			url = xmlCharStrdup ("http://anjuta.org/plugins/");
910 
911 		/* Check if the plugin is mandatory */
912 		mandatory_text = xmlGetProp (xml_node, (const xmlChar*)"mandatory");
913 		mandatory = mandatory_text && (xmlStrcasecmp (mandatory_text, (const xmlChar *)"yes") == 0);
914 		xmlFree(mandatory_text);
915 
916 		plugin_handles = parse_requires (xml_node, plugin_manager, file, &parse_error);
917 		if (parse_error != NULL) break;
918 		if (plugin_handles)
919 		{
920 			GList *set = parse_set (xml_node, file, &parse_error);
921 			if (parse_error != NULL) break;
922 
923 			handles_list = g_list_prepend (handles_list, plugin_handles);
924 			*set_list = g_list_prepend (*set_list, set);
925 		}
926 		else if (mandatory)
927 		{
928 			not_found_names = g_list_prepend (not_found_names, g_strdup ((const gchar *)name));
929 			not_found_urls = g_list_prepend (not_found_urls, g_strdup ((const gchar *)url));
930 		}
931 	}
932 
933 	if (parse_error != NULL)
934 	{
935 		g_propagate_error (error, parse_error);
936 		g_list_free_full (handles_list, (GDestroyNotify)g_list_free);
937 		handles_list = NULL;
938 	}
939 	else if (not_found_names)
940 	{
941 		/*
942 	 	* FIXME: Present a nice dialog box to promt the user to download
943 		* the plugin from corresponding URLs, install them and proceed.
944  		*/
945 		GList *node_name, *node_url;
946 		GString *mesg = g_string_new ("");
947 
948 		not_found_names = g_list_reverse (not_found_names);
949 		not_found_urls = g_list_reverse (not_found_urls);
950 
951 		node_name = not_found_names;
952 		node_url = not_found_urls;
953 		while (node_name)
954 		{
955 			/* <Pluginname>: Install it from <some location on the web> */
956 			g_string_append_printf (mesg, _("%s: Install it from '%s'\n"),
957 											(char *)node_name->data,
958 											(char*)node_url->data);
959 			node_name = g_list_next (node_name);
960 			node_url = g_list_next (node_url);
961 		}
962 		g_set_error (error, ANJUTA_PROFILE_ERROR,
963 					 ANJUTA_PROFILE_ERROR_PLUGIN_MISSING,
964 					 _("Failed to read '%s': Following mandatory plugins are missing"),
965 					 mesg->str);
966 		g_string_free (mesg, TRUE);
967 
968 		g_list_foreach (not_found_names, (GFunc)g_free, NULL);
969 		g_list_free (not_found_names);
970 		g_list_foreach (not_found_urls, (GFunc)g_free, NULL);
971 		g_list_free (not_found_urls);
972 
973 		g_list_free_full (handles_list, (GDestroyNotify)g_list_free);
974 		handles_list = NULL;
975 	}
976 
977 	return handles_list;
978 }
979 
980 static gboolean
anjuta_profile_read_xml(AnjutaProfile * profile,GError ** error)981 anjuta_profile_read_xml (AnjutaProfile *profile,
982                          GError **error)
983 {
984 	AnjutaProfilePriv *priv;
985 	AnjutaProfileXml *xml;
986 	xmlNodePtr xml_root;
987 	GError *parse_error = NULL;
988 	GList *disable_list;
989 	GHashTable *disable_hash;
990 	gboolean filter = FALSE;
991 
992 	/* Check if there are new XML files */
993 	priv = profile->priv;
994 	if (priv->xml == NULL) return TRUE;
995 
996 	/* Read all xml file */
997 	for (xml = priv->xml; xml != NULL; xml = xml->next)
998 	{
999 		xml->doc = load_profile_from_xml (xml->file, &parse_error);
1000 		if (parse_error != NULL)
1001 		{
1002 			g_propagate_error (error, parse_error);
1003 
1004 			return FALSE;
1005 		}
1006 	}
1007 
1008 	/* Get all plugins to load */
1009 	for (xml = priv->xml; xml != NULL; xml = xml->next)
1010 	{
1011 		GList *handles_list;
1012 		GList *plugin_list;
1013 		GList *set_list = NULL;
1014 
1015 		/* Parse plugin in xml file */
1016 		xml_root = xmlDocGetRootElement(xml->doc);
1017 		handles_list = parse_plugins (&set_list, xml_root, priv->plugin_manager, xml->file, &parse_error);
1018 		if (parse_error != NULL) break;
1019 
1020 		anjuta_profile_configure_plugins (profile, handles_list, set_list);
1021 
1022 		plugin_list = anjuta_profile_select_plugins (profile, handles_list);
1023 		g_list_foreach (handles_list, (GFunc)g_list_free, NULL);
1024 		g_list_free (handles_list);
1025 		for (; plugin_list != NULL; plugin_list = g_list_delete_link (plugin_list, plugin_list))
1026 		{
1027 			g_hash_table_add (priv->plugins_to_load, plugin_list->data);
1028 			if (xml->exclude_from_sync) g_hash_table_add (priv->plugins_to_exclude_from_sync, plugin_list->data);
1029 		}
1030 	}
1031 
1032 	/* Get all disable plugins */
1033 	if (priv->plugins_to_disable == NULL)
1034 	{
1035 		disable_list = anjuta_plugin_manager_list_query (priv->plugin_manager, NULL, NULL, NULL);
1036 	}
1037 	else
1038 	{
1039 		disable_list = priv->plugins_to_disable;
1040 	}
1041 	disable_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
1042 	for (; disable_list != NULL; disable_list = g_list_delete_link (disable_list, disable_list))
1043 	{
1044 		g_hash_table_add (disable_hash, disable_list->data);
1045 	}
1046 	for (xml = priv->xml; xml != NULL; xml = xml->next)
1047 	{
1048 		GList *handles_list;
1049 		GList *plugin_list;
1050 		GList *set_list = NULL;
1051 
1052 		/* Parse filter in xml file */
1053 		xml_root = xmlDocGetRootElement(xml->doc);
1054 		handles_list = parse_filter (&set_list, xml_root, priv->plugin_manager, xml->file, &parse_error);
1055 		if (parse_error != NULL) break;
1056 
1057 		anjuta_profile_configure_plugins (profile, handles_list, set_list);
1058 
1059 		filter = filter || (handles_list != NULL);
1060 		for (plugin_list = g_list_first (handles_list); plugin_list != NULL; plugin_list = g_list_next (plugin_list))
1061 		{
1062 			GList *node;
1063 			for (node = g_list_first ((GList *)plugin_list->data); node != NULL; node = g_list_next (node))
1064 			{
1065 				g_hash_table_remove (disable_hash, node->data);
1066 			}
1067 		}
1068 		g_list_foreach (handles_list, (GFunc)g_list_free, NULL);
1069 		g_list_free (handles_list);
1070 	}
1071 	if (filter)
1072 	{
1073 		/* Filter some plugins */
1074 		priv->plugins_to_disable = g_hash_table_get_keys (disable_hash);
1075 		anjuta_plugin_manager_set_disable_plugins (priv->plugin_manager, priv->plugins_to_disable, TRUE);
1076 	}
1077 	else
1078 	{
1079 		/* No filter, keep all plugins */
1080 		priv->plugins_to_disable = NULL;
1081 	}
1082 	g_hash_table_destroy (disable_hash);
1083 	if (parse_error != NULL) return FALSE;
1084 
1085 
1086 	/* Remove xml object */
1087 	while (priv->xml != NULL)
1088 	{
1089 		AnjutaProfileXml *next;
1090 
1091 		next = priv->xml->next;
1092 		g_object_unref (priv->xml->file);
1093 		xmlFreeDoc(priv->xml->doc);
1094 		g_free (priv->xml);
1095 		priv->xml = next;
1096 	}
1097 
1098 	if (parse_error != NULL) g_propagate_error (error, parse_error);
1099 
1100 	return parse_error == NULL;
1101 }
1102 
1103 
1104 
1105 /* Public functions
1106  *---------------------------------------------------------------------------*/
1107 
1108 /**
1109  * anjuta_profile_add_plugins_from_xml:
1110  * @profile: a #AnjutaProfile object.
1111  * @profile_xml_file: xml file containing plugin list.
1112  * @exclude_from_sync: %TRUE if these plugins shouldn't be saved in user session.
1113  * @error: error propagation and reporting.
1114  *
1115  * Add all plugins inscribed in the xml file into the profile plugin list.
1116  *
1117  * Return value: %TRUE on success, %FALSE otherwise.
1118  */
1119 gboolean
anjuta_profile_add_plugins_from_xml(AnjutaProfile * profile,GFile * profile_xml_file,gboolean exclude_from_sync,GError ** error)1120 anjuta_profile_add_plugins_from_xml (AnjutaProfile *profile,
1121 									 GFile* profile_xml_file,
1122 									 gboolean exclude_from_sync,
1123 									 GError **error)
1124 {
1125 	AnjutaProfilePriv *priv;
1126 	AnjutaProfileXml *xml;
1127 	AnjutaProfileXml **last;
1128 
1129 	g_return_val_if_fail (ANJUTA_IS_PROFILE (profile), FALSE);
1130 
1131 	priv = profile->priv;
1132 
1133 	/* Just save the file name, the xml wil be loaded later after unloading the
1134 	 * previous profile if needed */
1135 
1136 	xml = g_new (AnjutaProfileXml, 1);
1137 	xml->file = g_object_ref (profile_xml_file);
1138 	xml->doc = NULL;
1139 	xml->exclude_from_sync = exclude_from_sync;
1140 	xml->next = NULL;
1141 	for (last = &(priv->xml); *last != NULL; last = &((*last)->next));
1142 	*last = xml;
1143 
1144 	return TRUE;
1145 }
1146 
1147 /**
1148  * anjuta_profile_to_xml :
1149  * @profile: a #AnjutaProfile object.
1150  *
1151  * Return a string in xml format containing the list of saved plugins.
1152  *
1153  * Return value: (transfer full): a newly-allocated string that must be freed with g_free().
1154  */
1155 static gchar*
anjuta_profile_to_xml(AnjutaProfile * profile)1156 anjuta_profile_to_xml (AnjutaProfile *profile)
1157 {
1158 	GList *node;
1159 	GString *str;
1160 	AnjutaProfilePriv *priv;
1161 
1162 	g_return_val_if_fail (ANJUTA_IS_PROFILE (profile), FALSE);
1163 	priv = profile->priv;
1164 
1165 	str = g_string_new ("<?xml version=\"1.0\"?>\n<anjuta>\n");
1166 	for (node = g_hash_table_get_keys (priv->plugins_to_load); node != NULL; node = g_list_delete_link (node, node))
1167 	{
1168 		AnjutaPluginHandle *handle;
1169 		AnjutaPluginDescription *desc;
1170 		gboolean user_activatable = TRUE;
1171 		gchar *name = NULL, *plugin_id = NULL;
1172 
1173 		if (g_hash_table_lookup (priv->plugins_to_exclude_from_sync, node->data))
1174 		{
1175 			/* Do not save plugin in the exclude list */
1176 			continue;
1177 		}
1178 		handle = (AnjutaPluginHandle *)node->data;
1179 		desc = anjuta_plugin_handle_get_description(handle);
1180 		if (anjuta_plugin_description_get_boolean (desc, "Anjuta Plugin",
1181 												  "UserActivatable", &user_activatable)
1182 				&& !user_activatable)
1183 		{
1184 			/* Do not save plugins that are auto activated */
1185 			continue;
1186 		}
1187 
1188 		/* Do not use the _locale_ version because it's not in UI */
1189 		anjuta_plugin_description_get_string (desc, "Anjuta Plugin",
1190 											  "Name", &name);
1191 		DEBUG_PRINT("Saving plugin: %s", name);
1192 		if (!name)
1193 			name = g_strdup ("Unknown");
1194 
1195 		if (anjuta_plugin_description_get_string (desc, "Anjuta Plugin",
1196 												  "Location", &plugin_id))
1197 		{
1198 			g_string_append (str, "    <plugin name=\"");
1199 			g_string_append (str, name);
1200 			g_string_append (str, "\" mandatory=\"no\">\n");
1201 			g_string_append (str, "        <require group=\"Anjuta Plugin\"\n");
1202 			g_string_append (str, "                 attribute=\"Location\"\n");
1203 			g_string_append (str, "                 value=\"");
1204 			g_string_append (str, plugin_id);
1205 			g_string_append (str, "\"/>\n");
1206 			g_string_append (str, "    </plugin>\n");
1207 
1208 			g_free (plugin_id);
1209 		}
1210 		g_free (name);
1211 	}
1212 	g_string_append (str, "</anjuta>\n");
1213 
1214 	return g_string_free (str, FALSE);
1215 }
1216 
1217 /**
1218  * anjuta_profile_set_sync_file:
1219  * @profile: a #AnjutaProfile object.
1220  * @sync_file: file used to save profile.
1221  *
1222  * Define the file used to save plugins list.
1223  */
1224 
1225 void
anjuta_profile_set_sync_file(AnjutaProfile * profile,GFile * sync_file)1226 anjuta_profile_set_sync_file (AnjutaProfile *profile, GFile *sync_file)
1227 {
1228 	AnjutaProfilePriv *priv;
1229 
1230 	g_return_if_fail (ANJUTA_IS_PROFILE (profile));
1231 
1232 	priv = profile->priv;
1233 
1234 	if (priv->sync_file)
1235 		g_object_unref (priv->sync_file);
1236 	priv->sync_file = sync_file;
1237 	if (priv->sync_file)
1238 		g_object_ref (priv->sync_file);
1239 }
1240 
1241 /**
1242  * anjuta_profile_sync:
1243  * @profile: a #AnjutaProfile object.
1244  * @error: error propagation and reporting.
1245  *
1246  * Save the current plugins list in the xml file set with anjuta_profile_set_sync_file().
1247  *
1248  * Return value: %TRUE on success, %FALSE otherwise.
1249  */
1250 gboolean
anjuta_profile_sync(AnjutaProfile * profile,GError ** error)1251 anjuta_profile_sync (AnjutaProfile *profile, GError **error)
1252 {
1253 	gboolean ok;
1254 	gchar *xml_buffer;
1255 	AnjutaProfilePriv *priv;
1256 	GError* file_error = NULL;
1257 
1258 	g_return_val_if_fail (ANJUTA_IS_PROFILE (profile), FALSE);
1259 	priv = profile->priv;
1260 
1261 	if (!priv->sync_file)
1262 		return FALSE;
1263 
1264 	xml_buffer = anjuta_profile_to_xml (profile);
1265 	ok = g_file_replace_contents (priv->sync_file, xml_buffer, strlen(xml_buffer),
1266 								  NULL, FALSE, G_FILE_CREATE_NONE,
1267 								  NULL, NULL, &file_error);
1268 	if (!ok && g_error_matches (file_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
1269 	{
1270 		/* Try to create parent directory */
1271 		GFile* parent = g_file_get_parent (priv->sync_file);
1272 		if (g_file_make_directory (parent, NULL, NULL))
1273 		{
1274 			g_clear_error (&file_error);
1275 			ok = g_file_replace_contents (priv->sync_file, xml_buffer, strlen(xml_buffer),
1276 										  NULL, FALSE, G_FILE_CREATE_NONE,
1277 										  NULL, NULL, &file_error);
1278 		}
1279 		g_object_unref (parent);
1280 	}
1281 	g_free (xml_buffer);
1282 	if (file_error != NULL) g_propagate_error (error, file_error);
1283 
1284 	return ok;
1285 }
1286 
1287 /**
1288  * anjuta_profile_load:
1289  * @profile: a #AnjutaProfile object.
1290  * @error: error propagation and reporting.
1291  *
1292  * Load the profile
1293  *
1294  * Return value: TRUE on success, FALSE otherwise.
1295  */
1296 gboolean
anjuta_profile_load(AnjutaProfile * profile,GError ** error)1297 anjuta_profile_load (AnjutaProfile *profile, GError **error)
1298 {
1299 	AnjutaProfilePriv *priv;
1300 	GList *active_plugins, *node;
1301 	GHashTable *active_hash;
1302 
1303 	/* Read XML file if needed */
1304 	if (!anjuta_profile_read_xml (profile, error)) return FALSE;
1305 	priv = profile->priv;
1306 
1307 	/* Deactivate plugins that are already active, but are not requested to be
1308 	 * active */
1309 	active_plugins = anjuta_plugin_manager_get_active_plugins (priv->plugin_manager);
1310 	for (node = active_plugins; node != NULL; node = g_list_next (node))
1311 	{
1312 		AnjutaPluginHandle *handle = (AnjutaPluginHandle *)node->data;
1313 
1314 		if (anjuta_plugin_handle_get_can_unload (handle) &&
1315 		    !g_hash_table_lookup (priv->plugins_to_load, handle))
1316 		{
1317 			anjuta_plugin_manager_unload_plugin_by_handle (priv->plugin_manager,
1318 			                                               handle);
1319 		}
1320 	}
1321 
1322 	/* Prepare active plugins hash */
1323 	active_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
1324 	for (node = active_plugins; node != NULL; node = g_list_next (node))
1325 	{
1326 		g_hash_table_add (active_hash, node->data);
1327 	}
1328 	g_list_free (active_plugins);
1329 
1330 	/* Prepare the plugins to activate */
1331 	active_plugins = g_hash_table_get_keys (priv->plugins_to_load);
1332 	for (node = active_plugins; node != NULL;)
1333 	{
1334 		AnjutaPluginHandle *handle = (AnjutaPluginHandle *)node->data;
1335 		GList *next = g_list_next (node);
1336 
1337 		if (g_hash_table_lookup (active_hash, handle) != NULL)
1338 		{
1339 			active_plugins = g_list_delete_link (active_plugins, node);
1340 		}
1341 		node = next;
1342 	}
1343 	g_hash_table_destroy (active_hash);
1344 
1345 	/* For system profile, marks its plugin to keep them activated */
1346 	if (strcmp (priv->name, ANJUTA_SYSTEM_PROFILE_NAME) == 0)
1347 	{
1348 		for (node = g_list_first (active_plugins); node != NULL; node = g_list_next (node))
1349 		{
1350 			anjuta_plugin_handle_set_can_unload (ANJUTA_PLUGIN_HANDLE (node->data), FALSE);
1351 		}
1352 	}
1353 
1354 	/* Now activate the plugins */
1355 	if (active_plugins != NULL)
1356 	{
1357 		anjuta_plugin_manager_activate_plugins (priv->plugin_manager,
1358 		                                        active_plugins);
1359 		g_list_free (active_plugins);
1360 	}
1361 
1362 	/* Enable profile synchronization */
1363 	g_signal_connect (priv->plugin_manager, "plugin-activated",
1364 	                  G_CALLBACK (on_plugin_activated), profile);
1365 	g_signal_connect (priv->plugin_manager, "plugin-deactivated",
1366 	                  G_CALLBACK (on_plugin_deactivated), profile);
1367 
1368 	g_signal_emit_by_name (profile, "scoped");
1369 
1370 
1371 	return TRUE;
1372 }
1373 
1374 /**
1375  * anjuta_profile_unload:
1376  * @profile: a #AnjutaProfile object.
1377  * @error: error propagation and reporting.
1378  *
1379  * Unload the profile
1380  *
1381  * Return value: TRUE on success, FALSE otherwise.
1382  */
1383 gboolean
anjuta_profile_unload(AnjutaProfile * profile,GError ** error)1384 anjuta_profile_unload (AnjutaProfile *profile, GError **error)
1385 {
1386 	AnjutaProfilePriv *priv;
1387 
1388 	/* Disable profile synchronization while the profile is being activated */
1389 	priv = profile->priv;
1390 	g_signal_handlers_disconnect_by_func (priv->plugin_manager,
1391 	                                      G_CALLBACK (on_plugin_activated),
1392 	                                      profile);
1393 	g_signal_handlers_disconnect_by_func (priv->plugin_manager,
1394 	                                      G_CALLBACK (on_plugin_deactivated),
1395 	                                      profile);
1396 
1397 	/* Do not unload system profile */
1398 	if (strcmp (priv->name, ANJUTA_SYSTEM_PROFILE_NAME) == 0) return TRUE;
1399 
1400 	/* Remove profile configuration */
1401 	anjuta_profile_unconfigure_plugins (profile);
1402 
1403 	/* Re-enable disabled plugins */
1404 	anjuta_plugin_manager_set_disable_plugins (priv->plugin_manager, priv->plugins_to_disable, FALSE);
1405 
1406 	/* Emit pre-change for the last profile */
1407 	if (profile)
1408 	{
1409 		g_signal_emit_by_name (profile, "descoped");
1410 	}
1411 
1412 	return TRUE;
1413 }
1414