1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
4 * Copyright (C) Naba Kumar <naba@gnome.org>
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
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
37 * <term>$prefix/share/anjuta/profiles/default.profile</term>
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.
48 * <term>$project_dir/$project_name.anjuta</term>
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.
59 * <term>$project_dir/.anjuta/default.profile</term>
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.
73 #include <glib/gi18n.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"
101 typedef struct _AnjutaProfileXml AnjutaProfileXml
;
103 struct _AnjutaProfileXml
107 gboolean exclude_from_sync
;
108 AnjutaProfileXml
*next
;
112 struct _AnjutaProfilePriv
115 AnjutaPluginManager
*plugin_manager
;
116 GHashTable
*plugins_to_load
;
117 GHashTable
*plugins_to_exclude_from_sync
;
118 GList
*plugins_to_disable
;
119 GList
*configuration
;
122 AnjutaProfileXml
*xml
;
125 static GObjectClass
* parent_class
= NULL
;
126 static guint profile_signals
[LAST_SIGNAL
] = { 0 };
129 anjuta_profile_error_quark (void)
131 static GQuark quark
= 0;
134 quark
= g_quark_from_static_string ("anjuta-profile-quark");
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
,
146 object
->priv
->plugins_to_exclude_from_sync
= g_hash_table_new (g_direct_hash
,
151 anjuta_profile_finalize (GObject
*object
)
153 AnjutaProfilePriv
*priv
= ANJUTA_PROFILE (object
)->priv
;
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
);
171 G_OBJECT_CLASS (parent_class
)->finalize (object
);
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
));
184 case PROP_PLUGIN_MANAGER
:
185 priv
->plugin_manager
= g_value_get_object (value
);
187 case PROP_PROFILE_NAME
:
188 g_return_if_fail (g_value_get_string (value
) != NULL
);
190 priv
->name
= g_strdup (g_value_get_string (value
));
194 g_object_unref (priv
->sync_file
);
195 priv
->sync_file
= g_value_dup_object (value
);
198 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
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
));
213 case PROP_PLUGIN_MANAGER
:
214 g_value_set_object (value
, priv
->plugin_manager
);
216 case PROP_PROFILE_NAME
:
217 g_value_set_string (value
, priv
->name
);
220 g_value_set_object (value
, priv
->sync_file
);
223 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
229 anjuta_profile_plugin_added (AnjutaProfile
*self
,
230 AnjutaPluginHandle
*plugin
)
235 anjuta_profile_plugin_removed (AnjutaProfile
*self
,
236 AnjutaPluginHandle
*plugin
)
241 anjuta_profile_changed (AnjutaProfile
*self
)
243 GError
*error
= NULL
;
244 anjuta_profile_sync (self
, &error
);
247 g_warning ("Failed to synchronize plugins profile '%s': %s",
248 self
->priv
->name
, error
->message
);
249 g_error_free (error
);
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
,
269 g_param_spec_object ("plugin-manager",
271 _("The plugin manager to use for resolving plugins"),
272 ANJUTA_TYPE_PLUGIN_MANAGER
,
276 g_object_class_install_property (object_class
,
278 g_param_spec_string ("profile-name",
280 _("Name of the plugin profile"),
285 g_object_class_install_property (object_class
,
287 g_param_spec_object ("sync-file",
288 _("Synchronization file"),
289 _("File to syncronize the profile XML"),
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
),
306 G_STRUCT_OFFSET (AnjutaProfileClass
, plugin_added
),
308 anjuta_cclosure_marshal_VOID__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
),
323 G_STRUCT_OFFSET (AnjutaProfileClass
, plugin_removed
),
325 anjuta_cclosure_marshal_VOID__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
),
339 G_STRUCT_OFFSET (AnjutaProfileClass
, changed
),
341 anjuta_cclosure_marshal_VOID__POINTER
,
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
),
354 G_STRUCT_OFFSET (AnjutaProfileClass
,
357 g_cclosure_marshal_VOID__VOID
,
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
),
371 G_STRUCT_OFFSET (AnjutaProfileClass
,
374 g_cclosure_marshal_VOID__VOID
,
379 anjuta_profile_get_type (void)
381 static GType 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 */
395 (GInstanceInitFunc
) anjuta_profile_init
, /* instance_init */
396 NULL
/* value_table */
399 our_type
= g_type_register_static (G_TYPE_OBJECT
, "AnjutaProfile",
407 on_plugin_activated (AnjutaPluginManager
*plugin_manager
,
408 AnjutaPluginHandle
*plugin_handle
,
409 GObject
*plugin_object
,
410 AnjutaProfile
*profile
)
412 /* Add it current profile */
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
);
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.
443 anjuta_profile_new (const gchar
* name
, AnjutaPluginManager
*plugin_manager
)
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.
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
;
469 * anjuta_profile_add_plugin:
470 * @profile: a #AnjutaProfile object.
471 * @plugin: a #AnjutaPluginHandle.
473 * Add one plugin into the profile plugin list.
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.
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.
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
;
536 anjuta_profile_configure_plugins (AnjutaProfile
*profile
,
540 AnjutaProfilePriv
*priv
;
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
))
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
);
581 anjuta_profile_unconfigure_plugins (AnjutaProfile
*profile
)
583 AnjutaProfilePriv
*priv
;
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
;
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
;
611 anjuta_profile_select_plugins (AnjutaProfile
*profile
,
614 GList
*selected_plugins
= NULL
;
615 GList
*node
= handles_list
;
616 AnjutaProfilePriv
*priv
;
618 priv
= profile
->priv
;
622 GList
*descs
= node
->data
;
623 if (g_list_length (descs
) == 1)
625 selected_plugins
= g_list_prepend (selected_plugins
, descs
->data
);
629 AnjutaPluginHandle
* handle
;
630 handle
= anjuta_plugin_manager_select (priv
->plugin_manager
,
631 _("Select a plugin"),
632 _("Please select a plugin from the list"),
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 */
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."),
664 load_profile_from_xml (GFile
*file
, GError
**error
)
671 if (!g_file_load_contents (file
, NULL
, &read_buf
, &size
, NULL
, error
))
677 xml_doc
= xmlParseMemory (read_buf
, size
);
683 xml_root
= xmlDocGetRootElement(xml_doc
);
686 xmlStrEqual(xml_root
->name
, (const xmlChar
*)"anjuta"))
692 set_parse_error (error
, file
);
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
;
707 xml_require_node
= xml_require_node
->next
)
713 if (!xml_require_node
->name
||
714 !xmlStrEqual (xml_require_node
->name
,
715 (const xmlChar
*)"set"))
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
)
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
));
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;
751 set_parse_error (error
, file
);
754 return g_list_reverse (config
);
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
;
771 xml_require_node
= xml_require_node
->next
)
777 if (!xml_require_node
->name
||
778 !xmlStrEqual (xml_require_node
->name
,
779 (const xmlChar
*)"require"))
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
);
798 if (group
) xmlFree (group
);
799 if (attrib
) xmlFree (attrib
);
800 if (value
) xmlFree (value
);
802 g_warning ("XML parse error: group, attribute and value should be defined in require");
809 set_parse_error (error
, file
);
813 if (g_list_length (groups
) == 0)
816 g_warning ("XML Error: No attributes to match given");
821 anjuta_plugin_manager_list_query (plugin_manager
,
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
;
837 parse_filter (GList
**set_list
, xmlNodePtr xml_root
, AnjutaPluginManager
*plugin_manager
, GFile
*file
, GError
**error
)
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
;
848 if (!xml_node
->name
||
849 !xmlStrEqual (xml_node
->name
, (const xmlChar
*)"filter"))
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
);
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
);
869 *set_list
= g_list_prepend (*set_list
, set
);
875 /* Read plugins, return a list of plugin list */
877 parse_plugins (GList
**set_list
, xmlNodePtr xml_root
, AnjutaPluginManager
*plugin_manager
, GFile
*file
, GError
**error
)
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
;
890 GList
*plugin_handles
= NULL
;
892 if (!xml_node
->name
||
893 !xmlStrEqual (xml_node
->name
, (const xmlChar
*)"plugin"))
898 name
= xmlGetProp (xml_node
, (const xmlChar
*)"name");
899 url
= xmlGetProp (xml_node
, (const xmlChar
*)"url");
901 /* Ensure that both name is given */
904 g_warning ("XML error: Plugin name should be present in plugin tag");
905 set_parse_error (&parse_error
, file
);
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;
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
);
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
);
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
;
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"),
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
);
981 anjuta_profile_read_xml (AnjutaProfile
*profile
,
984 AnjutaProfilePriv
*priv
;
985 AnjutaProfileXml
*xml
;
987 GError
*parse_error
= NULL
;
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
);
1008 /* Get all plugins to load */
1009 for (xml
= priv
->xml
; xml
!= NULL
; xml
= xml
->next
)
1011 GList
*handles_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
);
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
;
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
))
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
);
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
);
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
);
1098 if (parse_error
!= NULL
) g_propagate_error (error
, parse_error
);
1100 return parse_error
== NULL
;
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.
1120 anjuta_profile_add_plugins_from_xml (AnjutaProfile
*profile
,
1121 GFile
* profile_xml_file
,
1122 gboolean exclude_from_sync
,
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
);
1139 xml
->exclude_from_sync
= exclude_from_sync
;
1141 for (last
= &(priv
->xml
); *last
!= NULL
; last
= &((*last
)->next
));
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().
1156 anjuta_profile_to_xml (AnjutaProfile
*profile
)
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 */
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 */
1188 /* Do not use the _locale_ version because it's not in UI */
1189 anjuta_plugin_description_get_string (desc
, "Anjuta Plugin",
1191 DEBUG_PRINT("Saving plugin: %s", 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");
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.
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.
1251 anjuta_profile_sync (AnjutaProfile
*profile
, GError
**error
)
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
)
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
);
1288 * anjuta_profile_load:
1289 * @profile: a #AnjutaProfile object.
1290 * @error: error propagation and reporting.
1294 * Return value: TRUE on success, FALSE otherwise.
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
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
,
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
);
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
,
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");
1375 * anjuta_profile_load:
1376 * @profile: a #AnjutaProfile object.
1377 * @error: error propagation and reporting.
1379 * Unload the profile
1381 * Return value: TRUE on success, FALSE otherwise.
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
),
1393 g_signal_handlers_disconnect_by_func (priv
->plugin_manager
,
1394 G_CALLBACK (on_plugin_deactivated
),
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 */
1409 g_signal_emit_by_name (profile
, "descoped");