git: Implement the Tags pane
[anjuta.git] / libanjuta / anjuta-plugin-manager.c
blob4f59fa553e47417aec43fcb220c71e3961ade0a6
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 * anjuta-plugin-manager.c
4 * Copyright (C) Naba Kumar <naba@gnome.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 /**
22 * SECTION:anjuta-plugin-manager
23 * @short_description: Plugins management and activation
24 * @see_also: #AnjutaPlugin, #AnjutaProfileManager
25 * @stability: Unstable
26 * @include: libanjuta/anjuta-plugin-manager.h
30 #include <sys/types.h>
31 #include <dirent.h>
32 #include <string.h>
34 #include <libanjuta/anjuta-plugin-manager.h>
35 #include <libanjuta/anjuta-marshal.h>
36 #include <libanjuta/anjuta-debug.h>
37 #include <libanjuta/anjuta-plugin-handle.h>
38 #include <libanjuta/anjuta-plugin.h>
39 #include <libanjuta/anjuta-c-plugin-factory.h>
40 #include <libanjuta/interfaces/ianjuta-plugin-factory.h>
41 #include <libanjuta/interfaces/ianjuta-preferences.h>
44 enum
46 PROP_0,
48 PROP_SHELL,
49 PROP_STATUS,
50 PROP_PROFILES,
51 PROP_AVAILABLE_PLUGINS,
52 PROP_ACTIVATED_PLUGINS
55 enum
57 PROFILE_PUSHED,
58 PROFILE_POPPED,
59 PLUGINS_TO_LOAD,
60 PLUGINS_TO_UNLOAD,
61 PLUGIN_ACTIVATED,
62 PLUGIN_DEACTIVATED,
64 LAST_SIGNAL
67 struct _AnjutaPluginManagerPriv
69 GObject *shell;
70 AnjutaStatus *status;
71 GList *plugin_dirs;
72 GList *available_plugins;
74 /* Indexes => plugin handles */
75 GHashTable *plugins_by_interfaces;
76 GHashTable *plugins_by_name;
77 GHashTable *plugins_by_description;
79 /* Plugins that are currently activated */
80 GHashTable *activated_plugins;
82 /* Plugins that have been previously loaded but current deactivated */
83 GHashTable *plugins_cache;
85 /* Remember plugin selection */
86 GHashTable *remember_plugins;
89 /* Available plugins page treeview */
90 enum {
91 COL_ACTIVABLE,
92 COL_ENABLED,
93 COL_ICON,
94 COL_NAME,
95 COL_PLUGIN,
96 N_COLS
99 /* Remembered plugins page treeview */
100 enum {
101 COL_REM_ICON,
102 COL_REM_NAME,
103 COL_REM_PLUGIN_KEY,
104 N_REM_COLS
107 /* Plugin class types */
109 static AnjutaCPluginFactory *anjuta_plugin_factory = NULL;
111 static GObjectClass* parent_class = NULL;
112 static guint plugin_manager_signals[LAST_SIGNAL] = { 0 };
114 static GHashTable* plugin_set_update (AnjutaPluginManager *plugin_manager,
115 AnjutaPluginHandle* selected_plugin,
116 gboolean load);
118 static IAnjutaPluginFactory* get_plugin_factory (AnjutaPluginManager *plugin_manager,
119 const gchar *language, GError **error);
121 GQuark
122 anjuta_plugin_manager_error_quark (void)
124 static GQuark quark = 0;
126 if (quark == 0) {
127 quark = g_quark_from_static_string ("anjuta-plugin-manager-quark");
129 return quark;
132 /** Dependency Resolution **/
134 static gboolean
135 collect_cycle (AnjutaPluginManager *plugin_manager,
136 AnjutaPluginHandle *base_plugin, AnjutaPluginHandle *cur_plugin,
137 GList **cycle)
139 AnjutaPluginManagerPriv *priv;
140 GList *l;
142 priv = plugin_manager->priv;
144 for (l = anjuta_plugin_handle_get_dependency_names (cur_plugin);
145 l != NULL; l = l->next)
147 AnjutaPluginHandle *dep = g_hash_table_lookup (priv->plugins_by_name,
148 l->data);
149 if (dep)
151 if (dep == base_plugin)
153 *cycle = g_list_prepend (NULL, dep);
154 /* DEBUG_PRINT ("%s", anjuta_plugin_handle_get_name (dep)); */
155 return TRUE;
157 else
159 if (collect_cycle (plugin_manager, base_plugin, dep, cycle))
161 *cycle = g_list_prepend (*cycle, dep);
162 /* DEBUG_PRINT ("%s", anjuta_plugin_handle_get_name (dep)); */
163 return TRUE;
168 return FALSE;
171 static void
172 add_dependency (AnjutaPluginHandle *dependent, AnjutaPluginHandle *dependency)
174 g_hash_table_insert (anjuta_plugin_handle_get_dependents (dependency),
175 dependent, dependency);
176 g_hash_table_insert (anjuta_plugin_handle_get_dependencies (dependent),
177 dependency, dependent);
180 static void
181 child_dep_foreach_cb (gpointer key, gpointer value, gpointer user_data)
183 add_dependency (ANJUTA_PLUGIN_HANDLE (user_data),
184 ANJUTA_PLUGIN_HANDLE (key));
187 /* Resolves dependencies for a single module recursively. Shortcuts if
188 * the module has already been resolved. Returns a list representing
189 * any cycles found, or NULL if no cycles are found. If a cycle is found,
190 * the graph is left unresolved.
192 static GList*
193 resolve_for_module (AnjutaPluginManager *plugin_manager,
194 AnjutaPluginHandle *plugin, int pass)
196 AnjutaPluginManagerPriv *priv;
197 GList *l;
198 GList *ret = NULL;
200 priv = plugin_manager->priv;
202 if (anjuta_plugin_handle_get_checked (plugin))
204 return NULL;
207 if (anjuta_plugin_handle_get_resolve_pass (plugin) == pass)
209 GList *cycle = NULL;
210 g_warning ("cycle found: %s on pass %d",
211 anjuta_plugin_handle_get_name (plugin),
212 anjuta_plugin_handle_get_resolve_pass (plugin));
213 collect_cycle (plugin_manager, plugin, plugin, &cycle);
214 return cycle;
217 if (anjuta_plugin_handle_get_resolve_pass (plugin) != -1)
219 return NULL;
222 anjuta_plugin_handle_set_can_load (plugin, TRUE);
223 anjuta_plugin_handle_set_resolve_pass (plugin, pass);
225 for (l = anjuta_plugin_handle_get_dependency_names (plugin);
226 l != NULL; l = l->next)
228 char *dep = l->data;
229 AnjutaPluginHandle *child =
230 g_hash_table_lookup (priv->plugins_by_name, dep);
231 if (child)
233 ret = resolve_for_module (plugin_manager, child, pass);
234 if (ret)
236 break;
239 /* Add the dependency's dense dependency list
240 * to the current module's dense dependency list */
241 g_hash_table_foreach (anjuta_plugin_handle_get_dependencies (child),
242 child_dep_foreach_cb, plugin);
243 add_dependency (plugin, child);
245 /* If the child can't load due to dependency problems,
246 * the current module can't either */
247 anjuta_plugin_handle_set_can_load (plugin,
248 anjuta_plugin_handle_get_can_load (child));
249 } else {
250 g_warning ("Dependency %s not found.\n", dep);
251 anjuta_plugin_handle_set_can_load (plugin, FALSE);
252 ret = NULL;
255 anjuta_plugin_handle_set_checked (plugin, TRUE);
257 return ret;
260 /* Clean up the results of a resolving run */
261 static void
262 unresolve_dependencies (AnjutaPluginManager *plugin_manager)
264 AnjutaPluginManagerPriv *priv;
265 GList *l;
267 priv = plugin_manager->priv;
269 for (l = priv->available_plugins; l != NULL; l = l->next)
271 AnjutaPluginHandle *plugin = l->data;
272 anjuta_plugin_handle_unresolve_dependencies (plugin);
276 /* done upto here */
278 static void
279 prune_modules (AnjutaPluginManager *plugin_manager, GList *modules)
281 AnjutaPluginManagerPriv *priv;
282 GList *l;
284 priv = plugin_manager->priv;
286 for (l = modules; l != NULL; l = l->next) {
287 AnjutaPluginHandle *plugin = l->data;
289 g_hash_table_remove (priv->plugins_by_name,
290 anjuta_plugin_handle_get_id (plugin));
291 priv->available_plugins = g_list_remove (priv->available_plugins, plugin);
295 static int
296 dependency_compare (AnjutaPluginHandle *plugin_a,
297 AnjutaPluginHandle *plugin_b)
299 int a = g_hash_table_size (anjuta_plugin_handle_get_dependencies (plugin_a));
300 int b = g_hash_table_size (anjuta_plugin_handle_get_dependencies (plugin_b));
302 return a - b;
305 /* Resolves the dependencies of the priv->available_plugins list. When this
306 * function is complete, the following will be true:
308 * 1) The dependencies and dependents hash tables of the modules will
309 * be filled.
311 * 2) Cycles in the graph will be removed.
313 * 3) Modules which cannot be loaded due to failed dependencies will
314 * be marked as such.
316 * 4) priv->available_plugins will be sorted such that no module depends on a
317 * module after it.
319 * If a cycle in the graph is found, it is pruned from the tree and
320 * returned as a list stored in the cycles list.
322 static void
323 resolve_dependencies (AnjutaPluginManager *plugin_manager, GList **cycles)
325 AnjutaPluginManagerPriv *priv;
326 GList *cycle = NULL;
327 GList *l;
329 priv = plugin_manager->priv;
330 *cycles = NULL;
332 /* Try resolving dependencies. If there is a cycle, prune the
333 * cycle and try to resolve again */
336 int pass = 1;
337 cycle = NULL;
338 for (l = priv->available_plugins; l != NULL && !cycle; l = l->next) {
339 cycle = resolve_for_module (plugin_manager, l->data, pass++);
340 cycle = NULL;
342 if (cycle) {
343 *cycles = g_list_prepend (*cycles, cycle);
344 prune_modules (plugin_manager, cycle);
345 unresolve_dependencies (plugin_manager);
347 } while (cycle);
349 /* Now that there is a fully resolved dependency tree, sort
350 * priv->available_plugins to create a valid load order */
351 priv->available_plugins = g_list_sort (priv->available_plugins,
352 (GCompareFunc)dependency_compare);
355 /* Plugins loading */
357 static gboolean
358 str_has_suffix (const char *haystack, const char *needle)
360 const char *h, *n;
362 if (needle == NULL) {
363 return TRUE;
365 if (haystack == NULL) {
366 return needle[0] == '\0';
369 /* Eat one character at a time. */
370 h = haystack + strlen(haystack);
371 n = needle + strlen(needle);
372 do {
373 if (n == needle) {
374 return TRUE;
376 if (h == haystack) {
377 return FALSE;
379 } while (*--h == *--n);
380 return FALSE;
383 static void
384 load_plugin (AnjutaPluginManager *plugin_manager,
385 const gchar *plugin_desc_path)
387 AnjutaPluginManagerPriv *priv;
388 AnjutaPluginHandle *plugin_handle;
390 g_return_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager));
391 priv = plugin_manager->priv;
393 plugin_handle = anjuta_plugin_handle_new (plugin_desc_path);
394 if (plugin_handle)
396 if (g_hash_table_lookup (priv->plugins_by_name,
397 anjuta_plugin_handle_get_id (plugin_handle)))
399 g_object_unref (plugin_handle);
401 else
403 GList *node;
404 /* Available plugin */
405 priv->available_plugins = g_list_prepend (priv->available_plugins,
406 plugin_handle);
407 /* Index by id */
408 g_hash_table_insert (priv->plugins_by_name,
409 (gchar *)anjuta_plugin_handle_get_id (plugin_handle),
410 plugin_handle);
412 /* Index by description */
413 g_hash_table_insert (priv->plugins_by_description,
414 anjuta_plugin_handle_get_description (plugin_handle),
415 plugin_handle);
417 /* Index by interfaces exported by this plugin */
418 node = anjuta_plugin_handle_get_interfaces (plugin_handle);
419 while (node)
421 GList *objs;
422 gchar *iface;
423 GList *obj_node;
424 gboolean found;
426 iface = node->data;
427 objs = (GList*)g_hash_table_lookup (priv->plugins_by_interfaces, iface);
429 obj_node = objs;
430 found = FALSE;
431 while (obj_node)
433 if (obj_node->data == plugin_handle)
435 found = TRUE;
436 break;
438 obj_node = g_list_next (obj_node);
440 if (!found)
442 g_hash_table_steal (priv->plugins_by_interfaces, iface);
443 objs = g_list_prepend (objs, plugin_handle);
444 g_hash_table_insert (priv->plugins_by_interfaces, iface, objs);
446 node = g_list_next (node);
450 return;
453 static void
454 load_plugins_from_directory (AnjutaPluginManager* plugin_manager,
455 const gchar *dirname)
457 DIR *dir;
458 struct dirent *entry;
460 dir = opendir (dirname);
462 if (!dir)
464 return;
467 for (entry = readdir (dir); entry != NULL; entry = readdir (dir))
469 if (str_has_suffix (entry->d_name, ".plugin"))
471 gchar *pathname;
472 pathname = g_strdup_printf ("%s/%s", dirname, entry->d_name);
473 load_plugin (plugin_manager,pathname);
474 g_free (pathname);
477 closedir (dir);
480 /* Plugin activation and deactivation */
482 static void
483 on_plugin_activated (AnjutaPlugin *plugin_object, AnjutaPluginHandle *plugin)
485 AnjutaPluginManager *plugin_manager;
486 AnjutaPluginManagerPriv *priv;
488 /* FIXME: Pass plugin_manager directly in signal arguments */
489 plugin_manager = anjuta_shell_get_plugin_manager (plugin_object->shell, NULL);
491 g_return_if_fail(plugin_manager != NULL);
493 priv = plugin_manager->priv;
495 g_hash_table_insert (priv->activated_plugins, plugin,
496 G_OBJECT (plugin_object));
497 if (g_hash_table_lookup (priv->plugins_cache, plugin))
498 g_hash_table_remove (priv->plugins_cache, plugin);
500 g_signal_emit_by_name (plugin_manager, "plugin-activated",
501 anjuta_plugin_handle_get_description (plugin),
502 plugin_object);
505 static void
506 on_plugin_deactivated (AnjutaPlugin *plugin_object, AnjutaPluginHandle *plugin)
508 AnjutaPluginManager *plugin_manager;
509 AnjutaPluginManagerPriv *priv;
511 /* FIXME: Pass plugin_manager directly in signal arguments */
512 plugin_manager = anjuta_shell_get_plugin_manager (plugin_object->shell, NULL);
514 g_return_if_fail (plugin_manager != NULL);
516 priv = plugin_manager->priv;
518 g_hash_table_insert (priv->plugins_cache, plugin, G_OBJECT (plugin_object));
519 g_hash_table_remove (priv->activated_plugins, plugin);
521 g_signal_emit_by_name (plugin_manager, "plugin-deactivated",
522 anjuta_plugin_handle_get_description (plugin),
523 plugin_object);
526 static AnjutaPlugin*
527 activate_plugin (AnjutaPluginManager *plugin_manager,
528 AnjutaPluginHandle *handle, GError **error)
530 AnjutaPluginManagerPriv *priv;
531 IAnjutaPluginFactory* factory;
532 AnjutaPlugin *plugin;
533 const gchar *plugin_id;
534 const gchar *language;
535 gboolean resident;
537 priv = plugin_manager->priv;
539 plugin_id = anjuta_plugin_handle_get_id (handle);
541 resident = anjuta_plugin_handle_get_resident (handle);
542 language = anjuta_plugin_handle_get_language (handle);
544 factory = get_plugin_factory (plugin_manager, language, error);
545 if (factory == NULL) return NULL;
547 plugin = ianjuta_plugin_factory_new_plugin (factory, handle, ANJUTA_SHELL (priv->shell), error);
549 if (plugin == NULL)
551 return NULL;
553 g_signal_connect (plugin, "activated",
554 G_CALLBACK (on_plugin_activated), handle);
555 g_signal_connect (plugin, "deactivated",
556 G_CALLBACK (on_plugin_deactivated), handle);
558 return plugin;
561 static gboolean
562 g_hashtable_foreach_true (gpointer key, gpointer value, gpointer user_data)
564 return TRUE;
567 void
568 anjuta_plugin_manager_unload_all_plugins (AnjutaPluginManager *plugin_manager)
570 AnjutaPluginManagerPriv *priv;
572 priv = plugin_manager->priv;
573 if (g_hash_table_size (priv->activated_plugins) > 0 ||
574 g_hash_table_size (priv->plugins_cache) > 0)
576 priv->available_plugins = g_list_reverse (priv->available_plugins);
577 if (g_hash_table_size (priv->activated_plugins) > 0)
579 GList *node;
580 node = priv->available_plugins;
581 while (node)
583 AnjutaPluginHandle *selected_plugin = node->data;
584 if (g_hash_table_lookup (priv->activated_plugins, selected_plugin))
586 plugin_set_update (plugin_manager, selected_plugin, FALSE);
587 /* DEBUG_PRINT ("Unloading plugin: %s",
588 anjuta_plugin_handle_get_id (selected_plugin));
591 node = g_list_next (node);
593 g_hash_table_foreach_remove (priv->activated_plugins,
594 g_hashtable_foreach_true, NULL);
596 if (g_hash_table_size (priv->plugins_cache) > 0)
598 GList *node;
599 node = priv->available_plugins;
600 while (node)
602 GObject *plugin_obj;
603 AnjutaPluginHandle *selected_plugin = node->data;
605 plugin_obj = g_hash_table_lookup (priv->plugins_cache,
606 selected_plugin);
607 if (plugin_obj)
609 /* DEBUG_PRINT ("Destroying plugin: %s",
610 anjuta_plugin_handle_get_id (selected_plugin));
612 g_object_unref (plugin_obj);
614 node = g_list_next (node);
616 g_hash_table_foreach_remove (priv->plugins_cache,
617 g_hashtable_foreach_true, NULL);
619 priv->available_plugins = g_list_reverse (priv->available_plugins);
623 static gboolean
624 should_unload (GHashTable *activated_plugins, AnjutaPluginHandle *plugin_to_unload,
625 AnjutaPluginHandle *plugin)
627 GObject *plugin_obj = g_hash_table_lookup (activated_plugins, plugin);
629 if (!plugin_obj)
630 return FALSE;
632 if (plugin_to_unload == plugin)
633 return TRUE;
635 gboolean dependent =
636 GPOINTER_TO_INT (g_hash_table_lookup (anjuta_plugin_handle_get_dependents (plugin),
637 plugin));
638 return dependent;
641 static gboolean
642 should_load (GHashTable *activated_plugins, AnjutaPluginHandle *plugin_to_load,
643 AnjutaPluginHandle *plugin)
645 GObject *plugin_obj = g_hash_table_lookup (activated_plugins, plugin);
647 if (plugin_obj)
648 return FALSE;
650 if (plugin_to_load == plugin)
651 return anjuta_plugin_handle_get_can_load (plugin);
653 gboolean dependency =
654 GPOINTER_TO_INT (g_hash_table_lookup (anjuta_plugin_handle_get_dependencies (plugin_to_load),
655 plugin));
656 return (dependency && anjuta_plugin_handle_get_can_load (plugin));
659 static AnjutaPluginHandle *
660 plugin_for_iter (GtkListStore *store, GtkTreeIter *iter)
662 AnjutaPluginHandle *plugin;
664 gtk_tree_model_get (GTK_TREE_MODEL (store), iter, COL_PLUGIN, &plugin, -1);
665 return plugin;
668 static void
669 update_enabled (GtkTreeModel *model, GHashTable *activated_plugins)
671 GtkTreeIter iter;
673 if (gtk_tree_model_get_iter_first (model, &iter)) {
674 do {
675 AnjutaPluginHandle *plugin;
676 GObject *plugin_obj;
677 gboolean installed;
679 plugin = plugin_for_iter(GTK_LIST_STORE(model), &iter);
680 plugin_obj = g_hash_table_lookup (activated_plugins, plugin);
681 installed = (plugin_obj != NULL) ? TRUE : FALSE;
682 gtk_tree_model_get (model, &iter, COL_PLUGIN, &plugin, -1);
683 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
684 COL_ENABLED, installed, -1);
685 } while (gtk_tree_model_iter_next (model, &iter));
689 static GHashTable*
690 plugin_set_update (AnjutaPluginManager *plugin_manager,
691 AnjutaPluginHandle* selected_plugin,
692 gboolean load)
694 AnjutaPluginManagerPriv *priv;
695 GObject *plugin_obj;
696 GList *l;
698 priv = plugin_manager->priv;
699 plugin_obj = g_hash_table_lookup (priv->activated_plugins, selected_plugin);
701 if (plugin_obj && load)
703 g_warning ("Trying to install already installed plugin '%s'",
704 anjuta_plugin_handle_get_name (selected_plugin));
705 return priv->activated_plugins;
707 if (!plugin_obj && !load)
709 g_warning ("Trying to uninstall a not installed plugin '%s'",
710 anjuta_plugin_handle_get_name (selected_plugin));
711 return priv->activated_plugins;
714 if (priv->status)
715 anjuta_status_busy_push (priv->status);
717 if (!load)
719 /* reverse priv->available_plugins when unloading, so that plugins are
720 * unloaded in the right order */
721 priv->available_plugins = g_list_reverse (priv->available_plugins);
723 for (l = priv->available_plugins; l != NULL; l = l->next)
725 AnjutaPluginHandle *plugin = l->data;
726 if (should_unload (priv->activated_plugins, selected_plugin, plugin))
728 /* FIXME: Unload the class and sharedlib if possible */
729 AnjutaPlugin *anjuta_plugin = ANJUTA_PLUGIN (plugin_obj);
730 if (!anjuta_plugin_deactivate (ANJUTA_PLUGIN (anjuta_plugin)))
732 anjuta_util_dialog_info (GTK_WINDOW (priv->shell),
733 _("Plugin '%s' does not want to be deactivated"),
734 anjuta_plugin_handle_get_name (plugin));
738 priv->available_plugins = g_list_reverse (priv->available_plugins);
740 else
742 for (l = priv->available_plugins; l != NULL; l = l->next)
744 AnjutaPluginHandle *plugin = l->data;
745 if (should_load (priv->activated_plugins, selected_plugin, plugin))
747 AnjutaPlugin *plugin_obj;
748 GError *error = NULL;
749 plugin_obj = g_hash_table_lookup (priv->plugins_cache, plugin);
750 if (!plugin_obj)
752 plugin_obj = activate_plugin (plugin_manager, plugin,
753 &error);
756 if (plugin_obj)
758 anjuta_plugin_activate (ANJUTA_PLUGIN (plugin_obj));
760 else
762 if (error)
764 gchar* message = g_strdup_printf (_("Could not load %s\n"
765 "This usually means that your installation is corrupted. The "
766 "error message leading to this was:\n%s"),
767 anjuta_plugin_handle_get_name (selected_plugin),
768 error->message);
769 anjuta_util_dialog_error (GTK_WINDOW(plugin_manager->priv->shell),
770 message);
771 g_error_free (error);
772 g_free(message);
778 if (priv->status)
779 anjuta_status_busy_pop (priv->status);
780 return priv->activated_plugins;
783 static void
784 plugin_toggled (GtkCellRendererToggle *cell, char *path_str, gpointer data)
786 AnjutaPluginManager *plugin_manager;
787 AnjutaPluginManagerPriv *priv;
788 GtkListStore *store = GTK_LIST_STORE (data);
789 GtkTreeIter iter;
790 GtkTreePath *path;
791 AnjutaPluginHandle *plugin;
792 gboolean enabled;
793 GList *activated_plugins;
794 GList *node;
795 AnjutaPlugin* plugin_object;
797 path = gtk_tree_path_new_from_string (path_str);
799 plugin_manager = g_object_get_data (G_OBJECT (store), "plugin-manager");
800 priv = plugin_manager->priv;
802 gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path);
803 gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
804 COL_ENABLED, &enabled,
805 COL_PLUGIN, &plugin,
806 -1);
808 /* Activate one plugin can force the loading of other ones, instead of
809 * searching which plugins have to be activated, we just unmerge all
810 * current plugins and merge all plugins after the modification */
812 /* unmerge all plugins */
813 activated_plugins = g_hash_table_get_values (priv->activated_plugins);
814 for (node = g_list_first (activated_plugins); node != NULL; node = g_list_next (node))
816 plugin_object = (AnjutaPlugin *)node->data;
817 if (plugin_object &&
818 IANJUTA_IS_PREFERENCES(plugin_object))
820 ianjuta_preferences_unmerge (IANJUTA_PREFERENCES (plugin_object),
821 anjuta_shell_get_preferences (ANJUTA_SHELL (priv->shell), NULL),
822 NULL);
825 g_list_free (activated_plugins);
827 plugin_set_update (plugin_manager, plugin, !enabled);
829 /* Make sure that it appears in the preferences. This method
830 can only be called when the preferences dialog is active so
831 it should be save
833 activated_plugins = g_hash_table_get_values (priv->activated_plugins);
834 for (node = g_list_first (activated_plugins); node != NULL; node = g_list_next (node))
836 plugin_object = (AnjutaPlugin *)node->data;
837 if (plugin_object &&
838 IANJUTA_IS_PREFERENCES(plugin_object))
840 ianjuta_preferences_merge (IANJUTA_PREFERENCES (plugin_object),
841 anjuta_shell_get_preferences (ANJUTA_SHELL (priv->shell), NULL),
842 NULL);
845 g_list_free (activated_plugins);
847 update_enabled (GTK_TREE_MODEL (store), priv->activated_plugins);
848 gtk_tree_path_free (path);
851 #if 0
852 static void
853 selection_changed (GtkTreeSelection *selection, GtkListStore *store)
855 GtkTreeIter iter;
857 if (gtk_tree_selection_get_selected (selection, NULL,
858 &iter)) {
859 GtkTextBuffer *buffer;
861 GtkWidget *txt = g_object_get_data (G_OBJECT (store),
862 "AboutText");
864 GtkWidget *image = g_object_get_data (G_OBJECT (store),
865 "Icon");
866 AnjutaPluginHandle *plugin = plugin_for_iter (store, &iter);
868 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (txt));
869 gtk_text_buffer_set_text (buffer, plugin->about, -1);
871 if (plugin->icon_path) {
872 gtk_image_set_from_file (GTK_IMAGE (image),
873 plugin->icon_path);
874 gtk_widget_show (GTK_WIDGET (image));
875 } else {
876 gtk_widget_hide (GTK_WIDGET (image));
880 #endif
882 static GtkWidget *
883 create_plugin_tree (void)
885 GtkListStore *store;
886 GtkWidget *tree;
887 GtkCellRenderer *renderer;
888 GtkTreeViewColumn *column;
890 store = gtk_list_store_new (N_COLS,
891 G_TYPE_BOOLEAN,
892 G_TYPE_BOOLEAN,
893 GDK_TYPE_PIXBUF,
894 G_TYPE_STRING,
895 G_TYPE_POINTER);
896 tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
898 renderer = gtk_cell_renderer_toggle_new ();
899 g_signal_connect (G_OBJECT (renderer), "toggled",
900 G_CALLBACK (plugin_toggled), store);
901 column = gtk_tree_view_column_new_with_attributes (_("Load"),
902 renderer,
903 "active",
904 COL_ENABLED,
905 "activatable",
906 COL_ACTIVABLE,
907 NULL);
908 gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
909 gtk_tree_view_column_set_sizing (column,
910 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
912 column = gtk_tree_view_column_new ();
913 renderer = gtk_cell_renderer_pixbuf_new ();
914 gtk_tree_view_column_pack_start (column, renderer, FALSE);
915 gtk_tree_view_column_add_attribute (column, renderer, "pixbuf",
916 COL_ICON);
917 renderer = gtk_cell_renderer_text_new ();
918 gtk_tree_view_column_pack_start (column, renderer, FALSE);
919 gtk_tree_view_column_add_attribute (column, renderer, "markup",
920 COL_NAME);
921 gtk_tree_view_column_set_sizing (column,
922 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
923 gtk_tree_view_column_set_title (column, _("Available Plugins"));
924 gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
925 gtk_tree_view_set_expander_column (GTK_TREE_VIEW (tree), column);
927 g_object_unref (store);
928 return tree;
931 /* Sort function for plugins */
932 static gint
933 sort_plugins(gconstpointer a, gconstpointer b)
935 g_return_val_if_fail (a != NULL, 0);
936 g_return_val_if_fail (b != NULL, 0);
938 AnjutaPluginHandle* plugin_a = ANJUTA_PLUGIN_HANDLE (a);
939 AnjutaPluginHandle* plugin_b = ANJUTA_PLUGIN_HANDLE (b);
941 return strcmp (anjuta_plugin_handle_get_name (plugin_a),
942 anjuta_plugin_handle_get_name (plugin_b));
945 /* If show_all == FALSE, show only user activatable plugins
946 * If show_all == TRUE, show all plugins
948 static void
949 populate_plugin_model (AnjutaPluginManager *plugin_manager,
950 GtkListStore *store,
951 GHashTable *plugins_to_show,
952 GHashTable *activated_plugins,
953 gboolean show_all)
955 AnjutaPluginManagerPriv *priv;
956 GList *l;
958 priv = plugin_manager->priv;
959 gtk_list_store_clear (store);
961 priv->available_plugins = g_list_sort (priv->available_plugins, sort_plugins);
963 for (l = priv->available_plugins; l != NULL; l = l->next)
965 AnjutaPluginHandle *plugin = l->data;
967 /* If plugins to show is NULL, show all available plugins */
968 if (plugins_to_show == NULL ||
969 g_hash_table_lookup (plugins_to_show, plugin))
972 gboolean enable = FALSE;
973 if (g_hash_table_lookup (activated_plugins, plugin))
974 enable = TRUE;
976 if (anjuta_plugin_handle_get_name (plugin) &&
977 anjuta_plugin_handle_get_description (plugin) &&
978 (anjuta_plugin_handle_get_user_activatable (plugin) ||
979 show_all))
981 GtkTreeIter iter;
982 gchar *text;
984 text = g_markup_printf_escaped ("<span size=\"larger\" weight=\"bold\">%s</span>\n%s",
985 anjuta_plugin_handle_get_name (plugin),
986 anjuta_plugin_handle_get_about (plugin));
988 gtk_list_store_append (store, &iter);
989 gtk_list_store_set (store, &iter,
990 COL_ACTIVABLE,
991 anjuta_plugin_handle_get_user_activatable (plugin),
992 COL_ENABLED, enable,
993 COL_NAME, text,
994 COL_PLUGIN, plugin,
995 -1);
996 if (anjuta_plugin_handle_get_icon_path (plugin))
998 GdkPixbuf *icon;
999 icon = gdk_pixbuf_new_from_file_at_size (anjuta_plugin_handle_get_icon_path (plugin),
1000 32, 32, NULL);
1001 if (icon) {
1002 gtk_list_store_set (store, &iter,
1003 COL_ICON, icon, -1);
1004 g_object_unref (icon);
1007 g_free (text);
1013 static GtkWidget *
1014 create_remembered_plugins_tree (void)
1016 GtkListStore *store;
1017 GtkWidget *tree;
1018 GtkCellRenderer *renderer;
1019 GtkTreeViewColumn *column;
1021 store = gtk_list_store_new (N_REM_COLS, GDK_TYPE_PIXBUF, G_TYPE_STRING,
1022 G_TYPE_STRING);
1023 tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
1025 column = gtk_tree_view_column_new ();
1026 renderer = gtk_cell_renderer_pixbuf_new ();
1027 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1028 gtk_tree_view_column_add_attribute (column, renderer, "pixbuf",
1029 COL_REM_ICON);
1030 renderer = gtk_cell_renderer_text_new ();
1031 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1032 gtk_tree_view_column_add_attribute (column, renderer, "markup",
1033 COL_REM_NAME);
1034 gtk_tree_view_column_set_sizing (column,
1035 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1036 gtk_tree_view_column_set_title (column, _("Preferred plugins"));
1037 gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
1038 gtk_tree_view_set_expander_column (GTK_TREE_VIEW (tree), column);
1040 g_object_unref (store);
1041 return tree;
1044 static void
1045 foreach_remembered_plugin (gpointer key, gpointer value, gpointer user_data)
1047 AnjutaPluginDescription *desc = (AnjutaPluginDescription *) value;
1048 GtkListStore *store = GTK_LIST_STORE (user_data);
1049 AnjutaPluginManager *manager = g_object_get_data (G_OBJECT (store),
1050 "plugin-manager");
1051 AnjutaPluginHandle *plugin =
1052 g_hash_table_lookup (manager->priv->plugins_by_description, desc);
1053 g_return_if_fail (plugin != NULL);
1055 if (anjuta_plugin_handle_get_name (plugin) &&
1056 anjuta_plugin_handle_get_description (plugin))
1058 GtkTreeIter iter;
1059 gchar *text;
1061 text = g_markup_printf_escaped ("<span size=\"larger\" weight=\"bold\">%s</span>\n%s",
1062 anjuta_plugin_handle_get_name (plugin),
1063 anjuta_plugin_handle_get_about (plugin));
1065 gtk_list_store_append (store, &iter);
1066 gtk_list_store_set (store, &iter,
1067 COL_REM_NAME, text,
1068 COL_REM_PLUGIN_KEY, key,
1069 -1);
1070 if (anjuta_plugin_handle_get_icon_path (plugin))
1072 GdkPixbuf *icon;
1073 icon = gdk_pixbuf_new_from_file_at_size (anjuta_plugin_handle_get_icon_path (plugin),
1074 32, 32, NULL);
1075 if (icon) {
1076 gtk_list_store_set (store, &iter,
1077 COL_REM_ICON, icon, -1);
1078 g_object_unref (icon);
1081 g_free (text);
1085 static void
1086 populate_remembered_plugins_model (AnjutaPluginManager *plugin_manager,
1087 GtkListStore *store)
1089 AnjutaPluginManagerPriv *priv = plugin_manager->priv;
1090 gtk_list_store_clear (store);
1091 g_hash_table_foreach (priv->remember_plugins, foreach_remembered_plugin,
1092 store);
1095 static void
1096 on_show_all_plugins_toggled (GtkToggleButton *button, GtkListStore *store)
1098 AnjutaPluginManager *plugin_manager;
1100 plugin_manager = g_object_get_data (G_OBJECT (button), "__plugin_manager");
1102 populate_plugin_model (plugin_manager, store, NULL,
1103 plugin_manager->priv->activated_plugins,
1104 !gtk_toggle_button_get_active (button));
1107 static void
1108 on_forget_plugin_clicked (GtkWidget *button, GtkTreeView *view)
1110 GtkTreeIter iter;
1111 GtkTreeModel *model;
1112 GtkTreeSelection *selection = gtk_tree_view_get_selection (view);
1113 if (gtk_tree_selection_get_selected (selection, &model, &iter))
1115 gchar *plugin_key;
1116 AnjutaPluginManager *manager = g_object_get_data (G_OBJECT (model),
1117 "plugin-manager");
1118 gtk_tree_model_get (model, &iter, COL_REM_PLUGIN_KEY, &plugin_key, -1);
1119 g_hash_table_remove (manager->priv->remember_plugins, plugin_key);
1120 gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
1121 g_free (plugin_key);
1125 static void
1126 on_forget_plugin_sel_changed (GtkTreeSelection *selection,
1127 GtkWidget *button)
1129 GtkTreeIter iter;
1131 if (gtk_tree_selection_get_selected (selection, NULL, &iter))
1132 gtk_widget_set_sensitive (button, TRUE);
1133 else
1134 gtk_widget_set_sensitive (button, FALSE);
1137 GtkWidget *
1138 anjuta_plugin_manager_get_plugins_page (AnjutaPluginManager *plugin_manager)
1140 GtkWidget *vbox;
1141 GtkWidget *checkbutton;
1142 GtkWidget *tree;
1143 GtkWidget *scrolled;
1144 GtkListStore *store;
1146 /* Plugins page */
1147 vbox = gtk_vbox_new (FALSE, 0);
1148 gtk_container_set_border_width (GTK_CONTAINER (vbox), 10);
1150 checkbutton = gtk_check_button_new_with_label (_("Only show user activatable plugins"));
1151 gtk_container_set_border_width (GTK_CONTAINER (checkbutton), 10);
1152 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), TRUE);
1153 gtk_box_pack_start (GTK_BOX (vbox), checkbutton, FALSE, FALSE, 0);
1155 scrolled = gtk_scrolled_window_new (NULL, NULL);
1156 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
1157 GTK_SHADOW_IN);
1158 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
1159 GTK_POLICY_AUTOMATIC,
1160 GTK_POLICY_AUTOMATIC);
1161 gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 0);
1163 tree = create_plugin_tree ();
1164 store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (tree)));
1166 populate_plugin_model (plugin_manager, store, NULL,
1167 plugin_manager->priv->activated_plugins, FALSE);
1169 gtk_container_add (GTK_CONTAINER (scrolled), tree);
1170 g_object_set_data (G_OBJECT (store), "plugin-manager", plugin_manager);
1173 g_object_set_data (G_OBJECT (checkbutton), "__plugin_manager", plugin_manager);
1174 g_signal_connect (G_OBJECT (checkbutton), "toggled",
1175 G_CALLBACK (on_show_all_plugins_toggled),
1176 store);
1177 gtk_widget_show_all (vbox);
1178 return vbox;
1181 GtkWidget *
1182 anjuta_plugin_manager_get_remembered_plugins_page (AnjutaPluginManager *plugin_manager)
1184 GtkWidget *vbox;
1185 GtkWidget *tree;
1186 GtkWidget *scrolled;
1187 GtkListStore *store;
1188 GtkWidget *hbox;
1189 GtkWidget *display_label;
1190 GtkWidget *forget_button;
1191 GtkTreeSelection *selection;
1193 /* Remembered plugin */
1194 vbox = gtk_vbox_new (FALSE, 10);
1195 gtk_container_set_border_width (GTK_CONTAINER (vbox), 10);
1197 display_label = gtk_label_new (_("These are the plugins selected by you "
1198 "when Anjuta prompted to choose one of "
1199 "many suitable plugins. Removing the "
1200 "preferred plugin will let Anjuta prompt "
1201 "you again to choose different plugin."));
1202 gtk_label_set_line_wrap (GTK_LABEL (display_label), TRUE);
1203 gtk_box_pack_start (GTK_BOX (vbox), display_label, FALSE, FALSE, 0);
1205 scrolled = gtk_scrolled_window_new (NULL, NULL);
1206 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
1207 GTK_SHADOW_IN);
1208 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
1209 GTK_POLICY_AUTOMATIC,
1210 GTK_POLICY_AUTOMATIC);
1211 gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 0);
1213 tree = create_remembered_plugins_tree ();
1214 store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (tree)));
1216 gtk_container_add (GTK_CONTAINER (scrolled), tree);
1217 g_object_set_data (G_OBJECT (store), "plugin-manager", plugin_manager);
1218 populate_remembered_plugins_model (plugin_manager, store);
1220 hbox = gtk_hbox_new (FALSE, 0);
1221 gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
1222 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
1223 forget_button = gtk_button_new_with_label (_("Forget selected plugin"));
1224 gtk_widget_set_sensitive (forget_button, FALSE);
1225 gtk_box_pack_end (GTK_BOX (hbox), forget_button, FALSE, FALSE, 0);
1227 g_signal_connect (forget_button, "clicked",
1228 G_CALLBACK (on_forget_plugin_clicked),
1229 tree);
1230 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree));
1231 g_signal_connect (selection, "changed",
1232 G_CALLBACK (on_forget_plugin_sel_changed),
1233 forget_button);
1234 gtk_widget_show_all (vbox);
1235 return vbox;
1238 static GList *
1239 property_to_list (const char *value)
1241 GList *l = NULL;
1242 char **split_str;
1243 char **p;
1245 split_str = g_strsplit (value, ",", -1);
1246 for (p = split_str; *p != NULL; p++) {
1247 l = g_list_prepend (l, g_strdup (g_strstrip (*p)));
1249 g_strfreev (split_str);
1250 return l;
1253 static IAnjutaPluginFactory*
1254 get_plugin_factory (AnjutaPluginManager *plugin_manager,
1255 const gchar *language,
1256 GError **error)
1258 AnjutaPluginManagerPriv *priv;
1259 AnjutaPluginHandle *plugin;
1260 GList *loader_plugins, *node;
1261 GList *valid_plugins;
1262 GObject *obj = NULL;
1264 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), G_TYPE_INVALID);
1267 if ((language == NULL) || (g_ascii_strcasecmp (language, "C") == 0))
1269 /* Support of C plugin is built-in */
1270 return IANJUTA_PLUGIN_FACTORY (anjuta_plugin_factory);
1273 priv = plugin_manager->priv;
1274 plugin = NULL;
1276 /* Find all plugins implementing the IAnjutaPluginLoader interface. */
1277 loader_plugins = g_hash_table_lookup (priv->plugins_by_interfaces, "IAnjutaPluginLoader");
1279 /* Create a list of loader supporting this language */
1280 node = loader_plugins;
1281 valid_plugins = NULL;
1282 while (node)
1284 AnjutaPluginDescription *desc;
1285 gchar *val;
1286 GList *vals = NULL;
1287 GList *l_node;
1288 gboolean found;
1290 plugin = node->data;
1292 desc = anjuta_plugin_handle_get_description (plugin);
1293 if (anjuta_plugin_description_get_string (desc, "Plugin Loader", "SupportedLanguage", &val))
1295 if (val != NULL)
1297 vals = property_to_list (val);
1298 g_free (val);
1302 found = FALSE;
1303 l_node = vals;
1304 while (l_node)
1306 if (!found && (g_ascii_strcasecmp (l_node->data, language) == 0))
1308 found = TRUE;
1310 g_free (l_node->data);
1311 l_node = g_list_next (l_node);
1313 g_list_free (vals);
1315 if (found)
1317 valid_plugins = g_list_prepend (valid_plugins, plugin);
1320 node = g_list_next (node);
1323 /* Find the first installed plugin from the valid plugins */
1324 node = valid_plugins;
1325 while (node)
1327 plugin = node->data;
1328 obj = g_hash_table_lookup (priv->activated_plugins, plugin);
1329 if (obj) break;
1330 node = g_list_next (node);
1333 /* If no plugin is installed yet, do something */
1334 if ((obj == NULL) && valid_plugins && g_list_length (valid_plugins) == 1)
1336 /* If there is just one plugin, consider it selected */
1337 plugin = valid_plugins->data;
1339 /* Install and return it */
1340 plugin_set_update (plugin_manager, plugin, TRUE);
1341 obj = g_hash_table_lookup (priv->activated_plugins, plugin);
1343 else if ((obj == NULL) && valid_plugins)
1345 /* Prompt the user to select one of these plugins */
1347 GList *descs = NULL;
1348 node = valid_plugins;
1349 while (node)
1351 plugin = node->data;
1352 descs = g_list_prepend (descs, anjuta_plugin_handle_get_description (plugin));
1353 node = g_list_next (node);
1355 descs = g_list_reverse (descs);
1356 obj = anjuta_plugin_manager_select_and_activate (plugin_manager,
1357 _("Select a plugin"),
1358 _("Please select a plugin to activate"),
1359 descs);
1360 g_list_free (descs);
1362 g_list_free (valid_plugins);
1364 if (obj != NULL)
1366 return IANJUTA_PLUGIN_FACTORY (obj);
1369 /* No plugin implementing this interface found */
1370 g_set_error (error, ANJUTA_PLUGIN_MANAGER_ERROR,
1371 ANJUTA_PLUGIN_MANAGER_MISSING_FACTORY,
1372 _("No plugin is able to load other plugins in %s"), language);
1374 return NULL;
1377 static void
1378 on_is_active_plugins_foreach (gpointer key, gpointer data, gpointer user_data)
1380 AnjutaPluginHandle *handle = ANJUTA_PLUGIN_HANDLE (key);
1381 gchar const **search_iface = (gchar const **)user_data;
1383 if (*search_iface != NULL)
1385 GList *interfaces;
1386 GList *found;
1388 interfaces = anjuta_plugin_handle_get_interfaces (handle);
1390 for (found = g_list_first (interfaces); found != NULL; found = g_list_next (found))
1394 found = g_list_find_custom (interfaces, *search_iface, (GCompareFunc)strcmp);
1396 if (found != NULL) *search_iface = NULL;
1401 * anjuta_plugin_manager_is_active_plugin:
1402 * @plugin_manager: A #AnjutaPluginManager object
1403 * @iface_name: The interface implemented by the object to be found
1405 * Searches if a currently loaded plugins implements
1406 * the given interface.
1408 * Return value: True is the plugin is currently loaded.
1411 gboolean
1412 anjuta_plugin_manager_is_active_plugin (AnjutaPluginManager *plugin_manager,
1413 const gchar *iface_name)
1415 const gchar *search_iface = iface_name;
1417 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), FALSE);
1419 g_hash_table_foreach (plugin_manager->priv->activated_plugins,
1420 on_is_active_plugins_foreach,
1421 &search_iface);
1423 return search_iface == NULL;
1427 * anjuta_plugin_manager_get_plugin:
1428 * @plugin_manager: A #AnjutaPluginManager object
1429 * @iface_name: The interface implemented by the object to be found
1431 * Searches the currently available plugins to find the one which
1432 * implements the given interface as primary interface and returns it. If
1433 * the plugin is not yet loaded, it will be loaded and activated.
1434 * It only searches
1435 * from the pool of plugin objects loaded in this shell and can only search
1436 * by primary interface. If there are more objects implementing this primary
1437 * interface, user might be prompted to select one from them (and might give
1438 * the option to use it as default for future queries). A typical usage of this
1439 * function is:
1440 * <programlisting>
1441 * GObject *docman =
1442 * anjuta_plugin_manager_get_plugin (plugin_manager, "IAnjutaDocumentManager", error);
1443 * </programlisting>
1444 * Notice that this function takes the interface name string as string, unlike
1445 * anjuta_plugins_get_interface() which takes the type directly.
1446 * If no plugin implementing this interface can be found, returns NULL.
1448 * Return value: The plugin object (subclass of #AnjutaPlugin) which implements
1449 * the given interface or NULL. See #AnjutaPlugin for more detail on interfaces
1450 * implemented by plugins.
1452 GObject *
1453 anjuta_plugin_manager_get_plugin (AnjutaPluginManager *plugin_manager,
1454 const gchar *iface_name)
1456 AnjutaPluginManagerPriv *priv;
1457 AnjutaPluginHandle *plugin;
1458 GList *valid_plugins, *node;
1460 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
1461 g_return_val_if_fail (iface_name != NULL, NULL);
1463 priv = plugin_manager->priv;
1464 plugin = NULL;
1466 /* Find all plugins implementing this (primary) interface. */
1467 valid_plugins = g_hash_table_lookup (priv->plugins_by_interfaces, iface_name);
1469 /* Find the first installed plugin from the valid plugins */
1470 node = valid_plugins;
1471 while (node)
1473 GObject *obj;
1474 plugin = node->data;
1475 obj = g_hash_table_lookup (priv->activated_plugins, plugin);
1476 if (obj)
1477 return obj;
1478 node = g_list_next (node);
1481 /* If no plugin is installed yet, do something */
1482 if (valid_plugins && g_list_length (valid_plugins) == 1)
1484 /* If there is just one plugin, consider it selected */
1485 GObject *obj;
1486 plugin = valid_plugins->data;
1488 /* Install and return it */
1489 plugin_set_update (plugin_manager, plugin, TRUE);
1490 obj = g_hash_table_lookup (priv->activated_plugins, plugin);
1492 return obj;
1494 else if (valid_plugins)
1496 /* Prompt the user to select one of these plugins */
1497 GObject *obj;
1498 GList *descs = NULL;
1499 node = valid_plugins;
1500 while (node)
1502 plugin = node->data;
1503 descs = g_list_prepend (descs, anjuta_plugin_handle_get_description (plugin));
1504 node = g_list_next (node);
1506 descs = g_list_reverse (descs);
1507 obj = anjuta_plugin_manager_select_and_activate (plugin_manager,
1508 _("Select a plugin"),
1509 _("Please select a plugin to activate"),
1510 descs);
1511 g_list_free (descs);
1512 return obj;
1515 /* No plugin implementing this interface found */
1516 return NULL;
1519 GObject *
1520 anjuta_plugin_manager_get_plugin_by_id (AnjutaPluginManager *plugin_manager,
1521 const gchar *plugin_id)
1523 AnjutaPluginManagerPriv *priv;
1524 AnjutaPluginHandle *plugin;
1526 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
1527 g_return_val_if_fail (plugin_id != NULL, NULL);
1529 priv = plugin_manager->priv;
1530 plugin = g_hash_table_lookup (priv->plugins_by_name, plugin_id);
1531 if (plugin)
1533 GObject *obj;
1534 obj = g_hash_table_lookup (priv->activated_plugins, plugin);
1535 if (obj)
1537 return obj;
1538 } else
1540 plugin_set_update (plugin_manager, plugin, TRUE);
1541 obj = g_hash_table_lookup (priv->activated_plugins, plugin);
1542 return obj;
1545 g_warning ("No plugin found with id \"%s\".", plugin_id);
1546 return NULL;
1549 static void
1550 on_activated_plugins_foreach (gpointer key, gpointer data, gpointer user_data)
1552 AnjutaPluginHandle *plugin = ANJUTA_PLUGIN_HANDLE (key);
1553 GList **active_plugins = (GList **)user_data;
1554 *active_plugins = g_list_prepend (*active_plugins,
1555 anjuta_plugin_handle_get_description (plugin));
1558 static void
1559 on_activated_plugin_objects_foreach (gpointer key, gpointer data, gpointer user_data)
1561 GList **active_plugins = (GList **)user_data;
1562 *active_plugins = g_list_prepend (*active_plugins,
1563 data);
1566 GList*
1567 anjuta_plugin_manager_get_active_plugins (AnjutaPluginManager *plugin_manager)
1569 GList *active_plugins = NULL;
1571 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
1572 g_hash_table_foreach (plugin_manager->priv->activated_plugins,
1573 on_activated_plugins_foreach,
1574 &active_plugins);
1575 return g_list_reverse (active_plugins);
1578 GList*
1579 anjuta_plugin_manager_get_active_plugin_objects (AnjutaPluginManager *plugin_manager)
1581 GList *active_plugins = NULL;
1583 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
1584 g_hash_table_foreach (plugin_manager->priv->activated_plugins,
1585 on_activated_plugin_objects_foreach,
1586 &active_plugins);
1587 return g_list_reverse (active_plugins);
1590 gboolean
1591 anjuta_plugin_manager_unload_plugin_by_id (AnjutaPluginManager *plugin_manager,
1592 const gchar *plugin_id)
1594 AnjutaPluginManagerPriv *priv;
1595 AnjutaPluginHandle *plugin;
1597 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), FALSE);
1598 g_return_val_if_fail (plugin_id != NULL, FALSE);
1600 priv = plugin_manager->priv;
1602 plugin = g_hash_table_lookup (priv->plugins_by_name, plugin_id);
1603 if (plugin)
1605 plugin_set_update (plugin_manager, plugin, FALSE);
1607 /* Check if the plugin has been indeed unloaded */
1608 if (!g_hash_table_lookup (priv->activated_plugins, plugin))
1609 return TRUE;
1610 else
1611 return FALSE;
1613 g_warning ("No plugin found with id \"%s\".", plugin_id);
1614 return FALSE;
1617 static gboolean
1618 find_plugin_for_object (gpointer key, gpointer value, gpointer data)
1620 if (value == data)
1622 g_object_set_data (G_OBJECT (data), "__plugin_plugin", key);
1623 return TRUE;
1625 return FALSE;
1628 gboolean
1629 anjuta_plugin_manager_unload_plugin (AnjutaPluginManager *plugin_manager,
1630 GObject *plugin_object)
1632 AnjutaPluginManagerPriv *priv;
1633 AnjutaPluginHandle *plugin;
1635 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), FALSE);
1636 g_return_val_if_fail (ANJUTA_IS_PLUGIN (plugin_object), FALSE);
1638 priv = plugin_manager->priv;
1640 plugin = NULL;
1642 /* Find the plugin that correspond to this plugin object */
1643 g_hash_table_find (priv->activated_plugins, find_plugin_for_object,
1644 plugin_object);
1645 plugin = g_object_get_data (G_OBJECT (plugin_object), "__plugin_plugin");
1647 if (plugin)
1649 plugin_set_update (plugin_manager, plugin, FALSE);
1651 /* Check if the plugin has been indeed unloaded */
1652 if (!g_hash_table_lookup (priv->activated_plugins, plugin))
1653 return TRUE;
1654 else
1655 return FALSE;
1657 g_warning ("No plugin found with object \"%p\".", plugin_object);
1658 return FALSE;
1661 GList*
1662 anjuta_plugin_manager_list_query (AnjutaPluginManager *plugin_manager,
1663 GList *secs,
1664 GList *anames,
1665 GList *avalues)
1667 AnjutaPluginManagerPriv *priv;
1668 GList *selected_plugins = NULL;
1669 const gchar *sec;
1670 const gchar *aname;
1671 const gchar *avalue;
1672 GList *available;
1674 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
1676 priv = plugin_manager->priv;
1677 available = priv->available_plugins;
1679 if (secs == NULL)
1681 /* If no query is given, select all plugins */
1682 while (available)
1684 AnjutaPluginHandle *plugin = available->data;
1685 AnjutaPluginDescription *desc =
1686 anjuta_plugin_handle_get_description (plugin);
1687 selected_plugins = g_list_prepend (selected_plugins, desc);
1688 available = g_list_next (available);
1690 return g_list_reverse (selected_plugins);
1693 g_return_val_if_fail (secs != NULL, NULL);
1694 g_return_val_if_fail (anames != NULL, NULL);
1695 g_return_val_if_fail (avalues != NULL, NULL);
1697 while (available)
1699 GList* s_node = secs;
1700 GList* n_node = anames;
1701 GList* v_node = avalues;
1703 gboolean satisfied = FALSE;
1705 AnjutaPluginHandle *plugin = available->data;
1706 AnjutaPluginDescription *desc =
1707 anjuta_plugin_handle_get_description (plugin);
1709 while (s_node)
1711 gchar *val;
1712 GList *vals;
1713 GList *node;
1714 gboolean found = FALSE;
1716 satisfied = TRUE;
1718 sec = s_node->data;
1719 aname = n_node->data;
1720 avalue = v_node->data;
1722 if (!anjuta_plugin_description_get_string (desc, sec, aname, &val))
1724 satisfied = FALSE;
1725 break;
1728 vals = property_to_list (val);
1729 g_free (val);
1731 node = vals;
1732 while (node)
1734 if (strchr(node->data, '*') != NULL)
1736 // Star match.
1737 gchar **segments;
1738 gchar **seg_ptr;
1739 const gchar *cursor;
1741 segments = g_strsplit (node->data, "*", -1);
1743 seg_ptr = segments;
1744 cursor = avalue;
1745 while (*seg_ptr != NULL)
1747 if (strlen (*seg_ptr) > 0) {
1748 cursor = strstr (cursor, *seg_ptr);
1749 if (cursor == NULL)
1750 break;
1752 cursor += strlen (*seg_ptr);
1753 seg_ptr++;
1755 if (*seg_ptr == NULL)
1756 found = TRUE;
1757 g_strfreev (segments);
1759 else if (g_ascii_strcasecmp (node->data, avalue) == 0)
1761 // String match.
1762 found = TRUE;
1764 g_free (node->data);
1765 node = g_list_next (node);
1767 g_list_free (vals);
1768 if (!found)
1770 satisfied = FALSE;
1771 break;
1773 s_node = g_list_next (s_node);
1774 n_node = g_list_next (n_node);
1775 v_node = g_list_next (v_node);
1777 if (satisfied)
1779 selected_plugins = g_list_prepend (selected_plugins, desc);
1780 /* DEBUG_PRINT ("Satisfied, Adding %s",
1781 anjuta_plugin_handle_get_name (plugin));*/
1783 available = g_list_next (available);
1786 return g_list_reverse (selected_plugins);
1789 GList*
1790 anjuta_plugin_manager_query (AnjutaPluginManager *plugin_manager,
1791 const gchar *section_name,
1792 const gchar *attribute_name,
1793 const gchar *attribute_value,
1794 ...)
1796 va_list var_args;
1797 GList *secs = NULL;
1798 GList *anames = NULL;
1799 GList *avalues = NULL;
1800 const gchar *sec;
1801 const gchar *aname;
1802 const gchar *avalue;
1803 GList *selected_plugins;
1806 if (section_name == NULL)
1808 /* If no query is given, select all plugins */
1809 return anjuta_plugin_manager_list_query (plugin_manager, NULL, NULL, NULL);
1812 g_return_val_if_fail (section_name != NULL, NULL);
1813 g_return_val_if_fail (attribute_name != NULL, NULL);
1814 g_return_val_if_fail (attribute_value != NULL, NULL);
1816 secs = g_list_prepend (secs, g_strdup (section_name));
1817 anames = g_list_prepend (anames, g_strdup (attribute_name));
1818 avalues = g_list_prepend (avalues, g_strdup (attribute_value));
1820 va_start (var_args, attribute_value);
1823 sec = va_arg (var_args, const gchar *);
1824 if (sec)
1826 aname = va_arg (var_args, const gchar *);
1827 if (aname)
1829 avalue = va_arg (var_args, const gchar *);
1830 if (avalue)
1832 secs = g_list_prepend (secs, g_strdup (sec));
1833 anames = g_list_prepend (anames, g_strdup (aname));
1834 avalues = g_list_prepend (avalues, g_strdup (avalue));
1839 while (sec);
1840 va_end (var_args);
1842 secs = g_list_reverse (secs);
1843 anames = g_list_reverse (anames);
1844 avalues = g_list_reverse (avalues);
1846 selected_plugins = anjuta_plugin_manager_list_query (plugin_manager,
1847 secs,
1848 anames,
1849 avalues);
1851 anjuta_util_glist_strings_free (secs);
1852 anjuta_util_glist_strings_free (anames);
1853 anjuta_util_glist_strings_free (avalues);
1855 return selected_plugins;
1858 enum {
1859 PIXBUF_COLUMN,
1860 PLUGIN_COLUMN,
1861 PLUGIN_DESCRIPTION_COLUMN,
1862 N_COLUMNS
1865 static void
1866 on_plugin_list_row_activated (GtkTreeView *tree_view,
1867 GtkTreePath *path,
1868 GtkTreeViewColumn *column,
1869 GtkDialog *dialog)
1871 gtk_dialog_response (dialog, GTK_RESPONSE_OK);
1874 AnjutaPluginDescription *
1875 anjuta_plugin_manager_select (AnjutaPluginManager *plugin_manager,
1876 gchar *title, gchar *description,
1877 GList *plugin_descriptions)
1879 AnjutaPluginDescription *desc;
1880 AnjutaPluginManagerPriv *priv;
1881 GtkWidget *dlg;
1882 GtkTreeModel *model;
1883 GtkWidget *view;
1884 GtkTreeViewColumn *column;
1885 GtkCellRenderer *renderer;
1886 GList *node;
1887 GtkWidget *label;
1888 GtkWidget *content_area;
1889 GtkWidget *sc;
1890 GtkWidget *remember_checkbox;
1891 gint response;
1892 GtkTreeIter selected;
1893 GtkTreeSelection *selection;
1894 GtkTreeModel *store;
1895 GList *selection_ids = NULL;
1896 GString *remember_key = g_string_new ("");
1898 g_return_val_if_fail (title != NULL, NULL);
1899 g_return_val_if_fail (description != NULL, NULL);
1900 g_return_val_if_fail (plugin_descriptions != NULL, NULL);
1902 priv = plugin_manager->priv;
1904 if (g_list_length (plugin_descriptions) <= 0)
1905 return NULL;
1907 dlg = gtk_dialog_new_with_buttons (title, GTK_WINDOW (priv->shell),
1908 GTK_DIALOG_DESTROY_WITH_PARENT,
1909 GTK_STOCK_CANCEL,
1910 GTK_RESPONSE_CANCEL,
1911 GTK_STOCK_OK, GTK_RESPONSE_OK,
1912 NULL);
1913 gtk_widget_set_size_request (dlg, 400, 300);
1914 gtk_window_set_default_size (GTK_WINDOW (dlg), 400, 300);
1916 label = gtk_label_new (description);
1917 gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
1918 gtk_widget_show (label);
1919 content_area = gtk_dialog_get_content_area (GTK_DIALOG (dlg));
1920 gtk_box_pack_start (GTK_BOX (content_area), label,
1921 FALSE, FALSE, 5);
1923 sc = gtk_scrolled_window_new (NULL, NULL);
1924 gtk_widget_show (sc);
1925 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sc),
1926 GTK_SHADOW_IN);
1927 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sc),
1928 GTK_POLICY_AUTOMATIC,
1929 GTK_POLICY_AUTOMATIC);
1931 gtk_box_pack_start (GTK_BOX (content_area), sc,
1932 TRUE, TRUE, 5);
1934 model = GTK_TREE_MODEL (gtk_list_store_new (N_COLUMNS, GDK_TYPE_PIXBUF,
1935 G_TYPE_STRING, G_TYPE_POINTER));
1936 view = gtk_tree_view_new_with_model (model);
1937 gtk_widget_show (view);
1938 gtk_container_add (GTK_CONTAINER (sc), view);
1940 column = gtk_tree_view_column_new ();
1941 gtk_tree_view_column_set_sizing (column,
1942 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1943 gtk_tree_view_column_set_title (column, _("Available Plugins"));
1945 renderer = gtk_cell_renderer_pixbuf_new ();
1946 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1947 gtk_tree_view_column_add_attribute (column, renderer, "pixbuf",
1948 PIXBUF_COLUMN);
1950 renderer = gtk_cell_renderer_text_new ();
1951 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1952 gtk_tree_view_column_add_attribute (column, renderer, "markup",
1953 PLUGIN_COLUMN);
1955 gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
1956 gtk_tree_view_set_expander_column (GTK_TREE_VIEW (view), column);
1958 g_signal_connect (view, "row-activated",
1959 G_CALLBACK (on_plugin_list_row_activated),
1960 GTK_DIALOG(dlg));
1961 remember_checkbox =
1962 gtk_check_button_new_with_label (_("Remember this selection"));
1963 gtk_container_set_border_width (GTK_CONTAINER (remember_checkbox), 10);
1964 gtk_widget_show (remember_checkbox);
1965 gtk_box_pack_start (GTK_BOX (content_area), remember_checkbox,
1966 FALSE, FALSE, 0);
1968 node = plugin_descriptions;
1969 while (node)
1971 GdkPixbuf *icon_pixbuf = NULL;
1972 gchar *plugin_name = NULL;
1973 gchar *plugin_desc = NULL;
1974 gchar *icon_filename = NULL;
1975 gchar *location = NULL;
1977 desc = (AnjutaPluginDescription*)node->data;
1979 if (anjuta_plugin_description_get_string (desc,
1980 "Anjuta Plugin",
1981 "Icon",
1982 &icon_filename))
1984 gchar *icon_path = NULL;
1985 icon_path = g_strconcat (PACKAGE_PIXMAPS_DIR"/",
1986 icon_filename, NULL);
1987 g_free (icon_filename);
1988 /* DEBUG_PRINT ("Icon: %s", icon_path); */
1989 icon_pixbuf =
1990 gdk_pixbuf_new_from_file (icon_path, NULL);
1991 if (icon_pixbuf == NULL)
1993 g_warning ("Plugin pixmap not found: %s", plugin_name);
1995 g_free (icon_path);
1997 else
1999 g_warning ("Plugin does not define Icon attribute");
2001 if (!anjuta_plugin_description_get_locale_string (desc,
2002 "Anjuta Plugin",
2003 "Name",
2004 &plugin_name))
2006 g_warning ("Plugin does not define Name attribute");
2008 if (!anjuta_plugin_description_get_locale_string (desc,
2009 "Anjuta Plugin",
2010 "Description",
2011 &plugin_desc))
2013 g_warning ("Plugin does not define Description attribute");
2015 if (plugin_name && plugin_desc)
2017 GtkTreeIter iter;
2018 gchar *text;
2020 if (!anjuta_plugin_description_get_string (desc,
2021 "Anjuta Plugin",
2022 "Location",
2023 &location))
2025 g_warning ("Plugin does not define Location attribute");
2029 text = g_markup_printf_escaped ("<span size=\"larger\" weight=\"bold\">%s</span>\n%s", plugin_name, plugin_desc);
2031 gtk_list_store_append (GTK_LIST_STORE (model), &iter);
2032 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
2033 PLUGIN_COLUMN, text,
2034 PLUGIN_DESCRIPTION_COLUMN, desc, -1);
2035 if (icon_pixbuf) {
2036 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
2037 PIXBUF_COLUMN, icon_pixbuf, -1);
2038 g_object_unref (icon_pixbuf);
2040 g_free (text);
2042 selection_ids = g_list_prepend (selection_ids, location);
2044 g_free (plugin_name);
2045 g_free (plugin_desc);
2046 node = g_list_next (node);
2049 /* Prepare remembering key */
2050 selection_ids = g_list_sort (selection_ids,
2051 (GCompareFunc)strcmp);
2052 node = selection_ids;
2053 while (node)
2055 g_string_append (remember_key, (gchar*)node->data);
2056 g_string_append (remember_key, ",");
2057 node = g_list_next (node);
2059 g_list_foreach (selection_ids, (GFunc) g_free, NULL);
2060 g_list_free (selection_ids);
2062 /* Find if the selection is remembered */
2063 desc = g_hash_table_lookup (priv->remember_plugins, remember_key->str);
2064 if (desc)
2066 g_string_free (remember_key, TRUE);
2067 gtk_widget_destroy (dlg);
2068 return desc;
2071 /* Prompt dialog */
2072 response = gtk_dialog_run (GTK_DIALOG (dlg));
2073 switch (response)
2075 case GTK_RESPONSE_OK:
2076 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
2077 if (gtk_tree_selection_get_selected (selection, &store,
2078 &selected))
2080 gtk_tree_model_get (model, &selected,
2081 PLUGIN_DESCRIPTION_COLUMN, &desc, -1);
2082 if (desc)
2084 /* Remember selection */
2085 if (gtk_toggle_button_get_active
2086 (GTK_TOGGLE_BUTTON (remember_checkbox)))
2088 /* DEBUG_PRINT ("Remembering selection '%s'",
2089 remember_key->str);*/
2090 g_hash_table_insert (priv->remember_plugins,
2091 g_strdup (remember_key->str), desc);
2093 g_string_free (remember_key, TRUE);
2094 gtk_widget_destroy (dlg);
2095 return desc;
2098 break;
2100 g_string_free (remember_key, TRUE);
2101 gtk_widget_destroy (dlg);
2102 return NULL;
2105 GObject*
2106 anjuta_plugin_manager_select_and_activate (AnjutaPluginManager *plugin_manager,
2107 gchar *title,
2108 gchar *description,
2109 GList *plugin_descriptions)
2111 AnjutaPluginDescription *d;
2113 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
2115 d = anjuta_plugin_manager_select (plugin_manager, title, description,
2116 plugin_descriptions);
2117 if (d)
2119 GObject *plugin = NULL;
2120 gchar *location = NULL;
2122 anjuta_plugin_description_get_string (d,
2123 "Anjuta Plugin",
2124 "Location",
2125 &location);
2126 g_return_val_if_fail (location != NULL, NULL);
2127 plugin =
2128 anjuta_plugin_manager_get_plugin_by_id (plugin_manager, location);
2129 g_free (location);
2130 return plugin;
2132 return NULL;
2135 /* Plugin manager */
2137 static void
2138 anjuta_plugin_manager_init (AnjutaPluginManager *object)
2140 object->priv = g_new0 (AnjutaPluginManagerPriv, 1);
2141 object->priv->plugins_by_name = g_hash_table_new (g_str_hash, g_str_equal);
2142 object->priv->plugins_by_interfaces = g_hash_table_new_full (g_str_hash,
2143 g_str_equal,
2144 NULL,
2145 (GDestroyNotify) g_list_free);
2146 object->priv->plugins_by_description = g_hash_table_new (g_direct_hash,
2147 g_direct_equal);
2148 object->priv->activated_plugins = g_hash_table_new (g_direct_hash,
2149 g_direct_equal);
2150 object->priv->plugins_cache = g_hash_table_new (g_direct_hash,
2151 g_direct_equal);
2152 object->priv->remember_plugins = g_hash_table_new_full (g_str_hash,
2153 g_str_equal,
2154 g_free, NULL);
2157 static void
2158 anjuta_plugin_manager_finalize (GObject *object)
2160 AnjutaPluginManagerPriv *priv;
2161 priv = ANJUTA_PLUGIN_MANAGER (object)->priv;
2162 if (priv->available_plugins)
2164 /* anjuta_plugin_manager_unload_all_plugins (ANJUTA_PLUGIN_MANAGER (object)); */
2165 g_list_foreach (priv->available_plugins, (GFunc)g_object_unref, NULL);
2166 g_list_free (priv->available_plugins);
2167 priv->available_plugins = NULL;
2169 if (priv->activated_plugins)
2171 g_hash_table_destroy (priv->activated_plugins);
2172 priv->activated_plugins = NULL;
2174 if (priv->plugins_cache)
2176 g_hash_table_destroy (priv->plugins_cache);
2177 priv->plugins_cache = NULL;
2179 if (priv->plugins_by_name)
2181 g_hash_table_destroy (priv->plugins_by_name);
2182 priv->plugins_by_name = NULL;
2184 if (priv->plugins_by_description)
2186 g_hash_table_destroy (priv->plugins_by_description);
2187 priv->plugins_by_description = NULL;
2189 if (priv->plugins_by_interfaces)
2191 g_hash_table_destroy (priv->plugins_by_interfaces);
2192 priv->plugins_by_interfaces = NULL;
2194 if (priv->plugin_dirs)
2196 g_list_foreach (priv->plugin_dirs, (GFunc)g_free, NULL);
2197 g_list_free (priv->plugin_dirs);
2198 priv->plugin_dirs = NULL;
2200 #if 0
2201 if (anjuta_c_plugin_factory)
2203 g_object_unref (anjuta_c_plugin_factory);
2204 anjuta_c_plugin_factory = NULL;
2206 #endif
2207 G_OBJECT_CLASS (parent_class)->finalize (object);
2210 static void
2211 anjuta_plugin_manager_set_property (GObject *object, guint prop_id,
2212 const GValue *value, GParamSpec *pspec)
2214 AnjutaPluginManagerPriv *priv;
2216 g_return_if_fail (ANJUTA_IS_PLUGIN_MANAGER (object));
2217 priv = ANJUTA_PLUGIN_MANAGER (object)->priv;
2219 switch (prop_id)
2221 case PROP_STATUS:
2222 priv->status = g_value_get_object (value);
2223 break;
2224 case PROP_SHELL:
2225 priv->shell = g_value_get_object (value);
2226 break;
2227 default:
2228 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2229 break;
2233 static void
2234 anjuta_plugin_manager_get_property (GObject *object, guint prop_id,
2235 GValue *value, GParamSpec *pspec)
2237 AnjutaPluginManagerPriv *priv;
2239 g_return_if_fail (ANJUTA_IS_PLUGIN_MANAGER (object));
2240 priv = ANJUTA_PLUGIN_MANAGER (object)->priv;
2242 switch (prop_id)
2244 case PROP_SHELL:
2245 g_value_set_object (value, priv->shell);
2246 break;
2247 case PROP_STATUS:
2248 g_value_set_object (value, priv->status);
2249 break;
2250 case PROP_AVAILABLE_PLUGINS:
2251 g_value_set_pointer (value, priv->available_plugins);
2252 break;
2253 case PROP_ACTIVATED_PLUGINS:
2254 g_value_set_pointer (value, priv->activated_plugins);
2255 break;
2256 default:
2257 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2258 break;
2261 static void
2262 anjuta_plugin_manager_plugin_activated (AnjutaPluginManager *self,
2263 AnjutaPluginDescription* plugin_desc,
2264 GObject *plugin)
2266 /* TODO: Add default signal handler implementation here */
2269 static void
2270 anjuta_plugin_manager_plugin_deactivated (AnjutaPluginManager *self,
2271 AnjutaPluginDescription* plugin_desc,
2272 GObject *plugin)
2274 /* TODO: Add default signal handler implementation here */
2277 static void
2278 anjuta_plugin_manager_class_init (AnjutaPluginManagerClass *klass)
2280 GObjectClass* object_class = G_OBJECT_CLASS (klass);
2281 parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
2283 object_class->finalize = anjuta_plugin_manager_finalize;
2284 object_class->set_property = anjuta_plugin_manager_set_property;
2285 object_class->get_property = anjuta_plugin_manager_get_property;
2287 klass->plugin_activated = anjuta_plugin_manager_plugin_activated;
2288 klass->plugin_deactivated = anjuta_plugin_manager_plugin_deactivated;
2290 g_object_class_install_property (object_class,
2291 PROP_PROFILES,
2292 g_param_spec_pointer ("profiles",
2293 _("Profiles"),
2294 _("Current stack of profiles"),
2295 G_PARAM_READABLE));
2296 g_object_class_install_property (object_class,
2297 PROP_AVAILABLE_PLUGINS,
2298 g_param_spec_pointer ("available-plugins",
2299 _("Available plugins"),
2300 _("Currently available plugins found in plugin paths"),
2301 G_PARAM_READABLE));
2303 g_object_class_install_property (object_class,
2304 PROP_ACTIVATED_PLUGINS,
2305 g_param_spec_pointer ("activated-plugins",
2306 _("Activated plugins"),
2307 _("Currently activated plugins"),
2308 G_PARAM_READABLE));
2309 g_object_class_install_property (object_class,
2310 PROP_SHELL,
2311 g_param_spec_object ("shell",
2312 _("Anjuta Shell"),
2313 _("Anjuta shell for which the plugins are made"),
2314 G_TYPE_OBJECT,
2315 G_PARAM_READABLE |
2316 G_PARAM_WRITABLE |
2317 G_PARAM_CONSTRUCT));
2318 g_object_class_install_property (object_class,
2319 PROP_STATUS,
2320 g_param_spec_object ("status",
2321 _("Anjuta Status"),
2322 _("Anjuta status to use in loading and unloading of plugins"),
2323 ANJUTA_TYPE_STATUS,
2324 G_PARAM_READABLE |
2325 G_PARAM_WRITABLE |
2326 G_PARAM_CONSTRUCT));
2328 plugin_manager_signals[PLUGIN_ACTIVATED] =
2329 g_signal_new ("plugin-activated",
2330 G_OBJECT_CLASS_TYPE (klass),
2331 G_SIGNAL_RUN_FIRST,
2332 G_STRUCT_OFFSET (AnjutaPluginManagerClass,
2333 plugin_activated),
2334 NULL, NULL,
2335 anjuta_cclosure_marshal_VOID__POINTER_OBJECT,
2336 G_TYPE_NONE, 2,
2337 G_TYPE_POINTER, ANJUTA_TYPE_PLUGIN);
2339 plugin_manager_signals[PLUGIN_DEACTIVATED] =
2340 g_signal_new ("plugin-deactivated",
2341 G_OBJECT_CLASS_TYPE (klass),
2342 G_SIGNAL_RUN_FIRST,
2343 G_STRUCT_OFFSET (AnjutaPluginManagerClass,
2344 plugin_deactivated),
2345 NULL, NULL,
2346 anjuta_cclosure_marshal_VOID__POINTER_OBJECT,
2347 G_TYPE_NONE, 2,
2348 G_TYPE_POINTER, ANJUTA_TYPE_PLUGIN);
2351 GType
2352 anjuta_plugin_manager_get_type (void)
2354 static GType our_type = 0;
2356 if(our_type == 0)
2358 static const GTypeInfo our_info =
2360 sizeof (AnjutaPluginManagerClass), /* class_size */
2361 (GBaseInitFunc) NULL, /* base_init */
2362 (GBaseFinalizeFunc) NULL, /* base_finalize */
2363 (GClassInitFunc) anjuta_plugin_manager_class_init, /* class_init */
2364 (GClassFinalizeFunc) NULL, /* class_finalize */
2365 NULL /* class_data */,
2366 sizeof (AnjutaPluginManager), /* instance_size */
2367 0, /* n_preallocs */
2368 (GInstanceInitFunc) anjuta_plugin_manager_init, /* instance_init */
2369 NULL /* value_table */
2371 our_type = g_type_register_static (G_TYPE_OBJECT,
2372 "AnjutaPluginManager",
2373 &our_info, 0);
2376 return our_type;
2379 AnjutaPluginManager*
2380 anjuta_plugin_manager_new (GObject *shell, AnjutaStatus *status,
2381 GList* plugins_directories)
2383 GObject *manager_object;
2384 AnjutaPluginManager *plugin_manager;
2385 GList *cycles = NULL;
2386 const char *gnome2_path;
2387 char **pathv;
2388 char **p;
2389 GList *node;
2390 GList *plugin_dirs = NULL;
2392 /* Initialize the anjuta plugin system */
2393 manager_object = g_object_new (ANJUTA_TYPE_PLUGIN_MANAGER,
2394 "shell", shell, "status", status, NULL);
2395 plugin_manager = ANJUTA_PLUGIN_MANAGER (manager_object);
2397 if (anjuta_plugin_factory == NULL)
2399 anjuta_plugin_factory = anjuta_c_plugin_factory_new ();
2402 gnome2_path = g_getenv ("GNOME2_PATH");
2403 if (gnome2_path) {
2404 pathv = g_strsplit (gnome2_path, ":", 1);
2406 for (p = pathv; *p != NULL; p++) {
2407 char *path = g_strdup (*p);
2408 plugin_dirs = g_list_prepend (plugin_dirs, path);
2410 g_strfreev (pathv);
2413 node = plugins_directories;
2414 while (node) {
2415 if (!node->data)
2416 continue;
2417 char *path = g_strdup (node->data);
2418 plugin_dirs = g_list_prepend (plugin_dirs, path);
2419 node = g_list_next (node);
2421 plugin_dirs = g_list_reverse (plugin_dirs);
2422 /* load_plugins (); */
2424 node = plugin_dirs;
2425 while (node)
2427 load_plugins_from_directory (plugin_manager, (char*)node->data);
2428 node = g_list_next (node);
2430 resolve_dependencies (plugin_manager, &cycles);
2431 g_list_foreach(plugin_dirs, (GFunc) g_free, NULL);
2432 g_list_free(plugin_dirs);
2433 return plugin_manager;
2436 void
2437 anjuta_plugin_manager_activate_plugins (AnjutaPluginManager *plugin_manager,
2438 GList *plugins_to_activate)
2440 AnjutaPluginManagerPriv *priv;
2441 GdkPixbuf *icon_pixbuf;
2442 GList *node;
2444 priv = plugin_manager->priv;
2446 /* Freeze shell operations */
2447 anjuta_shell_freeze (ANJUTA_SHELL (priv->shell), NULL);
2448 if (plugins_to_activate)
2450 anjuta_status_progress_add_ticks (ANJUTA_STATUS (priv->status),
2451 g_list_length (plugins_to_activate));
2453 node = plugins_to_activate;
2454 while (node)
2456 AnjutaPluginDescription *d;
2457 gchar *plugin_id;
2458 gchar *icon_filename, *label;
2459 gchar *icon_path = NULL;
2461 d = node->data;
2463 icon_pixbuf = NULL;
2464 label = NULL;
2465 if (anjuta_plugin_description_get_string (d, "Anjuta Plugin",
2466 "Icon",
2467 &icon_filename))
2469 gchar *title /*, *description */;
2470 anjuta_plugin_description_get_locale_string (d, "Anjuta Plugin",
2471 "Name",
2472 &title);
2474 anjuta_plugin_description_get_locale_string (d, "Anjuta Plugin",
2475 "Description",
2476 &description);
2478 icon_path = g_strconcat (PACKAGE_PIXMAPS_DIR"/",
2479 icon_filename, NULL);
2480 /* DEBUG_PRINT ("Icon: %s", icon_path); */
2481 /* Avoid space in translated string */
2482 label = g_strconcat (_("Loaded:"), " ", title, "...", NULL);
2483 icon_pixbuf = gdk_pixbuf_new_from_file (icon_path, NULL);
2484 if (!icon_pixbuf)
2485 g_warning ("Plugin does not define Icon: No such file %s",
2486 icon_path);
2487 g_free (icon_path);
2488 g_free (icon_filename);
2489 g_free (title);
2492 if (anjuta_plugin_description_get_string (d, "Anjuta Plugin",
2493 "Location", &plugin_id))
2495 GObject *plugin_obj;
2497 plugin_obj =
2498 anjuta_plugin_manager_get_plugin_by_id (plugin_manager,
2499 plugin_id);
2500 g_free (plugin_id);
2502 anjuta_status_progress_tick (ANJUTA_STATUS (priv->status),
2503 icon_pixbuf, label);
2504 g_free (label);
2505 if (icon_pixbuf)
2506 g_object_unref (icon_pixbuf);
2508 node = g_list_next (node);
2511 /* Thaw shell operations */
2512 anjuta_shell_thaw (ANJUTA_SHELL (priv->shell), NULL);
2515 static void
2516 on_collect (gpointer key, gpointer value, gpointer user_data)
2518 gchar *id;
2519 gchar *query = (gchar*) key;
2520 AnjutaPluginDescription *desc = (AnjutaPluginDescription *) value;
2521 GString *write_buffer = (GString *) user_data;
2523 anjuta_plugin_description_get_string (desc, "Anjuta Plugin", "Location",
2524 &id);
2525 g_string_append_printf (write_buffer, "%s=%s;", query, id);
2526 g_free (id);
2530 * anjuta_plugin_manager_get_remembered_plugins:
2531 * @plugin_manager: A #AnjutaPluginManager object
2533 * Get the list of plugins loaded when there is a choice between several
2534 * ones without asking the user.
2536 * The list format is returned as a string with the format detailed in
2537 * anjuta_plugin_manager_set_remembered_plugins().
2539 * Return value: a newly-allocated string that must be freed with g_free().
2542 gchar*
2543 anjuta_plugin_manager_get_remembered_plugins (AnjutaPluginManager *plugin_manager)
2545 AnjutaPluginManagerPriv *priv;
2546 GString *write_buffer = g_string_new ("");
2548 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), FALSE);
2550 priv = plugin_manager->priv;
2551 g_hash_table_foreach (priv->remember_plugins, on_collect,
2552 write_buffer);
2553 return g_string_free (write_buffer, FALSE);
2556 static gboolean
2557 on_foreach_remove_true (gpointer k, gpointer v, gpointer d)
2559 return TRUE;
2563 * anjuta_plugin_manager_set_remembered_plugins:
2564 * @plugin_manager: A #AnjutaPluginManager object
2565 * @remembered_plugins: A list of prefered plugins
2567 * Set the list of plugins loaded when there is a choice between several
2568 * ones without asking the user.
2569 * The list is a string composed of elements separated by ';'. Each element
2570 * is defined with "key=value", where key is the list of possible plugins and
2571 * the value is the choosen plugin.
2573 * By the example the following element
2574 * <programlisting>
2575 * anjuta-symbol-browser:SymbolBrowserPlugin,anjuta-symbol-db:SymbolDBPlugin,=anjuta-symbol-db:SymbolDBPlugin;
2576 * </programlisting>
2577 * means if Anjuta has to choose between SymbolBrowserPlugin and
2578 * SymbolDBPlugin, it will choose SymbolDBPlugin.
2580 void
2581 anjuta_plugin_manager_set_remembered_plugins (AnjutaPluginManager *plugin_manager,
2582 const gchar *remembered_plugins)
2584 AnjutaPluginManagerPriv *priv;
2585 gchar **strv_lines, **line_idx;
2587 g_return_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager));
2588 g_return_if_fail (remembered_plugins != NULL);
2590 priv = plugin_manager->priv;
2592 g_hash_table_foreach_remove (priv->remember_plugins,
2593 on_foreach_remove_true, NULL);
2595 strv_lines = g_strsplit (remembered_plugins, ";", -1);
2596 line_idx = strv_lines;
2597 while (*line_idx)
2599 gchar **strv_keyvals;
2600 strv_keyvals = g_strsplit (*line_idx, "=", -1);
2601 if (strv_keyvals && strv_keyvals[0] && strv_keyvals[1])
2603 AnjutaPluginHandle *plugin;
2604 plugin = g_hash_table_lookup (priv->plugins_by_name,
2605 strv_keyvals[1]);
2606 if (plugin)
2608 AnjutaPluginDescription *desc;
2609 desc = anjuta_plugin_handle_get_description (plugin);
2611 DEBUG_PRINT ("Restoring remember plugin: %s=%s",
2612 strv_keyvals[0],
2613 strv_keyvals[1]);
2615 g_hash_table_insert (priv->remember_plugins,
2616 g_strdup (strv_keyvals[0]), desc);
2618 g_strfreev (strv_keyvals);
2620 line_idx++;
2622 g_strfreev (strv_lines);