build-basic-autotools:bgo#688293 - build-basic-autotools: Fix invalid read
[anjuta.git] / libanjuta / anjuta-profile.c
blobd0092d0e6eba6a7f333d3f9b915e1042a850ea6e
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.
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
23 * @short_description: Profile is a collection of plugins
24 * @see_also: #AnjutaProfileManager, #AnjutaPlugin
25 * @stability: Unstable
26 * @include: libanjuta/anjuta-profile.h
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.
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 every profile and
41 * contains mandatory plugins for Anjuta. These plugins cannot be
42 * unloaded.
43 * </para>
44 * </listitem>
45 * </varlistentry>
46 * <varlistentry>
47 * <term>$project_dir/$project_name.anjuta</term>
48 * <listitem>
49 * <para>
50 * This contains the project plugins. It lists mandatory plugins for the
51 * project. This file is version controlled and distributed with the source
52 * code. Every user working on the project uses the same one. If there
53 * is no project loaded, no project plugins are loaded.
54 * </para>
55 * </listitem>
56 * </varlistentry>
57 * <varlistentry>
58 * <term>$project_dir/.anjuta/default.profile</term>
59 * <listitem>
60 * <para>
61 * This contains the user plugins. This is the only list of plugins
62 * which is updated when the user add or remove one plugin.
63 * If there is no project loaded, the user home directory is used
64 * instead of the project directory but this list is used only in this case.
65 * There is no global user plugins list.
66 * </para>
67 * </listitem>
68 * </varlistentry>
69 * </variablelist>
72 #include <glib/gi18n.h>
73 #include <glib.h>
74 #include <string.h>
75 #include <libxml/parser.h>
76 #include <libxml/tree.h>
78 #include "anjuta-profile.h"
79 #include "anjuta-marshal.h"
80 #include "anjuta-debug.h"
82 enum
84 PROP_0,
85 PROP_PLUGIN_MANAGER,
86 PROP_PROFILE_NAME,
87 PROP_PROFILE_PLUGINS,
88 PROP_SYNC_FILE,
91 enum
93 PLUGIN_ADDED,
94 PLUGIN_REMOVED,
95 CHANGED,
96 LAST_SIGNAL
99 struct _AnjutaProfilePriv
101 gchar *name;
102 AnjutaPluginManager *plugin_manager;
103 GList *plugins;
104 GHashTable *plugins_hash;
105 GHashTable *plugins_to_exclude_from_sync;
106 GFile *sync_file;
109 static GObjectClass* parent_class = NULL;
110 static guint profile_signals[LAST_SIGNAL] = { 0 };
112 GQuark
113 anjuta_profile_error_quark (void)
115 static GQuark quark = 0;
117 if (quark == 0) {
118 quark = g_quark_from_static_string ("anjuta-profile-quark");
121 return quark;
124 static void
125 anjuta_profile_init (AnjutaProfile *object)
127 object->priv = g_new0 (AnjutaProfilePriv, 1);
128 object->priv->plugins_hash = g_hash_table_new (g_direct_hash,
129 g_direct_equal);
130 object->priv->plugins_to_exclude_from_sync =
131 g_hash_table_new (g_direct_hash, g_direct_equal);
134 static void
135 anjuta_profile_finalize (GObject *object)
137 AnjutaProfilePriv *priv = ANJUTA_PROFILE (object)->priv;
138 g_free (priv->name);
139 if (priv->plugins)
140 g_list_free (priv->plugins);
141 g_hash_table_destroy (priv->plugins_hash);
142 g_hash_table_destroy (priv->plugins_to_exclude_from_sync);
144 G_OBJECT_CLASS (parent_class)->finalize (object);
147 static void
148 anjuta_profile_set_property (GObject *object, guint prop_id,
149 const GValue *value, GParamSpec *pspec)
151 AnjutaProfilePriv *priv = ANJUTA_PROFILE (object)->priv;
153 g_return_if_fail (ANJUTA_IS_PROFILE (object));
155 switch (prop_id)
157 case PROP_PLUGIN_MANAGER:
158 priv->plugin_manager = g_value_get_object (value);
159 break;
160 case PROP_PROFILE_NAME:
161 g_return_if_fail (g_value_get_string (value) != NULL);
162 g_free (priv->name);
163 priv->name = g_strdup (g_value_get_string (value));
164 break;
165 case PROP_PROFILE_PLUGINS:
166 if (priv->plugins)
167 g_list_free (priv->plugins);
168 if (g_value_get_pointer (value))
169 priv->plugins = g_list_copy (g_value_get_pointer (value));
170 else
171 priv->plugins = NULL;
172 break;
173 case PROP_SYNC_FILE:
174 if (priv->sync_file)
175 g_object_unref (priv->sync_file);
176 priv->sync_file = g_value_dup_object (value);
177 break;
178 default:
179 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
180 break;
184 static void
185 anjuta_profile_get_property (GObject *object, guint prop_id,
186 GValue *value, GParamSpec *pspec)
188 AnjutaProfilePriv *priv = ANJUTA_PROFILE (object)->priv;
190 g_return_if_fail (ANJUTA_IS_PROFILE (object));
192 switch (prop_id)
194 case PROP_PLUGIN_MANAGER:
195 g_value_set_object (value, priv->plugin_manager);
196 break;
197 case PROP_PROFILE_NAME:
198 g_value_set_string (value, priv->name);
199 break;
200 case PROP_PROFILE_PLUGINS:
201 g_value_set_pointer (value, priv->plugins);
202 break;
203 case PROP_SYNC_FILE:
204 g_value_set_object (value, priv->sync_file);
205 break;
206 default:
207 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
208 break;
212 static void
213 anjuta_profile_plugin_added (AnjutaProfile *self,
214 AnjutaPluginDescription *plugin)
218 static void
219 anjuta_profile_plugin_removed (AnjutaProfile *self,
220 AnjutaPluginDescription *plugin)
224 static void
225 anjuta_profile_changed (AnjutaProfile *self, GList *plugins)
227 GError *error = NULL;
228 anjuta_profile_sync (self, &error);
229 if (error)
231 g_warning ("Failed to synchronize plugins profile '%s': %s",
232 self->priv->name, error->message);
233 g_error_free (error);
237 static void
238 anjuta_profile_class_init (AnjutaProfileClass *klass)
240 GObjectClass* object_class = G_OBJECT_CLASS (klass);
241 parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
243 object_class->finalize = anjuta_profile_finalize;
244 object_class->set_property = anjuta_profile_set_property;
245 object_class->get_property = anjuta_profile_get_property;
247 klass->plugin_added = anjuta_profile_plugin_added;
248 klass->plugin_removed = anjuta_profile_plugin_removed;
249 klass->changed = anjuta_profile_changed;
251 g_object_class_install_property (object_class,
252 PROP_PLUGIN_MANAGER,
253 g_param_spec_object ("plugin-manager",
254 _("Plugin Manager"),
255 _("The plugin manager to use for resolving plugins"),
256 ANJUTA_TYPE_PLUGIN_MANAGER,
257 G_PARAM_READABLE |
258 G_PARAM_WRITABLE |
259 G_PARAM_CONSTRUCT));
260 g_object_class_install_property (object_class,
261 PROP_PROFILE_NAME,
262 g_param_spec_string ("profile-name",
263 _("Profile Name"),
264 _("Name of the plugin profile"),
265 NULL,
266 G_PARAM_READABLE |
267 G_PARAM_WRITABLE |
268 G_PARAM_CONSTRUCT));
269 g_object_class_install_property (object_class,
270 PROP_PROFILE_PLUGINS,
271 g_param_spec_pointer ("plugins",
272 _("Profile Plugins"),
273 _("List of plugins for this profile"),
274 G_PARAM_READABLE |
275 G_PARAM_WRITABLE |
276 G_PARAM_CONSTRUCT));
277 g_object_class_install_property (object_class,
278 PROP_SYNC_FILE,
279 g_param_spec_object ("sync-file",
280 _("Synchronization file"),
281 _("File to syncronize the profile XML"),
282 G_TYPE_FILE,
283 G_PARAM_READABLE |
284 G_PARAM_WRITABLE |
285 G_PARAM_CONSTRUCT));
288 * AnjutaProfile::plugin-added:
289 * @profile: a #AnjutaProfile object.
290 * @plugin: the new plugin as a #AnjutaPluginDescription.
292 * Emitted when a plugin is added in the list.
294 profile_signals[PLUGIN_ADDED] =
295 g_signal_new ("plugin-added",
296 G_OBJECT_CLASS_TYPE (klass),
297 G_SIGNAL_RUN_FIRST,
298 G_STRUCT_OFFSET (AnjutaProfileClass, plugin_added),
299 NULL, NULL,
300 anjuta_cclosure_marshal_VOID__POINTER,
301 G_TYPE_NONE, 1,
302 G_TYPE_POINTER);
305 * AnjutaProfile::plugin-removed:
306 * @profile: a #AnjutaProfile object.
307 * @plugin: the removed plugin as a #AnjutaPluginDescription.
309 * Emitted when a plugin is removed from the list.
311 profile_signals[PLUGIN_REMOVED] =
312 g_signal_new ("plugin-removed",
313 G_OBJECT_CLASS_TYPE (klass),
314 G_SIGNAL_RUN_FIRST,
315 G_STRUCT_OFFSET (AnjutaProfileClass, plugin_removed),
316 NULL, NULL,
317 anjuta_cclosure_marshal_VOID__POINTER,
318 G_TYPE_NONE, 1,
319 G_TYPE_POINTER);
322 * AnjutaProfile::changed:
323 * @profile: a #AnjutaProfile object.
324 * @plugin_list: the new plugins list.
326 * Emitted when a plugin is added or removed from the list.
328 profile_signals[CHANGED] =
329 g_signal_new ("changed",
330 G_OBJECT_CLASS_TYPE (klass),
331 G_SIGNAL_RUN_FIRST,
332 G_STRUCT_OFFSET (AnjutaProfileClass, changed),
333 NULL, NULL,
334 anjuta_cclosure_marshal_VOID__POINTER,
335 G_TYPE_NONE, 1,
336 G_TYPE_POINTER);
339 GType
340 anjuta_profile_get_type (void)
342 static GType our_type = 0;
344 if(our_type == 0)
346 static const GTypeInfo our_info =
348 sizeof (AnjutaProfileClass), /* class_size */
349 (GBaseInitFunc) NULL, /* base_init */
350 (GBaseFinalizeFunc) NULL, /* base_finalize */
351 (GClassInitFunc) anjuta_profile_class_init, /* class_init */
352 (GClassFinalizeFunc) NULL, /* class_finalize */
353 NULL /* class_data */,
354 sizeof (AnjutaProfile), /* instance_size */
355 0, /* n_preallocs */
356 (GInstanceInitFunc) anjuta_profile_init, /* instance_init */
357 NULL /* value_table */
360 our_type = g_type_register_static (G_TYPE_OBJECT, "AnjutaProfile",
361 &our_info, 0);
364 return our_type;
368 * anjuta_profile_new:
369 * @name: the new profile name.
370 * @plugin_manager: the #AnjutaPluginManager used by this profile.
372 * Create a new profile.
374 * Return value: the new #AnjutaProfile object.
376 AnjutaProfile*
377 anjuta_profile_new (const gchar* name, AnjutaPluginManager *plugin_manager)
379 GObject *profile;
380 profile = g_object_new (ANJUTA_TYPE_PROFILE, "profile-name", name,
381 "plugin-manager", plugin_manager, NULL);
382 return ANJUTA_PROFILE (profile);
386 * anjuta_profile_get_name:
387 * @profile: a #AnjutaProfile object.
389 * Get the profile name.
391 * Return value: the profile name.
393 const gchar*
394 anjuta_profile_get_name (AnjutaProfile *profile)
396 AnjutaProfilePriv *priv;
397 g_return_val_if_fail (ANJUTA_IS_PROFILE (profile), NULL);
398 priv = ANJUTA_PROFILE (profile)->priv;
399 return priv->name;
403 * anjuta_profile_add_plugin:
404 * @profile: a #AnjutaProfile object.
405 * @plugin: a #AnjutaPluginDescription.
407 * Add one plugin into the profile plugin list.
409 void
410 anjuta_profile_add_plugin (AnjutaProfile *profile,
411 AnjutaPluginDescription *plugin)
413 AnjutaProfilePriv *priv;
414 g_return_if_fail (ANJUTA_IS_PROFILE (profile));
415 g_return_if_fail (plugin != NULL);
416 priv = ANJUTA_PROFILE (profile)->priv;
417 if (priv->plugins == NULL || g_list_find (priv->plugins, plugin) == NULL)
419 priv->plugins = g_list_prepend (priv->plugins, plugin);
420 g_signal_emit_by_name (profile, "plugin-added", plugin);
421 g_signal_emit_by_name (profile, "changed", priv->plugins);
426 * anjuta_profile_remove_plugin:
427 * @profile: a #AnjutaProfile object.
428 * @plugin: a #AnjutaPluginDescription.
430 * Remove one plugin from the profile plugin list.
432 void
433 anjuta_profile_remove_plugin (AnjutaProfile *profile,
434 AnjutaPluginDescription *plugin)
436 AnjutaProfilePriv *priv;
437 g_return_if_fail (ANJUTA_IS_PROFILE (profile));
438 g_return_if_fail (plugin != NULL);
439 priv = ANJUTA_PROFILE (profile)->priv;
440 if (priv->plugins && g_list_find (priv->plugins, plugin) != NULL)
442 priv->plugins = g_list_remove (priv->plugins, plugin);
443 g_signal_emit_by_name (profile, "plugin-removed", plugin);
444 g_signal_emit_by_name (profile, "changed", priv->plugins);
449 * anjuta_profile_has_plugin:
450 * @profile: a #AnjutaProfile object
451 * @plugin: a #AnjutaPluginDescription
453 * Check if a plugin is included in the profile plugin list.
455 * Return value: TRUE if the plugin is included in the list.
457 gboolean
458 anjuta_profile_has_plugin (AnjutaProfile *profile,
459 AnjutaPluginDescription *plugin)
461 AnjutaProfilePriv *priv;
462 g_return_val_if_fail (ANJUTA_IS_PROFILE (profile), FALSE);
463 g_return_val_if_fail (plugin != NULL, FALSE);
464 priv = ANJUTA_PROFILE (profile)->priv;
465 return (priv->plugins != NULL &&
466 g_list_find (priv->plugins, plugin) != NULL);
470 * anjuta_profile_get_plugins:
471 * @profile: a #AnjutaProfile object.
473 * Get the profile current plugins list.
475 * Return value: the plugins list.
477 GList*
478 anjuta_profile_get_plugins (AnjutaProfile *profile)
480 AnjutaProfilePriv *priv;
481 g_return_val_if_fail (ANJUTA_IS_PROFILE (profile), FALSE);
482 priv = ANJUTA_PROFILE (profile)->priv;
483 return priv->plugins;
486 static GList*
487 anjuta_profile_select_plugins (AnjutaProfile *profile,
488 GList *descs_list)
490 GList *selected_plugins = NULL;
491 GList *node = descs_list;
492 AnjutaProfilePriv *priv;
494 priv = profile->priv;
496 while (node)
498 GList *descs = node->data;
499 if (g_list_length (descs) == 1)
501 selected_plugins = g_list_prepend (selected_plugins, descs->data);
503 else
505 AnjutaPluginDescription* d;
506 d = anjuta_plugin_manager_select (priv->plugin_manager,
507 _("Select a plugin"),
508 _("Please select a plugin from the list"),
509 descs);
510 if (d)
511 selected_plugins = g_list_prepend (selected_plugins, d);
513 node = g_list_next (node);
515 return g_list_reverse (selected_plugins);
518 static GList *
519 anjuta_profile_read_plugins_from_xml (AnjutaProfile *profile,
520 GFile *file,
521 GError **error)
523 gchar *read_buf;
524 gsize size;
525 xmlDocPtr xml_doc;
526 GList *descs_list = NULL;
527 GList *not_found_names = NULL;
528 GList *not_found_urls = NULL;
529 gboolean parse_error;
531 /* Read xml file */
532 if (!g_file_load_contents (file, NULL, &read_buf, &size, NULL, error))
534 return NULL;
537 /* Parse xml file */
538 parse_error = TRUE;
539 xml_doc = xmlParseMemory (read_buf, size);
540 g_free (read_buf);
541 if (xml_doc != NULL)
543 xmlNodePtr xml_root;
545 xml_root = xmlDocGetRootElement(xml_doc);
546 if (xml_root &&
547 (xml_root->name) &&
548 xmlStrEqual(xml_root->name, (const xmlChar *)"anjuta"))
550 xmlNodePtr xml_node;
552 parse_error = FALSE;
553 for (xml_node = xml_root->xmlChildrenNode; xml_node; xml_node = xml_node->next)
555 GList *groups = NULL;
556 GList *attribs = NULL;
557 GList *values = NULL;
558 xmlChar *name, *url, *mandatory_text;
559 xmlNodePtr xml_require_node;
560 gboolean mandatory;
562 if (!xml_node->name ||
563 !xmlStrEqual (xml_node->name, (const xmlChar*)"plugin"))
565 continue;
568 name = xmlGetProp (xml_node, (const xmlChar*)"name");
569 url = xmlGetProp (xml_node, (const xmlChar*)"url");
571 /* Ensure that both name is given */
572 if (!name)
574 g_warning ("XML error: Plugin name should be present in plugin tag");
575 parse_error = TRUE;
576 break;
578 if (!url)
579 url = xmlCharStrdup ("http://anjuta.org/plugins/");
581 /* Check if the plugin is mandatory */
582 mandatory_text = xmlGetProp (xml_node, (const xmlChar*)"mandatory");
583 mandatory = mandatory_text && (xmlStrcasecmp (mandatory_text, (const xmlChar *)"yes") == 0);
584 xmlFree(mandatory_text);
586 /* For all plugin attribute conditions */
587 for (xml_require_node = xml_node->xmlChildrenNode;
588 xml_require_node;
589 xml_require_node = xml_require_node->next)
591 xmlChar *group;
592 xmlChar *attrib;
593 xmlChar *value;
595 if (!xml_require_node->name ||
596 !xmlStrEqual (xml_require_node->name,
597 (const xmlChar*)"require"))
599 continue;
601 group = xmlGetProp (xml_require_node,
602 (const xmlChar *)"group");
603 attrib = xmlGetProp(xml_require_node,
604 (const xmlChar *)"attribute");
605 value = xmlGetProp(xml_require_node,
606 (const xmlChar *)"value");
608 if (group && attrib && value)
610 groups = g_list_prepend (groups, group);
611 attribs = g_list_prepend (attribs, attrib);
612 values = g_list_prepend (values, value);
614 else
616 if (group) xmlFree (group);
617 if (attrib) xmlFree (attrib);
618 if (value) xmlFree (value);
619 parse_error = TRUE;
620 g_warning ("XML parse error: group, attribute and value should be defined in require");
621 break;
625 if (!parse_error)
627 if (g_list_length (groups) == 0)
629 parse_error = TRUE;
630 g_warning ("XML Error: No attributes to match given");
632 else
634 GList *plugin_descs;
636 plugin_descs =
637 anjuta_plugin_manager_list_query (profile->priv->plugin_manager,
638 groups,
639 attribs,
640 values);
641 if (plugin_descs)
643 descs_list = g_list_prepend (descs_list, plugin_descs);
645 else if (mandatory)
647 not_found_names = g_list_prepend (not_found_names, g_strdup ((const gchar *)name));
648 not_found_urls = g_list_prepend (not_found_urls, g_strdup ((const gchar *)url));
652 g_list_foreach (groups, (GFunc)xmlFree, NULL);
653 g_list_foreach (attribs, (GFunc)xmlFree, NULL);
654 g_list_foreach (values, (GFunc)xmlFree, NULL);
655 g_list_free (groups);
656 g_list_free (attribs);
657 g_list_free (values);
658 xmlFree (name);
659 xmlFree (url);
662 xmlFreeDoc(xml_doc);
665 if (parse_error)
667 /* Error during parsing */
668 gchar *uri = g_file_get_uri (file);
670 g_set_error (error, ANJUTA_PROFILE_ERROR,
671 ANJUTA_PROFILE_ERROR_URI_READ_FAILED,
672 _("Failed to read '%s': XML parse error. "
673 "Invalid or corrupted Anjuta plugins profile."),
674 uri);
675 g_free (uri);
677 g_list_foreach (descs_list, (GFunc)g_list_free, NULL);
678 g_list_free (descs_list);
679 descs_list = NULL;
681 else if (not_found_names)
684 * FIXME: Present a nice dialog box to promt the user to download
685 * the plugin from corresponding URLs, install them and proceed.
687 GList *node_name, *node_url;
688 GString *mesg = g_string_new ("");
689 gchar *uri = g_file_get_uri (file);
691 not_found_names = g_list_reverse (not_found_names);
692 not_found_urls = g_list_reverse (not_found_urls);
694 node_name = not_found_names;
695 node_url = not_found_urls;
696 while (node_name)
698 /* <Pluginname>: Install it from <some location on the web> */
699 g_string_append_printf (mesg, _("%s: Install it from '%s'\n"),
700 (char *)node_name->data,
701 (char*)node_url->data);
702 node_name = g_list_next (node_name);
703 node_url = g_list_next (node_url);
705 g_set_error (error, ANJUTA_PROFILE_ERROR,
706 ANJUTA_PROFILE_ERROR_URI_READ_FAILED,
707 _("Failed to read '%s': Following mandatory plugins are missing:\n%s"),
708 uri, mesg->str);
709 g_free (uri);
710 g_string_free (mesg, TRUE);
712 g_list_foreach (descs_list, (GFunc)g_list_free, NULL);
713 g_list_free (descs_list);
714 descs_list = NULL;
716 g_list_foreach (not_found_names, (GFunc)g_free, NULL);
717 g_list_free (not_found_names);
718 g_list_foreach (not_found_urls, (GFunc)g_free, NULL);
719 g_list_free (not_found_urls);
721 return descs_list;
725 * anjuta_profile_add_plugins_from_xml:
726 * @profile: a #AnjutaProfile object.
727 * @profile_xml_file: xml file containing plugin list.
728 * @exclude_from_sync: TRUE if these plugins shouldn't be saved in user session.
729 * @error: error propagation and reporting.
731 * Add all plugins inscribed in the xml file into the profile plugin list.
733 * Return value: TRUE on success, FALSE otherwise.
735 gboolean
736 anjuta_profile_add_plugins_from_xml (AnjutaProfile *profile,
737 GFile* profile_xml_file,
738 gboolean exclude_from_sync,
739 GError **error)
741 AnjutaProfilePriv *priv;
742 GList *descs_list = NULL;
744 g_return_val_if_fail (ANJUTA_IS_PROFILE (profile), FALSE);
746 priv = profile->priv;
747 descs_list = anjuta_profile_read_plugins_from_xml (profile, profile_xml_file, error);
749 if (descs_list)
751 GList *selected_plugins = NULL;
752 GList *node;
754 /* Now everything okay. Select the plugins */
755 descs_list = g_list_reverse (descs_list);
756 selected_plugins =
757 anjuta_profile_select_plugins (profile, descs_list);
758 g_list_foreach (descs_list, (GFunc)g_list_free, NULL);
759 g_list_free (descs_list);
761 node = selected_plugins;
762 while (node)
764 if (exclude_from_sync)
766 g_hash_table_insert (priv->plugins_to_exclude_from_sync,
767 node->data, node->data);
770 /* Insure no duplicate plugins are added */
771 if (g_hash_table_lookup (priv->plugins_hash, node->data) == NULL)
773 priv->plugins = g_list_append (priv->plugins, node->data);
774 g_hash_table_insert (priv->plugins_hash,
775 node->data, node->data);
777 node = g_list_next (node);
779 g_list_free (selected_plugins);
782 return descs_list != NULL;
786 * anjuta_profile_to_xml :
787 * @profile: a #AnjutaProfile object.
789 * Return a string in xml format containing the list of saved plugins.
791 * Return value: a newly-allocated string that must be freed with g_free().
793 static gchar*
794 anjuta_profile_to_xml (AnjutaProfile *profile)
796 GList *node;
797 GString *str;
798 AnjutaProfilePriv *priv;
800 g_return_val_if_fail (ANJUTA_IS_PROFILE (profile), FALSE);
801 priv = profile->priv;
803 str = g_string_new ("<?xml version=\"1.0\"?>\n<anjuta>\n");
804 for (node = priv->plugins; node != NULL; node = g_list_next (node))
806 AnjutaPluginDescription *desc;
807 gboolean user_activatable = TRUE;
808 gchar *name = NULL, *plugin_id = NULL;
810 if (g_hash_table_lookup (priv->plugins_to_exclude_from_sync, node->data))
812 /* Do not save plugin in the exclude list */
813 continue;
816 desc = (AnjutaPluginDescription *)node->data;
817 if (anjuta_plugin_description_get_boolean (desc, "Anjuta Plugin",
818 "UserActivatable", &user_activatable)
819 && !user_activatable)
821 /* Do not save plugins that are auto activated */
822 continue;
825 /* Do not use the _locale_ version because it's not in UI */
826 anjuta_plugin_description_get_string (desc, "Anjuta Plugin",
827 "Name", &name);
828 DEBUG_PRINT("Saving plugin: %s", name);
829 if (!name)
830 name = g_strdup ("Unknown");
832 if (anjuta_plugin_description_get_string (desc, "Anjuta Plugin",
833 "Location", &plugin_id))
835 g_string_append (str, " <plugin name=\"");
836 g_string_append (str, name);
837 g_string_append (str, "\" mandatory=\"no\">\n");
838 g_string_append (str, " <require group=\"Anjuta Plugin\"\n");
839 g_string_append (str, " attribute=\"Location\"\n");
840 g_string_append (str, " value=\"");
841 g_string_append (str, plugin_id);
842 g_string_append (str, "\"/>\n");
843 g_string_append (str, " </plugin>\n");
845 g_free (plugin_id);
847 g_free (name);
849 g_string_append (str, "</anjuta>\n");
851 return g_string_free (str, FALSE);
855 * anjuta_profile_set_sync_file:
856 * @profile: a #AnjutaProfile object.
857 * @sync_file: file used to save profile.
859 * Define the file used to save plugins list.
862 void
863 anjuta_profile_set_sync_file (AnjutaProfile *profile, GFile *sync_file)
865 AnjutaProfilePriv *priv;
867 g_return_if_fail (ANJUTA_IS_PROFILE (profile));
869 priv = profile->priv;
871 if (priv->sync_file)
872 g_object_unref (priv->sync_file);
873 priv->sync_file = sync_file;
874 if (priv->sync_file);
875 g_object_ref (priv->sync_file);
879 * anjuta_profile_sync:
880 * @profile: a #AnjutaProfile object.
881 * @error: error propagation and reporting.
883 * Save the current plugins list in the xml file set with anjuta_profile_set_sync_file().
885 * Return value: TRUE on success, FALSE otherwise.
887 gboolean
888 anjuta_profile_sync (AnjutaProfile *profile, GError **error)
890 gboolean ok;
891 gchar *xml_buffer;
892 AnjutaProfilePriv *priv;
893 GError* file_error = NULL;
895 g_return_val_if_fail (ANJUTA_IS_PROFILE (profile), FALSE);
896 priv = profile->priv;
898 if (!priv->sync_file)
899 return FALSE;
901 xml_buffer = anjuta_profile_to_xml (profile);
902 ok = g_file_replace_contents (priv->sync_file, xml_buffer, strlen(xml_buffer),
903 NULL, FALSE, G_FILE_CREATE_NONE,
904 NULL, NULL, &file_error);
905 if (!ok && g_error_matches (file_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
907 /* Try to create parent directory */
908 GFile* parent = g_file_get_parent (priv->sync_file);
909 if (g_file_make_directory (parent, NULL, NULL))
911 g_clear_error (&file_error);
912 ok = g_file_replace_contents (priv->sync_file, xml_buffer, strlen(xml_buffer),
913 NULL, FALSE, G_FILE_CREATE_NONE,
914 NULL, NULL, &file_error);
916 g_object_unref (parent);
918 g_free (xml_buffer);
919 if (file_error != NULL) g_propagate_error (error, file_error);
921 return ok;