Updated Makefile.am files after make -f git.mk
[anjuta.git] / libanjuta / anjuta-profile.c
blob81e2dbab1401128baa0e696963e0cc5f207a867a
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 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>
73 #include <glib/gi18n.h>
74 #include <glib.h>
75 #include <string.h>
76 #include <libxml/parser.h>
77 #include <libxml/tree.h>
79 #include "anjuta-profile.h"
80 #include "anjuta-marshal.h"
81 #include "anjuta-debug.h"
83 enum
85 PROP_0,
86 PROP_PLUGIN_MANAGER,
87 PROP_PROFILE_NAME,
88 PROP_SYNC_FILE,
91 enum
93 PLUGIN_ADDED,
94 PLUGIN_REMOVED,
95 CHANGED,
96 DESCOPED,
97 SCOPED,
98 LAST_SIGNAL
101 typedef struct _AnjutaProfileXml AnjutaProfileXml;
103 struct _AnjutaProfileXml
105 GFile *file;
106 xmlDocPtr doc;
107 gboolean exclude_from_sync;
108 AnjutaProfileXml *next;
112 struct _AnjutaProfilePriv
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;
125 static GObjectClass* parent_class = NULL;
126 static guint profile_signals[LAST_SIGNAL] = { 0 };
128 GQuark
129 anjuta_profile_error_quark (void)
131 static GQuark quark = 0;
133 if (quark == 0) {
134 quark = g_quark_from_static_string ("anjuta-profile-quark");
137 return quark;
140 static void
141 anjuta_profile_init (AnjutaProfile *object)
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);
150 static void
151 anjuta_profile_finalize (GObject *object)
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);
161 while (priv->xml != NULL)
163 AnjutaProfileXml *next;
165 next = priv->xml->next;
166 g_object_unref (priv->xml->file);
167 g_free (priv->xml);
168 priv->xml = next;
171 G_OBJECT_CLASS (parent_class)->finalize (object);
174 static void
175 anjuta_profile_set_property (GObject *object, guint prop_id,
176 const GValue *value, GParamSpec *pspec)
178 AnjutaProfilePriv *priv = ANJUTA_PROFILE (object)->priv;
180 g_return_if_fail (ANJUTA_IS_PROFILE (object));
182 switch (prop_id)
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;
203 static void
204 anjuta_profile_get_property (GObject *object, guint prop_id,
205 GValue *value, GParamSpec *pspec)
207 AnjutaProfilePriv *priv = ANJUTA_PROFILE (object)->priv;
209 g_return_if_fail (ANJUTA_IS_PROFILE (object));
211 switch (prop_id)
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;
228 static void
229 anjuta_profile_plugin_added (AnjutaProfile *self,
230 AnjutaPluginHandle *plugin)
234 static void
235 anjuta_profile_plugin_removed (AnjutaProfile *self,
236 AnjutaPluginHandle *plugin)
240 static void
241 anjuta_profile_changed (AnjutaProfile *self)
243 GError *error = NULL;
244 anjuta_profile_sync (self, &error);
245 if (error)
247 g_warning ("Failed to synchronize plugins profile '%s': %s",
248 self->priv->name, error->message);
249 g_error_free (error);
253 static void
254 anjuta_profile_class_init (AnjutaProfileClass *klass)
256 GObjectClass* object_class = G_OBJECT_CLASS (klass);
257 parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
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;
263 klass->plugin_added = anjuta_profile_plugin_added;
264 klass->plugin_removed = anjuta_profile_plugin_removed;
265 klass->changed = anjuta_profile_changed;
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));
296 * AnjutaProfile::plugin-added:
297 * @profile: a #AnjutaProfile object.
298 * @plugin: the new plugin as a #AnjutaPluginHandle.
300 * Emitted when a plugin is added in the list.
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);
313 * AnjutaProfile::plugin-removed:
314 * @profile: a #AnjutaProfile object.
315 * @plugin: the removed plugin as a #AnjutaPluginHandle.
317 * Emitted when a plugin is removed from the list.
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);
330 * AnjutaProfile::changed:
331 * @profile: a #AnjutaProfile object.
333 * Emitted when a plugin is added or removed from the list.
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);
345 * AnjutaProfile::profile-descoped:
346 * @profile: the old unloaded #AnjutaProfile
348 * Emitted when a profile will be unloaded.
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);
361 * AnjutaProfileManager::profile-scoped:
362 * @profile_manager: a #AnjutaProfileManager object.
363 * @profile: the current loaded #AnjutaProfile.
365 * Emitted when a new profile is loaded.
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);
378 GType
379 anjuta_profile_get_type (void)
381 static GType our_type = 0;
383 if(our_type == 0)
385 static const GTypeInfo our_info =
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 */
399 our_type = g_type_register_static (G_TYPE_OBJECT, "AnjutaProfile",
400 &our_info, 0);
403 return our_type;
406 static void
407 on_plugin_activated (AnjutaPluginManager *plugin_manager,
408 AnjutaPluginHandle *plugin_handle,
409 GObject *plugin_object,
410 AnjutaProfile *profile)
412 /* Add it current profile */
413 gboolean exclude;
414 AnjutaPluginDescription *desc;
416 desc = anjuta_plugin_handle_get_description (plugin_handle);
417 if (!anjuta_plugin_description_get_boolean (desc, "Anjuta Plugin", "ExcludeFromSession", &exclude) || !exclude)
419 anjuta_profile_add_plugin (profile, plugin_handle);
423 static void
424 on_plugin_deactivated (AnjutaPluginManager *plugin_manager,
425 AnjutaPluginHandle *plugin_handle,
426 GObject *plugin_object,
427 AnjutaProfile *profile)
429 /* Remove from current profile */
430 anjuta_profile_remove_plugin (profile, plugin_handle);
434 * anjuta_profile_new:
435 * @name: the new profile name.
436 * @plugin_manager: the #AnjutaPluginManager used by this profile.
438 * Create a new profile.
440 * Return value: the new #AnjutaProfile object.
442 AnjutaProfile*
443 anjuta_profile_new (const gchar* name, AnjutaPluginManager *plugin_manager)
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);
452 * anjuta_profile_get_name:
453 * @profile: a #AnjutaProfile object.
455 * Get the profile name.
457 * Return value: the profile name.
459 const gchar*
460 anjuta_profile_get_name (AnjutaProfile *profile)
462 AnjutaProfilePriv *priv;
463 g_return_val_if_fail (ANJUTA_IS_PROFILE (profile), NULL);
464 priv = ANJUTA_PROFILE (profile)->priv;
465 return priv->name;
469 * anjuta_profile_add_plugin:
470 * @profile: a #AnjutaProfile object.
471 * @plugin: a #AnjutaPluginHandle.
473 * Add one plugin into the profile plugin list.
475 void
476 anjuta_profile_add_plugin (AnjutaProfile *profile,
477 AnjutaPluginHandle *plugin)
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)
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");
492 * anjuta_profile_remove_plugin:
493 * @profile: a #AnjutaProfile object.
494 * @plugin: a #AnjutaPluginHandle.
496 * Remove one plugin from the profile plugin list.
498 void
499 anjuta_profile_remove_plugin (AnjutaProfile *profile,
500 AnjutaPluginHandle *plugin)
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))
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");
515 * anjuta_profile_has_plugin:
516 * @profile: a #AnjutaProfile object
517 * @plugin: a #AnjutaPluginHandle
519 * Check if a plugin is included in the profile plugin list.
521 * Return value: %TRUE if the plugin is included in the list.
523 gboolean
524 anjuta_profile_has_plugin (AnjutaProfile *profile,
525 AnjutaPluginHandle *plugin)
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;
532 return g_hash_table_lookup (priv->plugins_to_load, plugin) != NULL;
535 static gboolean
536 anjuta_profile_configure_plugins (AnjutaProfile *profile,
537 GList *handles_list,
538 GList *config_list)
540 AnjutaProfilePriv *priv;
541 GList *item;
542 GList *config;
544 g_return_val_if_fail (ANJUTA_IS_PROFILE (profile), FALSE);
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))
549 GList *plugin;
550 GList *set;
552 for (plugin = g_list_first ((GList *)item->data); plugin != NULL; plugin = g_list_next (plugin))
554 AnjutaPluginHandle *handle = ANJUTA_PLUGIN_HANDLE (plugin->data);
555 AnjutaPluginDescription *desc;
557 desc = anjuta_plugin_handle_get_description (handle);
558 for (set = g_list_first ((GList *)config->data); set != NULL; set = g_list_next (set))
560 gchar *group = (gchar *)set->data;
561 gchar *key = group + strlen (group) + 1;
562 gchar *value = key + strlen (key) + 1;
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);
569 for (set = g_list_first ((GList *)config->data); set != NULL; set = g_list_delete_link (set, set))
571 priv->config_keys = g_list_prepend (priv->config_keys, set->data);
574 g_list_free (config_list);
576 return TRUE;
580 static gboolean
581 anjuta_profile_unconfigure_plugins (AnjutaProfile *profile)
583 AnjutaProfilePriv *priv;
584 GList *item;
586 g_return_val_if_fail (ANJUTA_IS_PROFILE (profile), FALSE);
588 priv = ANJUTA_PROFILE (profile)->priv;
589 for (item = g_list_first (priv->configuration); item != NULL; item = g_list_delete_link (item, item))
591 AnjutaPluginHandle *handle = ANJUTA_PLUGIN_HANDLE (item->data);
592 AnjutaPluginDescription *desc;
593 gchar *group;
594 gchar *key;
596 item = g_list_delete_link (item, item);
597 group = (gchar *)(item->data);
598 key = group + strlen (group) + 1;
600 desc = anjuta_plugin_handle_get_description (handle);
601 anjuta_plugin_description_remove (desc, group, key);
603 priv->configuration = NULL;
604 g_list_free_full (priv->config_keys, (GDestroyNotify)g_free);
605 priv->config_keys = NULL;
607 return TRUE;
610 static GList*
611 anjuta_profile_select_plugins (AnjutaProfile *profile,
612 GList *handles_list)
614 GList *selected_plugins = NULL;
615 GList *node = handles_list;
616 AnjutaProfilePriv *priv;
618 priv = profile->priv;
620 while (node)
622 GList *descs = node->data;
623 if (g_list_length (descs) == 1)
625 selected_plugins = g_list_prepend (selected_plugins, descs->data);
627 else
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);
637 node = g_list_next (node);
639 return g_list_reverse (selected_plugins);
643 /* Read profile from XML
644 *---------------------------------------------------------------------------*/
646 /* Error during parsing */
647 static gboolean
648 set_parse_error (GError **error, GFile*file)
650 gchar *uri = g_file_get_uri (file);
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);
660 return FALSE;
663 static xmlDocPtr
664 load_profile_from_xml (GFile *file, GError **error)
666 gchar *read_buf;
667 gsize size;
668 xmlDocPtr xml_doc;
670 /* Read xml file */
671 if (!g_file_load_contents (file, NULL, &read_buf, &size, NULL, error))
673 return NULL;
676 /* Parse xml file */
677 xml_doc = xmlParseMemory (read_buf, size);
678 g_free (read_buf);
679 if (xml_doc != NULL)
681 xmlNodePtr xml_root;
683 xml_root = xmlDocGetRootElement(xml_doc);
684 if (xml_root ||
685 (xml_root->name) ||
686 xmlStrEqual(xml_root->name, (const xmlChar *)"anjuta"))
688 return xml_doc;
690 xmlFreeDoc(xml_doc);
692 set_parse_error (error, file);
694 return NULL;
697 static GList *
698 parse_set (xmlNodePtr xml_node, GFile *file, GError **error)
700 GList *config = NULL;
701 gboolean parse_error = FALSE;
702 xmlNodePtr xml_require_node;
704 /* Read attribute conditions */
705 for (xml_require_node = xml_node->xmlChildrenNode;
706 xml_require_node;
707 xml_require_node = xml_require_node->next)
709 xmlChar *group;
710 xmlChar *attrib;
711 xmlChar *value;
713 if (!xml_require_node->name ||
714 !xmlStrEqual (xml_require_node->name,
715 (const xmlChar*)"set"))
717 continue;
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");
726 if (group && attrib && value)
728 GString *str;
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);
736 config = g_list_prepend (config, g_string_free (str, FALSE));
738 else
740 parse_error = TRUE;
741 g_warning ("XML parse error: group, attribute and value should be defined in set");
743 if (group) xmlFree (group);
744 if (attrib) xmlFree (attrib);
745 if (value) xmlFree (value);
746 if (parse_error) break;
749 if (parse_error)
751 set_parse_error (error, file);
754 return g_list_reverse (config);
758 static GList *
759 parse_requires (xmlNodePtr xml_node, AnjutaPluginManager *plugin_manager, GFile *file, GError **error)
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;
768 /* Read attribute conditions */
769 for (xml_require_node = xml_node->xmlChildrenNode;
770 xml_require_node;
771 xml_require_node = xml_require_node->next)
773 xmlChar *group;
774 xmlChar *attrib;
775 xmlChar *value;
777 if (!xml_require_node->name ||
778 !xmlStrEqual (xml_require_node->name,
779 (const xmlChar*)"require"))
781 continue;
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");
790 if (group && attrib && value)
792 groups = g_list_prepend (groups, group);
793 attribs = g_list_prepend (attribs, attrib);
794 values = g_list_prepend (values, value);
796 else
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;
807 if (parse_error)
809 set_parse_error (error, file);
811 else
813 if (g_list_length (groups) == 0)
815 parse_error = TRUE;
816 g_warning ("XML Error: No attributes to match given");
818 else
820 plugin_handles =
821 anjuta_plugin_manager_list_query (plugin_manager,
822 groups,
823 attribs,
824 values);
827 g_list_free_full (groups, (GDestroyNotify)xmlFree);
828 g_list_free_full (attribs, (GDestroyNotify)xmlFree);
829 g_list_free_full (values, (GDestroyNotify)xmlFree);
832 return plugin_handles;
835 /* Read filter */
836 static GList*
837 parse_filter (GList **set_list, xmlNodePtr xml_root, AnjutaPluginManager *plugin_manager, GFile *file, GError **error)
839 xmlNodePtr xml_node;
840 GError *parse_error = NULL;
841 GList *handles_list = NULL;
843 for (xml_node = xml_root->xmlChildrenNode; xml_node; xml_node = xml_node->next)
845 GList *plugin_handles = NULL;
846 GList *set;
848 if (!xml_node->name ||
849 !xmlStrEqual (xml_node->name, (const xmlChar*)"filter"))
851 continue;
854 /* Get all plugins fullfiling filter requirements */
855 plugin_handles = parse_requires (xml_node, plugin_manager, file, &parse_error);
856 if (parse_error != NULL)
858 g_propagate_error (error, parse_error);
859 break;
861 handles_list = g_list_prepend (handles_list, plugin_handles);
863 set = parse_set (xml_node, file, &parse_error);
864 if (parse_error != NULL)
866 g_propagate_error (error, parse_error);
867 break;
869 *set_list = g_list_prepend (*set_list, set);
872 return handles_list;
875 /* Read plugins, return a list of plugin list */
876 static GList *
877 parse_plugins (GList **set_list, xmlNodePtr xml_root, AnjutaPluginManager *plugin_manager, GFile *file, GError **error)
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;
885 /* Read plugin list */
886 for (xml_node = xml_root->xmlChildrenNode; xml_node; xml_node = xml_node->next)
888 xmlChar *name, *url, *mandatory_text;
889 gboolean mandatory;
890 GList *plugin_handles = NULL;
892 if (!xml_node->name ||
893 !xmlStrEqual (xml_node->name, (const xmlChar*)"plugin"))
895 continue;
898 name = xmlGetProp (xml_node, (const xmlChar*)"name");
899 url = xmlGetProp (xml_node, (const xmlChar*)"url");
901 /* Ensure that both name is given */
902 if (!name)
904 g_warning ("XML error: Plugin name should be present in plugin tag");
905 set_parse_error (&parse_error, file);
906 break;
908 if (!url)
909 url = xmlCharStrdup ("http://anjuta.org/plugins/");
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);
916 plugin_handles = parse_requires (xml_node, plugin_manager, file, &parse_error);
917 if (parse_error != NULL) break;
918 if (plugin_handles)
920 GList *set = parse_set (xml_node, file, &parse_error);
921 if (parse_error != NULL) break;
923 handles_list = g_list_prepend (handles_list, plugin_handles);
924 *set_list = g_list_prepend (*set_list, set);
926 else if (mandatory)
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));
933 if (parse_error != NULL)
935 g_propagate_error (error, parse_error);
936 g_list_free_full (handles_list, (GDestroyNotify)g_list_free);
937 handles_list = NULL;
939 else if (not_found_names)
942 * FIXME: Present a nice dialog box to promt the user to download
943 * the plugin from corresponding URLs, install them and proceed.
945 GList *node_name, *node_url;
946 GString *mesg = g_string_new ("");
948 not_found_names = g_list_reverse (not_found_names);
949 not_found_urls = g_list_reverse (not_found_urls);
951 node_name = not_found_names;
952 node_url = not_found_urls;
953 while (node_name)
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);
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);
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);
973 g_list_free_full (handles_list, (GDestroyNotify)g_list_free);
974 handles_list = NULL;
977 return handles_list;
980 static gboolean
981 anjuta_profile_read_xml (AnjutaProfile *profile,
982 GError **error)
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;
992 /* Check if there are new XML files */
993 priv = profile->priv;
994 if (priv->xml == NULL) return TRUE;
996 /* Read all xml file */
997 for (xml = priv->xml; xml != NULL; xml = xml->next)
999 xml->doc = load_profile_from_xml (xml->file, &parse_error);
1000 if (parse_error != NULL)
1002 g_propagate_error (error, parse_error);
1004 return FALSE;
1008 /* Get all plugins to load */
1009 for (xml = priv->xml; xml != NULL; xml = xml->next)
1011 GList *handles_list;
1012 GList *plugin_list;
1013 GList *set_list = NULL;
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;
1020 anjuta_profile_configure_plugins (profile, handles_list, set_list);
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))
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);
1032 /* Get all disable plugins */
1033 if (priv->plugins_to_disable == NULL)
1035 disable_list = anjuta_plugin_manager_list_query (priv->plugin_manager, NULL, NULL, NULL);
1037 else
1039 disable_list = priv->plugins_to_disable;
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))
1044 g_hash_table_add (disable_hash, disable_list->data);
1046 for (xml = priv->xml; xml != NULL; xml = xml->next)
1048 GList *handles_list;
1049 GList *plugin_list;
1050 GList *set_list = NULL;
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;
1057 anjuta_profile_configure_plugins (profile, handles_list, set_list);
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))
1062 GList *node;
1063 for (node = g_list_first ((GList *)plugin_list->data); node != NULL; node = g_list_next (node))
1065 g_hash_table_remove (disable_hash, node->data);
1068 g_list_foreach (handles_list, (GFunc)g_list_free, NULL);
1069 g_list_free (handles_list);
1071 if (filter)
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);
1077 else
1079 /* No filter, keep all plugins */
1080 priv->plugins_to_disable = NULL;
1082 g_hash_table_destroy (disable_hash);
1083 if (parse_error != NULL) return FALSE;
1086 /* Remove xml object */
1087 while (priv->xml != NULL)
1089 AnjutaProfileXml *next;
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;
1098 if (parse_error != NULL) g_propagate_error (error, parse_error);
1100 return parse_error == NULL;
1105 /* Public functions
1106 *---------------------------------------------------------------------------*/
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.
1115 * Add all plugins inscribed in the xml file into the profile plugin list.
1117 * Return value: %TRUE on success, %FALSE otherwise.
1119 gboolean
1120 anjuta_profile_add_plugins_from_xml (AnjutaProfile *profile,
1121 GFile* profile_xml_file,
1122 gboolean exclude_from_sync,
1123 GError **error)
1125 AnjutaProfilePriv *priv;
1126 AnjutaProfileXml *xml;
1127 AnjutaProfileXml **last;
1129 g_return_val_if_fail (ANJUTA_IS_PROFILE (profile), FALSE);
1131 priv = profile->priv;
1133 /* Just save the file name, the xml wil be loaded later after unloading the
1134 * previous profile if needed */
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;
1144 return TRUE;
1148 * anjuta_profile_to_xml :
1149 * @profile: a #AnjutaProfile object.
1151 * Return a string in xml format containing the list of saved plugins.
1153 * Return value: (transfer full): a newly-allocated string that must be freed with g_free().
1155 static gchar*
1156 anjuta_profile_to_xml (AnjutaProfile *profile)
1158 GList *node;
1159 GString *str;
1160 AnjutaProfilePriv *priv;
1162 g_return_val_if_fail (ANJUTA_IS_PROFILE (profile), FALSE);
1163 priv = profile->priv;
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))
1168 AnjutaPluginHandle *handle;
1169 AnjutaPluginDescription *desc;
1170 gboolean user_activatable = TRUE;
1171 gchar *name = NULL, *plugin_id = NULL;
1173 if (g_hash_table_lookup (priv->plugins_to_exclude_from_sync, node->data))
1175 /* Do not save plugin in the exclude list */
1176 continue;
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)
1184 /* Do not save plugins that are auto activated */
1185 continue;
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");
1195 if (anjuta_plugin_description_get_string (desc, "Anjuta Plugin",
1196 "Location", &plugin_id))
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");
1208 g_free (plugin_id);
1210 g_free (name);
1212 g_string_append (str, "</anjuta>\n");
1214 return g_string_free (str, FALSE);
1218 * anjuta_profile_set_sync_file:
1219 * @profile: a #AnjutaProfile object.
1220 * @sync_file: file used to save profile.
1222 * Define the file used to save plugins list.
1225 void
1226 anjuta_profile_set_sync_file (AnjutaProfile *profile, GFile *sync_file)
1228 AnjutaProfilePriv *priv;
1230 g_return_if_fail (ANJUTA_IS_PROFILE (profile));
1232 priv = profile->priv;
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);
1242 * anjuta_profile_sync:
1243 * @profile: a #AnjutaProfile object.
1244 * @error: error propagation and reporting.
1246 * Save the current plugins list in the xml file set with anjuta_profile_set_sync_file().
1248 * Return value: %TRUE on success, %FALSE otherwise.
1250 gboolean
1251 anjuta_profile_sync (AnjutaProfile *profile, GError **error)
1253 gboolean ok;
1254 gchar *xml_buffer;
1255 AnjutaProfilePriv *priv;
1256 GError* file_error = NULL;
1258 g_return_val_if_fail (ANJUTA_IS_PROFILE (profile), FALSE);
1259 priv = profile->priv;
1261 if (!priv->sync_file)
1262 return FALSE;
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))
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))
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);
1279 g_object_unref (parent);
1281 g_free (xml_buffer);
1282 if (file_error != NULL) g_propagate_error (error, file_error);
1284 return ok;
1288 * anjuta_profile_load:
1289 * @profile: a #AnjutaProfile object.
1290 * @error: error propagation and reporting.
1292 * Load the profile
1294 * Return value: TRUE on success, FALSE otherwise.
1296 gboolean
1297 anjuta_profile_load (AnjutaProfile *profile, GError **error)
1299 AnjutaProfilePriv *priv;
1300 GList *active_plugins, *node;
1301 GHashTable *active_hash;
1303 /* Read XML file if needed */
1304 if (!anjuta_profile_read_xml (profile, error)) return FALSE;
1305 priv = profile->priv;
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))
1312 AnjutaPluginHandle *handle = (AnjutaPluginHandle *)node->data;
1314 if (anjuta_plugin_handle_get_can_unload (handle) &&
1315 !g_hash_table_lookup (priv->plugins_to_load, handle))
1317 anjuta_plugin_manager_unload_plugin_by_handle (priv->plugin_manager,
1318 handle);
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))
1326 g_hash_table_add (active_hash, node->data);
1328 g_list_free (active_plugins);
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;)
1334 AnjutaPluginHandle *handle = (AnjutaPluginHandle *)node->data;
1335 GList *next = g_list_next (node);
1337 if (g_hash_table_lookup (active_hash, handle) != NULL)
1339 active_plugins = g_list_delete_link (active_plugins, node);
1341 node = next;
1343 g_hash_table_destroy (active_hash);
1345 /* For system profile, marks its plugin to keep them activated */
1346 if (strcmp (priv->name, ANJUTA_SYSTEM_PROFILE_NAME) == 0)
1348 for (node = g_list_first (active_plugins); node != NULL; node = g_list_next (node))
1350 anjuta_plugin_handle_set_can_unload (ANJUTA_PLUGIN_HANDLE (node->data), FALSE);
1354 /* Now activate the plugins */
1355 if (active_plugins != NULL)
1357 anjuta_plugin_manager_activate_plugins (priv->plugin_manager,
1358 active_plugins);
1359 g_list_free (active_plugins);
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);
1368 g_signal_emit_by_name (profile, "scoped");
1371 return TRUE;
1375 * anjuta_profile_unload:
1376 * @profile: a #AnjutaProfile object.
1377 * @error: error propagation and reporting.
1379 * Unload the profile
1381 * Return value: TRUE on success, FALSE otherwise.
1383 gboolean
1384 anjuta_profile_unload (AnjutaProfile *profile, GError **error)
1386 AnjutaProfilePriv *priv;
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);
1397 /* Do not unload system profile */
1398 if (strcmp (priv->name, ANJUTA_SYSTEM_PROFILE_NAME) == 0) return TRUE;
1400 /* Remove profile configuration */
1401 anjuta_profile_unconfigure_plugins (profile);
1403 /* Re-enable disabled plugins */
1404 anjuta_plugin_manager_set_disable_plugins (priv->plugin_manager, priv->plugins_to_disable, FALSE);
1406 /* Emit pre-change for the last profile */
1407 if (profile)
1409 g_signal_emit_by_name (profile, "descoped");
1412 return TRUE;