Use dist_doc_DATA for documentation text files
[anjuta.git] / libanjuta / anjuta-plugin-manager.c
blob46195b102e790fcc22acd656fa551e91760665eb
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 #ifdef HAVE_CONFIG_H
31 # include <config.h>
32 #endif
34 #include <sys/types.h>
35 #include <dirent.h>
36 #include <string.h>
38 #include <libanjuta/anjuta-plugin-manager.h>
39 #include <libanjuta/anjuta-marshal.h>
40 #include <libanjuta/anjuta-debug.h>
41 #include <libanjuta/anjuta-plugin-handle.h>
42 #include <libanjuta/anjuta-plugin.h>
43 #include <libanjuta/anjuta-c-plugin-factory.h>
44 #include <libanjuta/interfaces/ianjuta-plugin-factory.h>
45 #include <libanjuta/interfaces/ianjuta-preferences.h>
48 enum
50 PROP_0,
52 PROP_SHELL,
53 PROP_STATUS,
54 PROP_PROFILES,
55 PROP_AVAILABLE_PLUGINS,
56 PROP_ACTIVATED_PLUGINS
59 enum
61 PROFILE_PUSHED,
62 PROFILE_POPPED,
63 PLUGINS_TO_LOAD,
64 PLUGINS_TO_UNLOAD,
65 PLUGIN_ACTIVATED,
66 PLUGIN_DEACTIVATED,
68 LAST_SIGNAL
71 struct _AnjutaPluginManagerPriv
73 GObject *shell;
74 AnjutaStatus *status;
75 GList *plugin_dirs;
76 GList *available_plugins;
78 /* Indexes => plugin handles */
79 GHashTable *plugins_by_interfaces;
80 GHashTable *plugins_by_name;
81 GHashTable *plugins_by_description;
83 /* Plugins that are currently activated */
84 GHashTable *activated_plugins;
86 /* Plugins that have been previously loaded but current deactivated */
87 GHashTable *plugins_cache;
89 /* Remember plugin selection */
90 GHashTable *remember_plugins;
93 /* Available plugins page treeview */
94 enum {
95 COL_ACTIVABLE,
96 COL_ENABLED,
97 COL_ICON,
98 COL_NAME,
99 COL_PLUGIN,
100 N_COLS
103 /* Remembered plugins page treeview */
104 enum {
105 COL_REM_ICON,
106 COL_REM_NAME,
107 COL_REM_PLUGIN_KEY,
108 N_REM_COLS
111 /* Plugin class types */
113 static AnjutaCPluginFactory *anjuta_plugin_factory = NULL;
115 static GObjectClass* parent_class = NULL;
116 static guint plugin_manager_signals[LAST_SIGNAL] = { 0 };
118 static void plugin_set_update (AnjutaPluginManager *plugin_manager,
119 AnjutaPluginHandle* selected_plugin,
120 gboolean load);
122 static IAnjutaPluginFactory* get_plugin_factory (AnjutaPluginManager *plugin_manager,
123 const gchar *language, GError **error);
125 GQuark
126 anjuta_plugin_manager_error_quark (void)
128 static GQuark quark = 0;
130 if (quark == 0) {
131 quark = g_quark_from_static_string ("anjuta-plugin-manager-quark");
133 return quark;
136 /** Dependency Resolution **/
138 static gboolean
139 collect_cycle (AnjutaPluginManager *plugin_manager,
140 AnjutaPluginHandle *base_plugin, AnjutaPluginHandle *cur_plugin,
141 GList **cycle)
143 AnjutaPluginManagerPriv *priv;
144 GList *l;
146 priv = plugin_manager->priv;
148 for (l = anjuta_plugin_handle_get_dependency_names (cur_plugin);
149 l != NULL; l = l->next)
151 AnjutaPluginHandle *dep = g_hash_table_lookup (priv->plugins_by_name,
152 l->data);
153 if (dep)
155 if (dep == base_plugin)
157 *cycle = g_list_prepend (NULL, dep);
158 /* DEBUG_PRINT ("%s", anjuta_plugin_handle_get_name (dep)); */
159 return TRUE;
161 else
163 if (collect_cycle (plugin_manager, base_plugin, dep, cycle))
165 *cycle = g_list_prepend (*cycle, dep);
166 /* DEBUG_PRINT ("%s", anjuta_plugin_handle_get_name (dep)); */
167 return TRUE;
172 return FALSE;
175 static void
176 add_dependency (AnjutaPluginHandle *dependent, AnjutaPluginHandle *dependency)
178 g_hash_table_insert (anjuta_plugin_handle_get_dependents (dependency),
179 dependent, dependency);
180 g_hash_table_insert (anjuta_plugin_handle_get_dependencies (dependent),
181 dependency, dependent);
184 static void
185 child_dep_foreach_cb (gpointer key, gpointer value, gpointer user_data)
187 add_dependency (ANJUTA_PLUGIN_HANDLE (user_data),
188 ANJUTA_PLUGIN_HANDLE (key));
191 /* Resolves dependencies for a single module recursively. Shortcuts if
192 * the module has already been resolved. Returns a list representing
193 * any cycles found, or NULL if no cycles are found. If a cycle is found,
194 * the graph is left unresolved.
196 static GList*
197 resolve_for_module (AnjutaPluginManager *plugin_manager,
198 AnjutaPluginHandle *plugin, int pass)
200 AnjutaPluginManagerPriv *priv;
201 GList *l;
202 GList *ret = NULL;
204 priv = plugin_manager->priv;
206 if (anjuta_plugin_handle_get_checked (plugin))
208 return NULL;
211 if (anjuta_plugin_handle_get_resolve_pass (plugin) == pass)
213 GList *cycle = NULL;
214 g_warning ("cycle found: %s on pass %d",
215 anjuta_plugin_handle_get_name (plugin),
216 anjuta_plugin_handle_get_resolve_pass (plugin));
217 collect_cycle (plugin_manager, plugin, plugin, &cycle);
218 return cycle;
221 if (anjuta_plugin_handle_get_resolve_pass (plugin) != -1)
223 return NULL;
226 anjuta_plugin_handle_set_can_load (plugin, TRUE);
227 anjuta_plugin_handle_set_resolve_pass (plugin, pass);
229 for (l = anjuta_plugin_handle_get_dependency_names (plugin);
230 l != NULL; l = l->next)
232 char *dep = l->data;
233 AnjutaPluginHandle *child =
234 g_hash_table_lookup (priv->plugins_by_name, dep);
235 if (child)
237 ret = resolve_for_module (plugin_manager, child, pass);
238 if (ret)
240 break;
243 /* Add the dependency's dense dependency list
244 * to the current module's dense dependency list */
245 g_hash_table_foreach (anjuta_plugin_handle_get_dependencies (child),
246 child_dep_foreach_cb, plugin);
247 add_dependency (plugin, child);
249 /* If the child can't load due to dependency problems,
250 * the current module can't either */
251 anjuta_plugin_handle_set_can_load (plugin,
252 anjuta_plugin_handle_get_can_load (child));
253 } else {
254 g_warning ("Dependency %s not found.\n", dep);
255 anjuta_plugin_handle_set_can_load (plugin, FALSE);
256 ret = NULL;
259 anjuta_plugin_handle_set_checked (plugin, TRUE);
261 return ret;
264 /* Clean up the results of a resolving run */
265 static void
266 unresolve_dependencies (AnjutaPluginManager *plugin_manager)
268 AnjutaPluginManagerPriv *priv;
269 GList *l;
271 priv = plugin_manager->priv;
273 for (l = priv->available_plugins; l != NULL; l = l->next)
275 AnjutaPluginHandle *plugin = l->data;
276 anjuta_plugin_handle_unresolve_dependencies (plugin);
280 /* done upto here */
282 static void
283 prune_modules (AnjutaPluginManager *plugin_manager, GList *modules)
285 AnjutaPluginManagerPriv *priv;
286 GList *l;
288 priv = plugin_manager->priv;
290 for (l = modules; l != NULL; l = l->next) {
291 AnjutaPluginHandle *plugin = l->data;
293 g_hash_table_remove (priv->plugins_by_name,
294 anjuta_plugin_handle_get_id (plugin));
295 priv->available_plugins = g_list_remove (priv->available_plugins, plugin);
299 static int
300 dependency_compare (AnjutaPluginHandle *plugin_a,
301 AnjutaPluginHandle *plugin_b)
303 int a = g_hash_table_size (anjuta_plugin_handle_get_dependencies (plugin_a));
304 int b = g_hash_table_size (anjuta_plugin_handle_get_dependencies (plugin_b));
306 return a - b;
309 /* Resolves the dependencies of the priv->available_plugins list. When this
310 * function is complete, the following will be true:
312 * 1) The dependencies and dependents hash tables of the modules will
313 * be filled.
315 * 2) Cycles in the graph will be removed.
317 * 3) Modules which cannot be loaded due to failed dependencies will
318 * be marked as such.
320 * 4) priv->available_plugins will be sorted such that no module depends on a
321 * module after it.
323 * If a cycle in the graph is found, it is pruned from the tree and
324 * returned as a list stored in the cycles list.
326 static void
327 resolve_dependencies (AnjutaPluginManager *plugin_manager, GList **cycles)
329 AnjutaPluginManagerPriv *priv;
330 GList *cycle = NULL;
331 GList *l;
333 priv = plugin_manager->priv;
334 *cycles = NULL;
336 /* Try resolving dependencies. If there is a cycle, prune the
337 * cycle and try to resolve again */
340 int pass = 1;
341 cycle = NULL;
342 for (l = priv->available_plugins; l != NULL && !cycle; l = l->next) {
343 cycle = resolve_for_module (plugin_manager, l->data, pass++);
344 cycle = NULL;
346 if (cycle) {
347 *cycles = g_list_prepend (*cycles, cycle);
348 prune_modules (plugin_manager, cycle);
349 unresolve_dependencies (plugin_manager);
351 } while (cycle);
353 /* Now that there is a fully resolved dependency tree, sort
354 * priv->available_plugins to create a valid load order */
355 priv->available_plugins = g_list_sort (priv->available_plugins,
356 (GCompareFunc)dependency_compare);
359 /* Plugins loading */
361 static gboolean
362 str_has_suffix (const char *haystack, const char *needle)
364 const char *h, *n;
366 if (needle == NULL) {
367 return TRUE;
369 if (haystack == NULL) {
370 return needle[0] == '\0';
373 /* Eat one character at a time. */
374 h = haystack + strlen(haystack);
375 n = needle + strlen(needle);
376 do {
377 if (n == needle) {
378 return TRUE;
380 if (h == haystack) {
381 return FALSE;
383 } while (*--h == *--n);
384 return FALSE;
387 static void
388 load_plugin (AnjutaPluginManager *plugin_manager,
389 const gchar *plugin_desc_path)
391 AnjutaPluginManagerPriv *priv;
392 AnjutaPluginHandle *plugin_handle;
394 g_return_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager));
395 priv = plugin_manager->priv;
397 plugin_handle = anjuta_plugin_handle_new (plugin_desc_path);
398 if (plugin_handle)
400 if (g_hash_table_lookup (priv->plugins_by_name,
401 anjuta_plugin_handle_get_id (plugin_handle)))
403 g_object_unref (plugin_handle);
405 else
407 GList *node;
408 /* Available plugin */
409 priv->available_plugins = g_list_prepend (priv->available_plugins,
410 plugin_handle);
411 /* Index by id */
412 g_hash_table_insert (priv->plugins_by_name,
413 (gchar *)anjuta_plugin_handle_get_id (plugin_handle),
414 plugin_handle);
416 /* Index by description */
417 g_hash_table_insert (priv->plugins_by_description,
418 anjuta_plugin_handle_get_description (plugin_handle),
419 plugin_handle);
421 /* Index by interfaces exported by this plugin */
422 node = anjuta_plugin_handle_get_interfaces (plugin_handle);
423 while (node)
425 GList *objs;
426 gchar *iface;
427 GList *obj_node;
428 gboolean found;
430 iface = node->data;
431 objs = (GList*)g_hash_table_lookup (priv->plugins_by_interfaces, iface);
433 obj_node = objs;
434 found = FALSE;
435 while (obj_node)
437 if (obj_node->data == plugin_handle)
439 found = TRUE;
440 break;
442 obj_node = g_list_next (obj_node);
444 if (!found)
446 g_hash_table_steal (priv->plugins_by_interfaces, iface);
447 objs = g_list_prepend (objs, plugin_handle);
448 g_hash_table_insert (priv->plugins_by_interfaces, iface, objs);
450 node = g_list_next (node);
454 return;
457 static void
458 load_plugins_from_directory (AnjutaPluginManager* plugin_manager,
459 const gchar *dirname)
461 DIR *dir;
462 struct dirent *entry;
464 dir = opendir (dirname);
466 if (!dir)
468 return;
471 for (entry = readdir (dir); entry != NULL; entry = readdir (dir))
473 if (str_has_suffix (entry->d_name, ".plugin"))
475 gchar *pathname;
476 pathname = g_strdup_printf ("%s/%s", dirname, entry->d_name);
477 load_plugin (plugin_manager,pathname);
478 g_free (pathname);
481 closedir (dir);
484 /* Plugin activation and deactivation */
486 static void
487 on_plugin_activated (AnjutaPlugin *plugin_object, AnjutaPluginHandle *plugin)
489 AnjutaPluginManager *plugin_manager;
490 AnjutaPluginManagerPriv *priv;
492 /* FIXME: Pass plugin_manager directly in signal arguments */
493 plugin_manager = anjuta_shell_get_plugin_manager (plugin_object->shell, NULL);
495 g_return_if_fail(plugin_manager != NULL);
497 priv = plugin_manager->priv;
499 g_hash_table_insert (priv->activated_plugins, plugin,
500 g_object_ref (plugin_object));
501 g_hash_table_remove (priv->plugins_cache, plugin);
503 g_signal_emit_by_name (plugin_manager, "plugin-activated",
504 anjuta_plugin_handle_get_description (plugin),
505 plugin_object);
508 static void
509 on_plugin_deactivated (AnjutaPlugin *plugin_object, AnjutaPluginHandle *plugin)
511 AnjutaPluginManager *plugin_manager;
512 AnjutaPluginManagerPriv *priv;
514 /* FIXME: Pass plugin_manager directly in signal arguments */
515 plugin_manager = anjuta_shell_get_plugin_manager (plugin_object->shell, NULL);
517 g_return_if_fail (plugin_manager != NULL);
519 priv = plugin_manager->priv;
521 g_hash_table_insert (priv->plugins_cache, plugin, g_object_ref (plugin_object));
522 g_hash_table_remove (priv->activated_plugins, plugin);
524 g_signal_emit_by_name (plugin_manager, "plugin-deactivated",
525 anjuta_plugin_handle_get_description (plugin),
526 plugin_object);
529 static AnjutaPlugin*
530 activate_plugin (AnjutaPluginManager *plugin_manager,
531 AnjutaPluginHandle *handle, GError **error)
533 AnjutaPluginManagerPriv *priv;
534 IAnjutaPluginFactory* factory;
535 AnjutaPlugin *plugin;
536 const gchar *language;
538 priv = plugin_manager->priv;
540 language = anjuta_plugin_handle_get_language (handle);
542 factory = get_plugin_factory (plugin_manager, language, error);
543 if (factory == NULL) return NULL;
545 plugin = ianjuta_plugin_factory_new_plugin (factory, handle, ANJUTA_SHELL (priv->shell), error);
547 if (plugin == NULL)
549 return NULL;
551 g_signal_connect (plugin, "activated",
552 G_CALLBACK (on_plugin_activated), handle);
553 g_signal_connect (plugin, "deactivated",
554 G_CALLBACK (on_plugin_deactivated), handle);
556 return plugin;
560 * anjuta_plugin_manager_unload_all_plugins:
561 * @plugin_manager: A #AnjutaPluginManager object
563 * Unload all plugins. Do not take care of the dependencies because all plugins
564 * are unloaded anyway.
566 void
567 anjuta_plugin_manager_unload_all_plugins (AnjutaPluginManager *plugin_manager)
569 AnjutaPluginManagerPriv *priv;
571 priv = plugin_manager->priv;
572 if (g_hash_table_size (priv->activated_plugins) > 0 ||
573 g_hash_table_size (priv->plugins_cache) > 0)
575 if (g_hash_table_size (priv->activated_plugins) > 0)
577 GList *node;
578 for (node = g_list_last (priv->available_plugins); node; node = g_list_previous (node))
580 AnjutaPluginHandle *selected_plugin = node->data;
581 AnjutaPlugin *plugin;
583 plugin = g_hash_table_lookup (priv->activated_plugins, selected_plugin);
584 if (plugin)
586 DEBUG_PRINT ("Deactivating plugin: %s",
587 anjuta_plugin_handle_get_id (selected_plugin));
588 anjuta_plugin_deactivate (plugin);
591 g_hash_table_remove_all (priv->activated_plugins);
593 if (g_hash_table_size (priv->plugins_cache) > 0)
595 GList *node;
597 for (node = g_list_last (priv->available_plugins); node; node = g_list_previous (node))
599 AnjutaPluginHandle *selected_plugin = node->data;
601 g_hash_table_remove (priv->plugins_cache, selected_plugin);
603 g_hash_table_remove_all (priv->plugins_cache);
608 /* Return true if plugin should be unloaded when plugin_to_unloaded is unloaded.
609 * It can be because plugin is or need plugin_to_unload. */
610 static gboolean
611 should_unload (GHashTable *activated_plugins, AnjutaPluginHandle *plugin_to_unload,
612 AnjutaPluginHandle *plugin)
614 GObject *plugin_obj = g_hash_table_lookup (activated_plugins, plugin);
616 if (!plugin_obj)
617 return FALSE;
619 if (plugin_to_unload == plugin)
620 return TRUE;
622 gboolean dependent =
623 GPOINTER_TO_INT (g_hash_table_lookup (anjuta_plugin_handle_get_dependents (plugin_to_unload),
624 plugin));
625 return dependent;
628 /* Return true if plugin should be loaded when plugin_to_loaded is loaded.
629 * It can be because plugin_to_load is or need plugin. */
630 static gboolean
631 should_load (GHashTable *activated_plugins, AnjutaPluginHandle *plugin_to_load,
632 AnjutaPluginHandle *plugin)
634 GObject *plugin_obj = g_hash_table_lookup (activated_plugins, plugin);
636 if (plugin_obj)
637 return FALSE;
639 if (plugin_to_load == plugin)
640 return anjuta_plugin_handle_get_can_load (plugin);
642 gboolean dependency =
643 GPOINTER_TO_INT (g_hash_table_lookup (anjuta_plugin_handle_get_dependencies (plugin_to_load),
644 plugin));
645 return (dependency && anjuta_plugin_handle_get_can_load (plugin));
648 static AnjutaPluginHandle *
649 plugin_for_iter (GtkListStore *store, GtkTreeIter *iter)
651 AnjutaPluginHandle *plugin;
653 gtk_tree_model_get (GTK_TREE_MODEL (store), iter, COL_PLUGIN, &plugin, -1);
654 return plugin;
657 static void
658 update_enabled (GtkTreeModel *model, GHashTable *activated_plugins)
660 GtkTreeIter iter;
662 if (gtk_tree_model_get_iter_first (model, &iter)) {
663 do {
664 AnjutaPluginHandle *plugin;
665 GObject *plugin_obj;
666 gboolean installed;
668 plugin = plugin_for_iter(GTK_LIST_STORE(model), &iter);
669 plugin_obj = g_hash_table_lookup (activated_plugins, plugin);
670 installed = (plugin_obj != NULL) ? TRUE : FALSE;
671 gtk_tree_model_get (model, &iter, COL_PLUGIN, &plugin, -1);
672 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
673 COL_ENABLED, installed, -1);
674 } while (gtk_tree_model_iter_next (model, &iter));
678 static void
679 plugin_set_update (AnjutaPluginManager *plugin_manager,
680 AnjutaPluginHandle* selected_plugin,
681 gboolean load)
683 AnjutaPluginManagerPriv *priv;
684 gboolean loaded;
685 GList *l;
687 priv = plugin_manager->priv;
689 /* Plugins can be loaded or unloaded implicitely because they need or are
690 * needed by another plugin so it is possible that we try to load or unload
691 * respectively an already loaded or already unloaded plugin. */
692 loaded = g_hash_table_lookup (priv->activated_plugins, selected_plugin) != NULL;
693 if ((load && loaded) || (!load && !loaded)) return;
695 if (priv->status)
696 anjuta_status_busy_push (priv->status);
698 if (!load)
700 /* visit priv->available_plugins in reverse order when unloading, so
701 * that plugins are unloaded in the right order */
702 for (l = g_list_last(priv->available_plugins); l != NULL; l = l->prev)
704 AnjutaPluginHandle *plugin = l->data;
705 if (should_unload (priv->activated_plugins, selected_plugin, plugin))
707 AnjutaPlugin *plugin_obj = ANJUTA_PLUGIN (g_hash_table_lookup (priv->activated_plugins, plugin));
708 if (!anjuta_plugin_deactivate (plugin_obj))
710 anjuta_util_dialog_info (GTK_WINDOW (priv->shell),
711 dgettext (GETTEXT_PACKAGE, "Plugin '%s' does not want to be deactivated"),
712 anjuta_plugin_handle_get_name (plugin));
717 else
719 for (l = priv->available_plugins; l != NULL; l = l->next)
721 AnjutaPluginHandle *plugin = l->data;
722 if (should_load (priv->activated_plugins, selected_plugin, plugin))
724 AnjutaPlugin *plugin_obj;
725 GError *error = NULL;
726 plugin_obj = g_hash_table_lookup (priv->plugins_cache, plugin);
727 if (plugin_obj)
728 g_object_ref (plugin_obj);
729 else
731 plugin_obj = activate_plugin (plugin_manager, plugin,
732 &error);
735 if (plugin_obj)
737 anjuta_plugin_activate (ANJUTA_PLUGIN (plugin_obj));
738 g_object_unref (plugin_obj);
740 else
742 if (error)
744 gchar* message = g_strdup_printf (dgettext (GETTEXT_PACKAGE, "Could not load %s\n"
745 "This usually means that your installation is corrupted. The "
746 "error message leading to this was:\n%s"),
747 anjuta_plugin_handle_get_name (selected_plugin),
748 error->message);
749 anjuta_util_dialog_error (GTK_WINDOW(plugin_manager->priv->shell),
750 message);
751 g_error_free (error);
752 g_free(message);
758 if (priv->status)
759 anjuta_status_busy_pop (priv->status);
761 return;
764 static void
765 plugin_toggled (GtkCellRendererToggle *cell, char *path_str, gpointer data)
767 AnjutaPluginManager *plugin_manager;
768 AnjutaPluginManagerPriv *priv;
769 GtkListStore *store = GTK_LIST_STORE (data);
770 GtkTreeIter iter;
771 GtkTreePath *path;
772 AnjutaPluginHandle *plugin;
773 gboolean enabled;
774 GList *activated_plugins;
775 GList *node;
776 AnjutaPlugin* plugin_object;
778 path = gtk_tree_path_new_from_string (path_str);
780 plugin_manager = g_object_get_data (G_OBJECT (store), "plugin-manager");
781 priv = plugin_manager->priv;
783 gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path);
784 gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
785 COL_ENABLED, &enabled,
786 COL_PLUGIN, &plugin,
787 -1);
789 /* Activate one plugin can force the loading of other ones, instead of
790 * searching which plugins have to be activated, we just unmerge all
791 * current plugins and merge all plugins after the modification */
793 /* unmerge all plugins */
794 activated_plugins = g_hash_table_get_values (priv->activated_plugins);
795 for (node = g_list_first (activated_plugins); node != NULL; node = g_list_next (node))
797 plugin_object = (AnjutaPlugin *)node->data;
798 if (plugin_object &&
799 IANJUTA_IS_PREFERENCES(plugin_object))
801 ianjuta_preferences_unmerge (IANJUTA_PREFERENCES (plugin_object),
802 anjuta_shell_get_preferences (ANJUTA_SHELL (priv->shell), NULL),
803 NULL);
806 g_list_free (activated_plugins);
808 plugin_set_update (plugin_manager, plugin, !enabled);
810 /* Make sure that it appears in the preferences. This method
811 can only be called when the preferences dialog is active so
812 it should be save
814 activated_plugins = g_hash_table_get_values (priv->activated_plugins);
815 for (node = g_list_first (activated_plugins); node != NULL; node = g_list_next (node))
817 plugin_object = (AnjutaPlugin *)node->data;
818 if (plugin_object &&
819 IANJUTA_IS_PREFERENCES(plugin_object))
821 ianjuta_preferences_merge (IANJUTA_PREFERENCES (plugin_object),
822 anjuta_shell_get_preferences (ANJUTA_SHELL (priv->shell), NULL),
823 NULL);
826 g_list_free (activated_plugins);
828 update_enabled (GTK_TREE_MODEL (store), priv->activated_plugins);
829 gtk_tree_path_free (path);
832 #if 0
833 static void
834 selection_changed (GtkTreeSelection *selection, GtkListStore *store)
836 GtkTreeIter iter;
838 if (gtk_tree_selection_get_selected (selection, NULL,
839 &iter)) {
840 GtkTextBuffer *buffer;
842 GtkWidget *txt = g_object_get_data (G_OBJECT (store),
843 "AboutText");
845 GtkWidget *image = g_object_get_data (G_OBJECT (store),
846 "Icon");
847 AnjutaPluginHandle *plugin = plugin_for_iter (store, &iter);
849 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (txt));
850 gtk_text_buffer_set_text (buffer, plugin->about, -1);
852 if (plugin->icon_path) {
853 gtk_image_set_from_file (GTK_IMAGE (image),
854 plugin->icon_path);
855 gtk_widget_show (GTK_WIDGET (image));
856 } else {
857 gtk_widget_hide (GTK_WIDGET (image));
861 #endif
863 static GtkWidget *
864 create_plugin_tree (void)
866 GtkListStore *store;
867 GtkWidget *tree;
868 GtkCellRenderer *renderer;
869 GtkTreeViewColumn *column;
871 store = gtk_list_store_new (N_COLS,
872 G_TYPE_BOOLEAN,
873 G_TYPE_BOOLEAN,
874 GDK_TYPE_PIXBUF,
875 G_TYPE_STRING,
876 G_TYPE_POINTER);
877 tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
879 renderer = gtk_cell_renderer_toggle_new ();
880 g_signal_connect (G_OBJECT (renderer), "toggled",
881 G_CALLBACK (plugin_toggled), store);
882 column = gtk_tree_view_column_new_with_attributes (dgettext (GETTEXT_PACKAGE, "Load"),
883 renderer,
884 "active",
885 COL_ENABLED,
886 "activatable",
887 COL_ACTIVABLE,
888 NULL);
889 gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
890 gtk_tree_view_column_set_sizing (column,
891 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
893 column = gtk_tree_view_column_new ();
894 renderer = gtk_cell_renderer_pixbuf_new ();
895 gtk_tree_view_column_pack_start (column, renderer, FALSE);
896 gtk_tree_view_column_add_attribute (column, renderer, "pixbuf",
897 COL_ICON);
898 renderer = gtk_cell_renderer_text_new ();
899 g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
900 gtk_tree_view_column_pack_start (column, renderer, TRUE);
901 gtk_tree_view_column_add_attribute (column, renderer, "markup",
902 COL_NAME);
903 gtk_tree_view_column_set_sizing (column,
904 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
905 gtk_tree_view_column_set_title (column, dgettext (GETTEXT_PACKAGE, "Available Plugins"));
906 gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
907 gtk_tree_view_set_expander_column (GTK_TREE_VIEW (tree), column);
909 g_object_unref (store);
910 return tree;
913 /* Sort function for plugins */
914 static gint
915 sort_plugins(gconstpointer a, gconstpointer b)
917 g_return_val_if_fail (a != NULL, 0);
918 g_return_val_if_fail (b != NULL, 0);
920 AnjutaPluginHandle* plugin_a = ANJUTA_PLUGIN_HANDLE (a);
921 AnjutaPluginHandle* plugin_b = ANJUTA_PLUGIN_HANDLE (b);
923 return strcmp (anjuta_plugin_handle_get_name (plugin_a),
924 anjuta_plugin_handle_get_name (plugin_b));
927 /* If show_all == FALSE, show only user activatable plugins
928 * If show_all == TRUE, show all plugins
930 static void
931 populate_plugin_model (AnjutaPluginManager *plugin_manager,
932 GtkListStore *store,
933 GHashTable *plugins_to_show,
934 GHashTable *activated_plugins,
935 gboolean show_all)
937 AnjutaPluginManagerPriv *priv;
938 GList *sorted_plugins, *l;
940 priv = plugin_manager->priv;
941 gtk_list_store_clear (store);
943 sorted_plugins = g_list_copy (priv->available_plugins);
944 sorted_plugins = g_list_sort (sorted_plugins, sort_plugins);
946 for (l = sorted_plugins; l != NULL; l = l->next)
948 AnjutaPluginHandle *plugin = l->data;
950 /* If plugins to show is NULL, show all available plugins */
951 if (plugins_to_show == NULL ||
952 g_hash_table_lookup (plugins_to_show, plugin))
955 gboolean enable = FALSE;
956 if (g_hash_table_lookup (activated_plugins, plugin))
957 enable = TRUE;
959 if (anjuta_plugin_handle_get_name (plugin) &&
960 anjuta_plugin_handle_get_description (plugin) &&
961 (anjuta_plugin_handle_get_user_activatable (plugin) ||
962 show_all))
964 GtkTreeIter iter;
965 gchar *text;
967 text = g_markup_printf_escaped ("<span size=\"larger\" weight=\"bold\">%s</span>\n%s",
968 anjuta_plugin_handle_get_name (plugin),
969 anjuta_plugin_handle_get_about (plugin));
971 gtk_list_store_append (store, &iter);
972 gtk_list_store_set (store, &iter,
973 COL_ACTIVABLE,
974 anjuta_plugin_handle_get_user_activatable (plugin),
975 COL_ENABLED, enable,
976 COL_NAME, text,
977 COL_PLUGIN, plugin,
978 -1);
979 if (anjuta_plugin_handle_get_icon_path (plugin))
981 GdkPixbuf *icon;
982 icon = gdk_pixbuf_new_from_file_at_size (anjuta_plugin_handle_get_icon_path (plugin),
983 32, 32, NULL);
984 if (icon) {
985 gtk_list_store_set (store, &iter,
986 COL_ICON, icon, -1);
987 g_object_unref (icon);
990 g_free (text);
995 g_list_free (sorted_plugins);
998 static GtkWidget *
999 create_remembered_plugins_tree (void)
1001 GtkListStore *store;
1002 GtkWidget *tree;
1003 GtkCellRenderer *renderer;
1004 GtkTreeViewColumn *column;
1006 store = gtk_list_store_new (N_REM_COLS, GDK_TYPE_PIXBUF, G_TYPE_STRING,
1007 G_TYPE_STRING);
1008 tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
1010 column = gtk_tree_view_column_new ();
1011 renderer = gtk_cell_renderer_pixbuf_new ();
1012 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1013 gtk_tree_view_column_add_attribute (column, renderer, "pixbuf",
1014 COL_REM_ICON);
1015 renderer = gtk_cell_renderer_text_new ();
1016 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1017 gtk_tree_view_column_add_attribute (column, renderer, "markup",
1018 COL_REM_NAME);
1019 gtk_tree_view_column_set_sizing (column,
1020 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1021 gtk_tree_view_column_set_title (column, dgettext (GETTEXT_PACKAGE, "Preferred plugins"));
1022 gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
1023 gtk_tree_view_set_expander_column (GTK_TREE_VIEW (tree), column);
1025 g_object_unref (store);
1026 return tree;
1029 static void
1030 foreach_remembered_plugin (gpointer key, gpointer value, gpointer user_data)
1032 AnjutaPluginDescription *desc = (AnjutaPluginDescription *) value;
1033 GtkListStore *store = GTK_LIST_STORE (user_data);
1034 AnjutaPluginManager *manager = g_object_get_data (G_OBJECT (store),
1035 "plugin-manager");
1036 AnjutaPluginHandle *plugin =
1037 g_hash_table_lookup (manager->priv->plugins_by_description, desc);
1038 g_return_if_fail (plugin != NULL);
1040 if (anjuta_plugin_handle_get_name (plugin) &&
1041 anjuta_plugin_handle_get_description (plugin))
1043 GtkTreeIter iter;
1044 gchar *text;
1046 text = g_markup_printf_escaped ("<span size=\"larger\" weight=\"bold\">%s</span>\n%s",
1047 anjuta_plugin_handle_get_name (plugin),
1048 anjuta_plugin_handle_get_about (plugin));
1050 gtk_list_store_append (store, &iter);
1051 gtk_list_store_set (store, &iter,
1052 COL_REM_NAME, text,
1053 COL_REM_PLUGIN_KEY, key,
1054 -1);
1055 if (anjuta_plugin_handle_get_icon_path (plugin))
1057 GdkPixbuf *icon;
1058 icon = gdk_pixbuf_new_from_file_at_size (anjuta_plugin_handle_get_icon_path (plugin),
1059 32, 32, NULL);
1060 if (icon) {
1061 gtk_list_store_set (store, &iter,
1062 COL_REM_ICON, icon, -1);
1063 g_object_unref (icon);
1066 g_free (text);
1070 static void
1071 populate_remembered_plugins_model (AnjutaPluginManager *plugin_manager,
1072 GtkListStore *store)
1074 AnjutaPluginManagerPriv *priv = plugin_manager->priv;
1075 gtk_list_store_clear (store);
1076 g_hash_table_foreach (priv->remember_plugins, foreach_remembered_plugin,
1077 store);
1080 static void
1081 on_show_all_plugins_toggled (GtkToggleButton *button, GtkListStore *store)
1083 AnjutaPluginManager *plugin_manager;
1085 plugin_manager = g_object_get_data (G_OBJECT (button), "__plugin_manager");
1087 populate_plugin_model (plugin_manager, store, NULL,
1088 plugin_manager->priv->activated_plugins,
1089 !gtk_toggle_button_get_active (button));
1092 static void
1093 on_forget_plugin_clicked (GtkWidget *button, GtkTreeView *view)
1095 GtkTreeIter iter;
1096 GtkTreeModel *model;
1097 GtkTreeSelection *selection = gtk_tree_view_get_selection (view);
1098 if (gtk_tree_selection_get_selected (selection, &model, &iter))
1100 gchar *plugin_key;
1101 AnjutaPluginManager *manager = g_object_get_data (G_OBJECT (model),
1102 "plugin-manager");
1103 gtk_tree_model_get (model, &iter, COL_REM_PLUGIN_KEY, &plugin_key, -1);
1104 g_hash_table_remove (manager->priv->remember_plugins, plugin_key);
1105 gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
1106 g_free (plugin_key);
1110 static void
1111 on_forget_plugin_sel_changed (GtkTreeSelection *selection,
1112 GtkWidget *button)
1114 GtkTreeIter iter;
1116 if (gtk_tree_selection_get_selected (selection, NULL, &iter))
1117 gtk_widget_set_sensitive (button, TRUE);
1118 else
1119 gtk_widget_set_sensitive (button, FALSE);
1122 GtkWidget *
1123 anjuta_plugin_manager_get_plugins_page (AnjutaPluginManager *plugin_manager)
1125 GtkWidget *vbox;
1126 GtkWidget *checkbutton;
1127 GtkWidget *tree;
1128 GtkWidget *scrolled;
1129 GtkWidget *toolbar;
1130 GtkToolItem *toolitem;
1131 GtkListStore *store;
1133 /* Plugins page */
1134 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1135 gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
1137 scrolled = gtk_scrolled_window_new (NULL, NULL);
1138 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
1139 GTK_SHADOW_IN);
1140 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
1141 GTK_POLICY_NEVER,
1142 GTK_POLICY_AUTOMATIC);
1143 gtk_style_context_set_junction_sides (gtk_widget_get_style_context (scrolled), GTK_JUNCTION_BOTTOM);
1144 gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 0);
1146 toolbar = gtk_toolbar_new ();
1147 gtk_style_context_add_class (gtk_widget_get_style_context (toolbar), GTK_STYLE_CLASS_INLINE_TOOLBAR);
1148 gtk_style_context_set_junction_sides (gtk_widget_get_style_context (toolbar), GTK_JUNCTION_TOP);
1149 gtk_box_pack_start (GTK_BOX (vbox), toolbar, FALSE, FALSE, 0);
1150 gtk_widget_show (toolbar);
1152 toolitem = gtk_tool_item_new ();
1153 gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (toolitem), 0);
1154 gtk_widget_show (GTK_WIDGET(toolitem));
1156 checkbutton = gtk_check_button_new_with_label (dgettext (GETTEXT_PACKAGE, "Only show user activatable plugins"));
1157 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), TRUE);
1158 gtk_container_add (GTK_CONTAINER (toolitem), checkbutton);
1160 tree = create_plugin_tree ();
1161 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (tree), TRUE);
1162 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree), FALSE);
1163 store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (tree)));
1165 populate_plugin_model (plugin_manager, store, NULL,
1166 plugin_manager->priv->activated_plugins, FALSE);
1168 gtk_container_add (GTK_CONTAINER (scrolled), tree);
1169 g_object_set_data (G_OBJECT (store), "plugin-manager", plugin_manager);
1172 g_object_set_data (G_OBJECT (checkbutton), "__plugin_manager", plugin_manager);
1173 g_signal_connect (G_OBJECT (checkbutton), "toggled",
1174 G_CALLBACK (on_show_all_plugins_toggled),
1175 store);
1176 gtk_widget_show_all (vbox);
1177 return vbox;
1180 GtkWidget *
1181 anjuta_plugin_manager_get_remembered_plugins_page (AnjutaPluginManager *plugin_manager)
1183 GtkWidget *vbox;
1184 GtkWidget *tree;
1185 GtkWidget *scrolled;
1186 GtkListStore *store;
1187 GtkWidget *hbox;
1188 GtkWidget *display_label;
1189 GtkWidget *forget_button;
1190 GtkTreeSelection *selection;
1192 /* Remembered plugin */
1193 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10);
1194 gtk_container_set_border_width (GTK_CONTAINER (vbox), 10);
1196 display_label = gtk_label_new (dgettext (GETTEXT_PACKAGE, "These are the plugins selected by you "
1197 "when you have been prompted to choose one of "
1198 "many suitable plugins. Removing the "
1199 "preferred plugin will let you "
1200 "choose a different plugin."));
1201 gtk_label_set_line_wrap (GTK_LABEL (display_label), TRUE);
1202 gtk_box_pack_start (GTK_BOX (vbox), display_label, FALSE, FALSE, 0);
1204 scrolled = gtk_scrolled_window_new (NULL, NULL);
1205 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
1206 GTK_SHADOW_IN);
1207 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
1208 GTK_POLICY_AUTOMATIC,
1209 GTK_POLICY_AUTOMATIC);
1210 gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 0);
1212 tree = create_remembered_plugins_tree ();
1213 store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (tree)));
1215 gtk_container_add (GTK_CONTAINER (scrolled), tree);
1216 g_object_set_data (G_OBJECT (store), "plugin-manager", plugin_manager);
1217 populate_remembered_plugins_model (plugin_manager, store);
1219 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1220 gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
1221 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
1222 forget_button = gtk_button_new_with_label (dgettext (GETTEXT_PACKAGE, "Forget selected plugin"));
1223 gtk_widget_set_sensitive (forget_button, FALSE);
1224 gtk_box_pack_end (GTK_BOX (hbox), forget_button, FALSE, FALSE, 0);
1226 g_signal_connect (forget_button, "clicked",
1227 G_CALLBACK (on_forget_plugin_clicked),
1228 tree);
1229 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree));
1230 g_signal_connect (selection, "changed",
1231 G_CALLBACK (on_forget_plugin_sel_changed),
1232 forget_button);
1233 gtk_widget_show_all (vbox);
1234 return vbox;
1237 static GList *
1238 property_to_list (const char *value)
1240 GList *l = NULL;
1241 char **split_str;
1242 char **p;
1244 split_str = g_strsplit (value, ",", -1);
1245 for (p = split_str; *p != NULL; p++) {
1246 l = g_list_prepend (l, g_strdup (g_strstrip (*p)));
1248 g_strfreev (split_str);
1249 return l;
1252 static IAnjutaPluginFactory*
1253 get_plugin_factory (AnjutaPluginManager *plugin_manager,
1254 const gchar *language,
1255 GError **error)
1257 AnjutaPluginManagerPriv *priv;
1258 AnjutaPluginHandle *plugin;
1259 GList *loader_plugins, *node;
1260 GList *valid_plugins;
1261 GObject *obj = NULL;
1263 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), G_TYPE_INVALID);
1266 if ((language == NULL) || (g_ascii_strcasecmp (language, "C") == 0))
1268 /* Support of C plugin is built-in */
1269 return IANJUTA_PLUGIN_FACTORY (anjuta_plugin_factory);
1272 priv = plugin_manager->priv;
1273 plugin = NULL;
1275 /* Find all plugins implementing the IAnjutaPluginLoader interface. */
1276 loader_plugins = g_hash_table_lookup (priv->plugins_by_interfaces, "IAnjutaPluginLoader");
1278 /* Create a list of loader supporting this language */
1279 node = loader_plugins;
1280 valid_plugins = NULL;
1281 while (node)
1283 AnjutaPluginDescription *desc;
1284 gchar *val;
1285 GList *vals = NULL;
1286 GList *l_node;
1287 gboolean found;
1289 plugin = node->data;
1291 desc = anjuta_plugin_handle_get_description (plugin);
1292 if (anjuta_plugin_description_get_string (desc, "Plugin Loader", "SupportedLanguage", &val))
1294 if (val != NULL)
1296 vals = property_to_list (val);
1297 g_free (val);
1301 found = FALSE;
1302 l_node = vals;
1303 while (l_node)
1305 if (!found && (g_ascii_strcasecmp (l_node->data, language) == 0))
1307 found = TRUE;
1309 g_free (l_node->data);
1310 l_node = g_list_next (l_node);
1312 g_list_free (vals);
1314 if (found)
1316 valid_plugins = g_list_prepend (valid_plugins, plugin);
1319 node = g_list_next (node);
1322 /* Find the first installed plugin from the valid plugins */
1323 node = valid_plugins;
1324 while (node)
1326 plugin = node->data;
1327 obj = g_hash_table_lookup (priv->activated_plugins, plugin);
1328 if (obj) break;
1329 node = g_list_next (node);
1332 /* If no plugin is installed yet, do something */
1333 if ((obj == NULL) && valid_plugins && g_list_length (valid_plugins) == 1)
1335 /* If there is just one plugin, consider it selected */
1336 plugin = valid_plugins->data;
1338 /* Install and return it */
1339 plugin_set_update (plugin_manager, plugin, TRUE);
1340 obj = g_hash_table_lookup (priv->activated_plugins, plugin);
1342 else if ((obj == NULL) && valid_plugins)
1344 /* Prompt the user to select one of these plugins */
1346 GList *descs = NULL;
1347 node = valid_plugins;
1348 while (node)
1350 plugin = node->data;
1351 descs = g_list_prepend (descs, anjuta_plugin_handle_get_description (plugin));
1352 node = g_list_next (node);
1354 descs = g_list_reverse (descs);
1355 obj = anjuta_plugin_manager_select_and_activate (plugin_manager,
1356 dgettext (GETTEXT_PACKAGE, "Select a plugin"),
1357 dgettext (GETTEXT_PACKAGE, "Please select a plugin to activate"),
1358 descs);
1359 g_list_free (descs);
1361 g_list_free (valid_plugins);
1363 if (obj != NULL)
1365 return IANJUTA_PLUGIN_FACTORY (obj);
1368 /* No plugin implementing this interface found */
1369 g_set_error (error, ANJUTA_PLUGIN_MANAGER_ERROR,
1370 ANJUTA_PLUGIN_MANAGER_MISSING_FACTORY,
1371 dgettext (GETTEXT_PACKAGE, "No plugin is able to load other plugins in %s"), language);
1373 return NULL;
1376 static void
1377 on_is_active_plugins_foreach (gpointer key, gpointer data, gpointer user_data)
1379 AnjutaPluginHandle *handle = ANJUTA_PLUGIN_HANDLE (key);
1380 gchar const **search_iface = (gchar const **)user_data;
1382 if (*search_iface != NULL)
1384 GList *interfaces;
1385 GList *found;
1387 interfaces = anjuta_plugin_handle_get_interfaces (handle);
1389 for (found = g_list_first (interfaces); found != NULL; found = g_list_next (found))
1393 found = g_list_find_custom (interfaces, *search_iface, (GCompareFunc)strcmp);
1395 if (found != NULL) *search_iface = NULL;
1400 * anjuta_plugin_manager_is_active_plugin:
1401 * @plugin_manager: A #AnjutaPluginManager object
1402 * @iface_name: The interface implemented by the object to be found
1404 * Searches if a currently loaded plugins implements
1405 * the given interface.
1407 * Return value: %TRUE is the plugin is currently loaded.
1410 gboolean
1411 anjuta_plugin_manager_is_active_plugin (AnjutaPluginManager *plugin_manager,
1412 const gchar *iface_name)
1414 const gchar *search_iface = iface_name;
1416 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), FALSE);
1418 g_hash_table_foreach (plugin_manager->priv->activated_plugins,
1419 on_is_active_plugins_foreach,
1420 &search_iface);
1422 return search_iface == NULL;
1426 * anjuta_plugin_manager_get_plugin:
1427 * @plugin_manager: A #AnjutaPluginManager object
1428 * @iface_name: The interface implemented by the object to be found
1430 * Searches the currently available plugins to find the one which
1431 * implements the given interface as primary interface and returns it. If
1432 * the plugin is not yet loaded, it will be loaded and activated.
1433 * It only searches
1434 * from the pool of plugin objects loaded in this shell and can only search
1435 * by primary interface. If there are more objects implementing this primary
1436 * interface, user might be prompted to select one from them (and might give
1437 * the option to use it as default for future queries). A typical usage of this
1438 * function is:
1439 * <programlisting>
1440 * GObject *docman =
1441 * anjuta_plugin_manager_get_plugin (plugin_manager, "IAnjutaDocumentManager", error);
1442 * </programlisting>
1443 * Notice that this function takes the interface name string as string, unlike
1444 * anjuta_plugins_get_interface() which takes the type directly.
1445 * If no plugin implementing this interface can be found, returns %NULL.
1447 * Return value: The plugin object (subclass of #AnjutaPlugin) which implements
1448 * the given interface or %NULL. See #AnjutaPlugin for more detail on interfaces
1449 * implemented by plugins.
1451 GObject *
1452 anjuta_plugin_manager_get_plugin (AnjutaPluginManager *plugin_manager,
1453 const gchar *iface_name)
1455 AnjutaPluginManagerPriv *priv;
1456 AnjutaPluginHandle *plugin;
1457 GList *valid_plugins, *node;
1459 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
1460 g_return_val_if_fail (iface_name != NULL, NULL);
1462 priv = plugin_manager->priv;
1463 plugin = NULL;
1465 /* Find all plugins implementing this (primary) interface. */
1466 valid_plugins = g_hash_table_lookup (priv->plugins_by_interfaces, iface_name);
1468 /* Find the first installed plugin from the valid plugins */
1469 node = valid_plugins;
1470 while (node)
1472 GObject *obj;
1473 plugin = node->data;
1474 obj = g_hash_table_lookup (priv->activated_plugins, plugin);
1475 if (obj)
1476 return obj;
1477 node = g_list_next (node);
1480 /* If no plugin is installed yet, do something */
1481 if (valid_plugins && g_list_length (valid_plugins) == 1)
1483 /* If there is just one plugin, consider it selected */
1484 GObject *obj;
1485 plugin = valid_plugins->data;
1487 /* Install and return it */
1488 plugin_set_update (plugin_manager, plugin, TRUE);
1489 obj = g_hash_table_lookup (priv->activated_plugins, plugin);
1491 return obj;
1493 else if (valid_plugins)
1495 /* Prompt the user to select one of these plugins */
1496 GObject *obj;
1497 GList *descs = NULL;
1498 node = valid_plugins;
1499 while (node)
1501 plugin = node->data;
1502 descs = g_list_prepend (descs, anjuta_plugin_handle_get_description (plugin));
1503 node = g_list_next (node);
1505 descs = g_list_reverse (descs);
1506 obj = anjuta_plugin_manager_select_and_activate (plugin_manager,
1507 dgettext (GETTEXT_PACKAGE, "Select a plugin"),
1508 dgettext (GETTEXT_PACKAGE, "<b>Please select a plugin to activate</b>"),
1509 descs);
1510 g_list_free (descs);
1511 return obj;
1514 /* No plugin implementing this interface found */
1515 return NULL;
1519 * anjuta_plugin_manager_get_plugin_by_id:
1520 * @plugin_manager: A #AnjutaPluginManager object
1521 * @plugin_id: The plugin id
1523 * Searches the currently available plugins to find the one with the
1524 * specified identifier. If the plugin is not yet loaded, it will be loaded
1525 * and activated.
1527 * Return value: The plugin object (subclass of #AnjutaPlugin)
1529 GObject *
1530 anjuta_plugin_manager_get_plugin_by_id (AnjutaPluginManager *plugin_manager,
1531 const gchar *plugin_id)
1533 AnjutaPluginManagerPriv *priv;
1534 AnjutaPluginHandle *plugin;
1536 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
1537 g_return_val_if_fail (plugin_id != NULL, NULL);
1539 priv = plugin_manager->priv;
1540 plugin = g_hash_table_lookup (priv->plugins_by_name, plugin_id);
1541 if (plugin)
1543 GObject *obj;
1544 obj = g_hash_table_lookup (priv->activated_plugins, plugin);
1545 if (obj)
1547 return obj;
1548 } else
1550 plugin_set_update (plugin_manager, plugin, TRUE);
1551 obj = g_hash_table_lookup (priv->activated_plugins, plugin);
1552 return obj;
1555 g_warning ("No plugin found with id \"%s\".", plugin_id);
1556 return NULL;
1559 static void
1560 on_activated_plugins_foreach (gpointer key, gpointer data, gpointer user_data)
1562 AnjutaPluginHandle *plugin = ANJUTA_PLUGIN_HANDLE (key);
1563 GList **active_plugins = (GList **)user_data;
1564 *active_plugins = g_list_prepend (*active_plugins,
1565 anjuta_plugin_handle_get_description (plugin));
1568 static void
1569 on_activated_plugin_objects_foreach (gpointer key, gpointer data, gpointer user_data)
1571 GList **active_plugins = (GList **)user_data;
1572 *active_plugins = g_list_prepend (*active_plugins,
1573 data);
1576 GList*
1577 anjuta_plugin_manager_get_active_plugins (AnjutaPluginManager *plugin_manager)
1579 GList *active_plugins = NULL;
1581 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
1582 g_hash_table_foreach (plugin_manager->priv->activated_plugins,
1583 on_activated_plugins_foreach,
1584 &active_plugins);
1585 return g_list_reverse (active_plugins);
1588 GList*
1589 anjuta_plugin_manager_get_active_plugin_objects (AnjutaPluginManager *plugin_manager)
1591 GList *active_plugins = NULL;
1593 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
1594 g_hash_table_foreach (plugin_manager->priv->activated_plugins,
1595 on_activated_plugin_objects_foreach,
1596 &active_plugins);
1597 return g_list_reverse (active_plugins);
1601 * anjuta_plugin_manager_unload_plugin_by_id:
1602 * @plugin_manager: A #AnjutaPluginManager object
1603 * @plugin_id: The plugin identifier
1605 * Unload the plugin corresponding to the given identifier. If the plugin is
1606 * already unloaded, nothing will be done.
1608 * Return value: %TRUE is the plugin is unloaded. %FALSE if a corresponding
1609 * plugin does not exist or if the plugin cannot be unloaded.
1611 gboolean
1612 anjuta_plugin_manager_unload_plugin_by_id (AnjutaPluginManager *plugin_manager,
1613 const gchar *plugin_id)
1615 AnjutaPluginManagerPriv *priv;
1616 AnjutaPluginHandle *plugin;
1618 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), FALSE);
1619 g_return_val_if_fail (plugin_id != NULL, FALSE);
1621 priv = plugin_manager->priv;
1623 plugin = g_hash_table_lookup (priv->plugins_by_name, plugin_id);
1624 if (plugin)
1626 plugin_set_update (plugin_manager, plugin, FALSE);
1628 /* Check if the plugin has been indeed unloaded */
1629 if (!g_hash_table_lookup (priv->activated_plugins, plugin))
1630 return TRUE;
1631 else
1632 return FALSE;
1634 g_warning ("No plugin found with id \"%s\".", plugin_id);
1635 return FALSE;
1638 static gboolean
1639 find_plugin_for_object (gpointer key, gpointer value, gpointer data)
1641 if (value == data)
1643 g_object_set_data (G_OBJECT (data), "__plugin_plugin", key);
1644 return TRUE;
1646 return FALSE;
1650 * anjuta_plugin_manager_unload_plugin:
1651 * @plugin_manager: A #AnjutaPluginManager object
1652 * @plugin_object: A #AnjutaPlugin object
1654 * Unload the corresponding plugin. The plugin has to be loaded.
1656 * Return value: %TRUE if the plugin has been unloaded. %FALSE if the plugin is
1657 * already or cannot be unloaded.
1659 gboolean
1660 anjuta_plugin_manager_unload_plugin (AnjutaPluginManager *plugin_manager,
1661 GObject *plugin_object)
1663 AnjutaPluginManagerPriv *priv;
1664 AnjutaPluginHandle *plugin;
1666 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), FALSE);
1667 g_return_val_if_fail (ANJUTA_IS_PLUGIN (plugin_object), FALSE);
1669 priv = plugin_manager->priv;
1671 plugin = NULL;
1673 /* Find the plugin that correspond to this plugin object */
1674 g_hash_table_find (priv->activated_plugins, find_plugin_for_object,
1675 plugin_object);
1676 plugin = g_object_get_data (G_OBJECT (plugin_object), "__plugin_plugin");
1678 if (plugin)
1680 plugin_set_update (plugin_manager, plugin, FALSE);
1682 /* Check if the plugin has been indeed unloaded */
1683 if (!g_hash_table_lookup (priv->activated_plugins, plugin))
1684 return TRUE;
1685 else
1686 return FALSE;
1688 g_warning ("No plugin found with object \"%p\".", plugin_object);
1689 return FALSE;
1692 GList*
1693 anjuta_plugin_manager_list_query (AnjutaPluginManager *plugin_manager,
1694 GList *secs,
1695 GList *anames,
1696 GList *avalues)
1698 AnjutaPluginManagerPriv *priv;
1699 GList *selected_plugins = NULL;
1700 const gchar *sec;
1701 const gchar *aname;
1702 const gchar *avalue;
1703 GList *available;
1705 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
1707 priv = plugin_manager->priv;
1708 available = priv->available_plugins;
1710 if (secs == NULL)
1712 /* If no query is given, select all plugins */
1713 while (available)
1715 AnjutaPluginHandle *plugin = available->data;
1716 AnjutaPluginDescription *desc =
1717 anjuta_plugin_handle_get_description (plugin);
1718 selected_plugins = g_list_prepend (selected_plugins, desc);
1719 available = g_list_next (available);
1721 return g_list_reverse (selected_plugins);
1724 g_return_val_if_fail (secs != NULL, NULL);
1725 g_return_val_if_fail (anames != NULL, NULL);
1726 g_return_val_if_fail (avalues != NULL, NULL);
1728 while (available)
1730 GList* s_node = secs;
1731 GList* n_node = anames;
1732 GList* v_node = avalues;
1734 gboolean satisfied = FALSE;
1736 AnjutaPluginHandle *plugin = available->data;
1737 AnjutaPluginDescription *desc =
1738 anjuta_plugin_handle_get_description (plugin);
1740 while (s_node)
1742 gchar *val;
1743 GList *vals;
1744 GList *node;
1745 gboolean found = FALSE;
1747 satisfied = TRUE;
1749 sec = s_node->data;
1750 aname = n_node->data;
1751 avalue = v_node->data;
1753 if (!anjuta_plugin_description_get_string (desc, sec, aname, &val))
1755 satisfied = FALSE;
1756 break;
1759 vals = property_to_list (val);
1760 g_free (val);
1762 node = vals;
1763 while (node)
1765 if (strchr(node->data, '*') != NULL)
1767 // Star match.
1768 gchar **segments;
1769 gchar **seg_ptr;
1770 const gchar *cursor;
1772 segments = g_strsplit (node->data, "*", -1);
1774 seg_ptr = segments;
1775 cursor = avalue;
1776 while (*seg_ptr != NULL)
1778 if (strlen (*seg_ptr) > 0) {
1779 cursor = strstr (cursor, *seg_ptr);
1780 if (cursor == NULL)
1781 break;
1783 cursor += strlen (*seg_ptr);
1784 seg_ptr++;
1786 if (*seg_ptr == NULL)
1787 found = TRUE;
1788 g_strfreev (segments);
1790 else if (g_ascii_strcasecmp (node->data, avalue) == 0)
1792 // String match.
1793 found = TRUE;
1795 g_free (node->data);
1796 node = g_list_next (node);
1798 g_list_free (vals);
1799 if (!found)
1801 satisfied = FALSE;
1802 break;
1804 s_node = g_list_next (s_node);
1805 n_node = g_list_next (n_node);
1806 v_node = g_list_next (v_node);
1808 if (satisfied)
1810 selected_plugins = g_list_prepend (selected_plugins, desc);
1811 /* DEBUG_PRINT ("Satisfied, Adding %s",
1812 anjuta_plugin_handle_get_name (plugin));*/
1814 available = g_list_next (available);
1817 return g_list_reverse (selected_plugins);
1820 GList*
1821 anjuta_plugin_manager_query (AnjutaPluginManager *plugin_manager,
1822 const gchar *section_name,
1823 const gchar *attribute_name,
1824 const gchar *attribute_value,
1825 ...)
1827 va_list var_args;
1828 GList *secs = NULL;
1829 GList *anames = NULL;
1830 GList *avalues = NULL;
1831 const gchar *sec;
1832 const gchar *aname;
1833 const gchar *avalue;
1834 GList *selected_plugins;
1837 if (section_name == NULL)
1839 /* If no query is given, select all plugins */
1840 return anjuta_plugin_manager_list_query (plugin_manager, NULL, NULL, NULL);
1843 g_return_val_if_fail (section_name != NULL, NULL);
1844 g_return_val_if_fail (attribute_name != NULL, NULL);
1845 g_return_val_if_fail (attribute_value != NULL, NULL);
1847 secs = g_list_prepend (secs, g_strdup (section_name));
1848 anames = g_list_prepend (anames, g_strdup (attribute_name));
1849 avalues = g_list_prepend (avalues, g_strdup (attribute_value));
1851 va_start (var_args, attribute_value);
1854 sec = va_arg (var_args, const gchar *);
1855 if (sec)
1857 aname = va_arg (var_args, const gchar *);
1858 if (aname)
1860 avalue = va_arg (var_args, const gchar *);
1861 if (avalue)
1863 secs = g_list_prepend (secs, g_strdup (sec));
1864 anames = g_list_prepend (anames, g_strdup (aname));
1865 avalues = g_list_prepend (avalues, g_strdup (avalue));
1870 while (sec);
1871 va_end (var_args);
1873 secs = g_list_reverse (secs);
1874 anames = g_list_reverse (anames);
1875 avalues = g_list_reverse (avalues);
1877 selected_plugins = anjuta_plugin_manager_list_query (plugin_manager,
1878 secs,
1879 anames,
1880 avalues);
1882 anjuta_util_glist_strings_free (secs);
1883 anjuta_util_glist_strings_free (anames);
1884 anjuta_util_glist_strings_free (avalues);
1886 return selected_plugins;
1889 enum {
1890 PIXBUF_COLUMN,
1891 PLUGIN_COLUMN,
1892 PLUGIN_DESCRIPTION_COLUMN,
1893 N_COLUMNS
1896 static void
1897 on_plugin_list_row_activated (GtkTreeView *tree_view,
1898 GtkTreePath *path,
1899 GtkTreeViewColumn *column,
1900 GtkDialog *dialog)
1902 gtk_dialog_response (dialog, GTK_RESPONSE_OK);
1906 static void
1907 on_plugin_list_show (GtkTreeView *view,
1908 GtkDirectionType direction,
1909 GtkDialog *dialog)
1911 GtkTreeSelection *selection;
1912 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
1914 g_signal_emit_by_name (G_OBJECT (selection), "changed", GTK_DIALOG(dialog), NULL);
1918 static void
1919 on_plugin_list_selection_changed (GtkTreeSelection *tree_selection,
1920 GtkDialog *dialog)
1922 GtkContainer *action_area;
1923 GList *list;
1924 GtkButton *bt = NULL;
1926 action_area = GTK_CONTAINER (gtk_dialog_get_action_area (dialog));
1927 list = gtk_container_get_children (action_area);
1928 for (; list; list = list->next) {
1929 bt = list->data;
1930 if (!strcmp("gtk-ok", gtk_button_get_label (bt)))
1931 break;
1933 if (bt && gtk_tree_selection_get_selected (tree_selection, NULL, NULL))
1934 gtk_widget_set_sensitive ((GtkWidget *) bt, TRUE);
1935 else
1936 gtk_widget_set_sensitive ((GtkWidget *) bt, FALSE);
1937 g_list_free(list);
1941 * anjuta_plugin_manager_select:
1942 * @plugin_manager: #AnjutaPluginManager object
1943 * @title: Title of the dialog
1944 * @description: label shown on the dialog
1945 * @plugin_descriptions: List of #AnjutaPluginDescription
1947 * Show a dialog where the user can choose between the given plugins
1949 * Returns: The chosen plugin description
1951 AnjutaPluginDescription *
1952 anjuta_plugin_manager_select (AnjutaPluginManager *plugin_manager,
1953 gchar *title, gchar *description,
1954 GList *plugin_descriptions)
1956 AnjutaPluginDescription *desc;
1957 AnjutaPluginManagerPriv *priv;
1958 GtkWidget *dlg;
1959 GtkTreeModel *model;
1960 GtkWidget *view;
1961 GtkTreeViewColumn *column;
1962 GtkCellRenderer *renderer;
1963 GList *node;
1964 GtkWidget *label;
1965 GtkWidget *content_area;
1966 GtkWidget *sc;
1967 GtkWidget *remember_checkbox;
1968 gint response;
1969 GtkTreeIter selected;
1970 GtkTreeSelection *selection;
1971 GtkTreeModel *store;
1972 GList *selection_ids = NULL;
1973 GString *remember_key = g_string_new ("");
1975 g_return_val_if_fail (title != NULL, NULL);
1976 g_return_val_if_fail (description != NULL, NULL);
1977 g_return_val_if_fail (plugin_descriptions != NULL, NULL);
1979 priv = plugin_manager->priv;
1981 if (g_list_length (plugin_descriptions) <= 0)
1982 return NULL;
1984 dlg = gtk_dialog_new_with_buttons (title, GTK_WINDOW (priv->shell),
1985 GTK_DIALOG_DESTROY_WITH_PARENT,
1986 GTK_STOCK_CANCEL,
1987 GTK_RESPONSE_CANCEL,
1988 GTK_STOCK_OK, GTK_RESPONSE_OK,
1989 NULL);
1990 gtk_window_set_default_size (GTK_WINDOW (dlg), 400, 300);
1992 label = gtk_label_new (description);
1993 gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
1994 gtk_widget_show (label);
1995 content_area = gtk_dialog_get_content_area (GTK_DIALOG (dlg));
1996 gtk_box_pack_start (GTK_BOX (content_area), label,
1997 FALSE, FALSE, 5);
1999 sc = gtk_scrolled_window_new (NULL, NULL);
2000 gtk_widget_show (sc);
2001 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sc),
2002 GTK_SHADOW_IN);
2003 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sc),
2004 GTK_POLICY_AUTOMATIC,
2005 GTK_POLICY_AUTOMATIC);
2007 gtk_box_pack_start (GTK_BOX (content_area), sc,
2008 TRUE, TRUE, 5);
2010 model = GTK_TREE_MODEL (gtk_list_store_new (N_COLUMNS, GDK_TYPE_PIXBUF,
2011 G_TYPE_STRING, G_TYPE_POINTER));
2012 view = gtk_tree_view_new_with_model (model);
2013 gtk_widget_show (view);
2014 gtk_container_add (GTK_CONTAINER (sc), view);
2016 column = gtk_tree_view_column_new ();
2017 gtk_tree_view_column_set_sizing (column,
2018 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
2019 gtk_tree_view_column_set_title (column, dgettext (GETTEXT_PACKAGE, "Available Plugins"));
2021 renderer = gtk_cell_renderer_pixbuf_new ();
2022 gtk_tree_view_column_pack_start (column, renderer, FALSE);
2023 gtk_tree_view_column_add_attribute (column, renderer, "pixbuf",
2024 PIXBUF_COLUMN);
2026 renderer = gtk_cell_renderer_text_new ();
2027 gtk_tree_view_column_pack_start (column, renderer, TRUE);
2028 gtk_tree_view_column_add_attribute (column, renderer, "markup",
2029 PLUGIN_COLUMN);
2031 gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
2032 gtk_tree_view_set_expander_column (GTK_TREE_VIEW (view), column);
2034 g_signal_connect (view, "row-activated",
2035 G_CALLBACK (on_plugin_list_row_activated),
2036 GTK_DIALOG(dlg));
2037 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
2038 g_signal_connect(selection, "changed",
2039 G_CALLBACK(on_plugin_list_selection_changed),
2040 GTK_DIALOG(dlg));
2041 g_signal_connect(view, "focus",
2042 G_CALLBACK(on_plugin_list_show),
2043 GTK_DIALOG(dlg));
2045 remember_checkbox =
2046 gtk_check_button_new_with_label (dgettext (GETTEXT_PACKAGE, "Remember this selection"));
2047 gtk_container_set_border_width (GTK_CONTAINER (remember_checkbox), 10);
2048 gtk_widget_show (remember_checkbox);
2049 gtk_box_pack_start (GTK_BOX (content_area), remember_checkbox,
2050 FALSE, FALSE, 0);
2052 node = plugin_descriptions;
2053 while (node)
2055 GdkPixbuf *icon_pixbuf = NULL;
2056 gchar *plugin_name = NULL;
2057 gchar *plugin_desc = NULL;
2058 gchar *icon_filename = NULL;
2059 gchar *location = NULL;
2061 desc = (AnjutaPluginDescription*)node->data;
2063 if (anjuta_plugin_description_get_string (desc,
2064 "Anjuta Plugin",
2065 "Icon",
2066 &icon_filename))
2068 gchar *icon_path = NULL;
2069 icon_path = g_strconcat (PACKAGE_PIXMAPS_DIR"/",
2070 icon_filename, NULL);
2071 g_free (icon_filename);
2072 /* DEBUG_PRINT ("Icon: %s", icon_path); */
2073 icon_pixbuf =
2074 gdk_pixbuf_new_from_file (icon_path, NULL);
2075 if (icon_pixbuf == NULL)
2077 g_warning ("Plugin pixmap not found: %s", plugin_name);
2079 g_free (icon_path);
2081 else
2083 g_warning ("Plugin does not define Icon attribute");
2085 if (!anjuta_plugin_description_get_locale_string (desc,
2086 "Anjuta Plugin",
2087 "Name",
2088 &plugin_name))
2090 g_warning ("Plugin does not define Name attribute");
2092 if (!anjuta_plugin_description_get_locale_string (desc,
2093 "Anjuta Plugin",
2094 "Description",
2095 &plugin_desc))
2097 g_warning ("Plugin does not define Description attribute");
2099 if (plugin_name && plugin_desc)
2101 GtkTreeIter iter;
2102 gchar *text;
2104 if (!anjuta_plugin_description_get_string (desc,
2105 "Anjuta Plugin",
2106 "Location",
2107 &location))
2109 g_warning ("Plugin does not define Location attribute");
2113 text = g_markup_printf_escaped ("<span size=\"larger\" weight=\"bold\">%s</span>\n%s", plugin_name, plugin_desc);
2115 gtk_list_store_append (GTK_LIST_STORE (model), &iter);
2116 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
2117 PLUGIN_COLUMN, text,
2118 PLUGIN_DESCRIPTION_COLUMN, desc, -1);
2119 if (icon_pixbuf) {
2120 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
2121 PIXBUF_COLUMN, icon_pixbuf, -1);
2122 g_object_unref (icon_pixbuf);
2124 g_free (text);
2126 selection_ids = g_list_prepend (selection_ids, location);
2128 g_free (plugin_name);
2129 g_free (plugin_desc);
2130 node = g_list_next (node);
2133 /* Prepare remembering key */
2134 selection_ids = g_list_sort (selection_ids,
2135 (GCompareFunc)strcmp);
2136 node = selection_ids;
2137 while (node)
2139 g_string_append (remember_key, (gchar*)node->data);
2140 g_string_append (remember_key, ",");
2141 node = g_list_next (node);
2143 g_list_foreach (selection_ids, (GFunc) g_free, NULL);
2144 g_list_free (selection_ids);
2146 /* Find if the selection is remembered */
2147 desc = g_hash_table_lookup (priv->remember_plugins, remember_key->str);
2148 if (desc)
2150 g_string_free (remember_key, TRUE);
2151 gtk_widget_destroy (dlg);
2152 return desc;
2155 /* Prompt dialog */
2156 response = gtk_dialog_run (GTK_DIALOG (dlg));
2157 switch (response)
2159 case GTK_RESPONSE_OK:
2160 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
2161 if (gtk_tree_selection_get_selected (selection, &store,
2162 &selected))
2164 gtk_tree_model_get (model, &selected,
2165 PLUGIN_DESCRIPTION_COLUMN, &desc, -1);
2166 if (desc)
2168 /* Remember selection */
2169 if (gtk_toggle_button_get_active
2170 (GTK_TOGGLE_BUTTON (remember_checkbox)))
2172 /* DEBUG_PRINT ("Remembering selection '%s'",
2173 remember_key->str);*/
2174 g_hash_table_insert (priv->remember_plugins,
2175 g_strdup (remember_key->str), desc);
2177 g_string_free (remember_key, TRUE);
2178 gtk_widget_destroy (dlg);
2179 return desc;
2182 break;
2184 g_string_free (remember_key, TRUE);
2185 gtk_widget_destroy (dlg);
2186 return NULL;
2189 GObject*
2190 anjuta_plugin_manager_select_and_activate (AnjutaPluginManager *plugin_manager,
2191 gchar *title,
2192 gchar *description,
2193 GList *plugin_descriptions)
2195 AnjutaPluginDescription *d;
2197 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
2199 d = anjuta_plugin_manager_select (plugin_manager, title, description,
2200 plugin_descriptions);
2201 if (d)
2203 GObject *plugin = NULL;
2204 gchar *location = NULL;
2206 anjuta_plugin_description_get_string (d,
2207 "Anjuta Plugin",
2208 "Location",
2209 &location);
2210 g_return_val_if_fail (location != NULL, NULL);
2211 plugin =
2212 anjuta_plugin_manager_get_plugin_by_id (plugin_manager, location);
2213 g_free (location);
2214 return plugin;
2216 return NULL;
2220 * anjuta_plugin_manager_get_plugin_description:
2221 * @plugin_manager: #AnjutaPluginManager object
2222 * @plugin: #AnjutaPlugin object
2224 * Get the description corresponding to the plugin or %NULL if the plugin is not
2225 * activated.
2227 * Returns: (transfer none) (allow-none): A #AnjutaPluginDescription or %NULL.
2229 AnjutaPluginDescription*
2230 anjuta_plugin_manager_get_plugin_description (AnjutaPluginManager *plugin_manager,
2231 GObject *plugin)
2233 GHashTableIter iter;
2234 gpointer key, value;
2236 g_hash_table_iter_init (&iter, plugin_manager->priv->activated_plugins);
2237 while (g_hash_table_iter_next (&iter, &key, &value))
2239 if (G_OBJECT(value) == plugin)
2241 return anjuta_plugin_handle_get_description (ANJUTA_PLUGIN_HANDLE (key));
2245 return NULL;
2249 /* Plugin manager */
2251 static void
2252 anjuta_plugin_manager_init (AnjutaPluginManager *object)
2254 object->priv = g_new0 (AnjutaPluginManagerPriv, 1);
2255 object->priv->plugins_by_name = g_hash_table_new (g_str_hash, g_str_equal);
2256 object->priv->plugins_by_interfaces = g_hash_table_new_full (g_str_hash,
2257 g_str_equal,
2258 NULL,
2259 (GDestroyNotify) g_list_free);
2260 object->priv->plugins_by_description = g_hash_table_new (g_direct_hash,
2261 g_direct_equal);
2262 object->priv->activated_plugins = g_hash_table_new_full (g_direct_hash, g_direct_equal,
2263 NULL, g_object_unref);
2264 object->priv->plugins_cache = g_hash_table_new_full (g_direct_hash, g_direct_equal,
2265 NULL, g_object_unref);
2266 object->priv->remember_plugins = g_hash_table_new_full (g_str_hash,
2267 g_str_equal,
2268 g_free, NULL);
2271 static void
2272 anjuta_plugin_manager_dispose (GObject *object)
2274 AnjutaPluginManagerPriv *priv;
2275 priv = ANJUTA_PLUGIN_MANAGER (object)->priv;
2277 if (priv->available_plugins)
2279 g_list_foreach (priv->available_plugins, (GFunc)g_object_unref, NULL);
2280 g_list_free (priv->available_plugins);
2281 priv->available_plugins = NULL;
2283 if (priv->activated_plugins)
2285 g_hash_table_destroy (priv->activated_plugins);
2286 priv->activated_plugins = NULL;
2288 if (priv->plugins_cache)
2290 g_hash_table_destroy (priv->plugins_cache);
2291 priv->plugins_cache = NULL;
2293 if (priv->plugins_by_name)
2295 g_hash_table_destroy (priv->plugins_by_name);
2296 priv->plugins_by_name = NULL;
2298 if (priv->plugins_by_description)
2300 g_hash_table_destroy (priv->plugins_by_description);
2301 priv->plugins_by_description = NULL;
2303 if (priv->plugins_by_interfaces)
2305 g_hash_table_destroy (priv->plugins_by_interfaces);
2306 priv->plugins_by_interfaces = NULL;
2308 if (priv->plugin_dirs)
2310 g_list_foreach (priv->plugin_dirs, (GFunc)g_free, NULL);
2311 g_list_free (priv->plugin_dirs);
2312 priv->plugin_dirs = NULL;
2314 #if 0
2315 if (anjuta_c_plugin_factory)
2317 g_object_unref (anjuta_c_plugin_factory);
2318 anjuta_c_plugin_factory = NULL;
2320 #endif
2321 G_OBJECT_CLASS (parent_class)->dispose (object);
2324 static void
2325 anjuta_plugin_manager_set_property (GObject *object, guint prop_id,
2326 const GValue *value, GParamSpec *pspec)
2328 AnjutaPluginManagerPriv *priv;
2330 g_return_if_fail (ANJUTA_IS_PLUGIN_MANAGER (object));
2331 priv = ANJUTA_PLUGIN_MANAGER (object)->priv;
2333 switch (prop_id)
2335 case PROP_STATUS:
2336 priv->status = g_value_get_object (value);
2337 break;
2338 case PROP_SHELL:
2339 priv->shell = g_value_get_object (value);
2340 break;
2341 default:
2342 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2343 break;
2347 static void
2348 anjuta_plugin_manager_get_property (GObject *object, guint prop_id,
2349 GValue *value, GParamSpec *pspec)
2351 AnjutaPluginManagerPriv *priv;
2353 g_return_if_fail (ANJUTA_IS_PLUGIN_MANAGER (object));
2354 priv = ANJUTA_PLUGIN_MANAGER (object)->priv;
2356 switch (prop_id)
2358 case PROP_SHELL:
2359 g_value_set_object (value, priv->shell);
2360 break;
2361 case PROP_STATUS:
2362 g_value_set_object (value, priv->status);
2363 break;
2364 case PROP_AVAILABLE_PLUGINS:
2365 g_value_set_pointer (value, priv->available_plugins);
2366 break;
2367 case PROP_ACTIVATED_PLUGINS:
2368 g_value_set_pointer (value, priv->activated_plugins);
2369 break;
2370 default:
2371 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2372 break;
2375 static void
2376 anjuta_plugin_manager_plugin_activated (AnjutaPluginManager *self,
2377 AnjutaPluginDescription* plugin_desc,
2378 GObject *plugin)
2380 /* TODO: Add default signal handler implementation here */
2383 static void
2384 anjuta_plugin_manager_plugin_deactivated (AnjutaPluginManager *self,
2385 AnjutaPluginDescription* plugin_desc,
2386 GObject *plugin)
2388 /* TODO: Add default signal handler implementation here */
2391 static void
2392 anjuta_plugin_manager_class_init (AnjutaPluginManagerClass *klass)
2394 GObjectClass* object_class = G_OBJECT_CLASS (klass);
2395 parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
2397 object_class->dispose = anjuta_plugin_manager_dispose;
2398 object_class->set_property = anjuta_plugin_manager_set_property;
2399 object_class->get_property = anjuta_plugin_manager_get_property;
2401 klass->plugin_activated = anjuta_plugin_manager_plugin_activated;
2402 klass->plugin_deactivated = anjuta_plugin_manager_plugin_deactivated;
2404 g_object_class_install_property (object_class,
2405 PROP_PROFILES,
2406 g_param_spec_pointer ("profiles",
2407 dgettext (GETTEXT_PACKAGE, "Profiles"),
2408 dgettext (GETTEXT_PACKAGE, "Current stack of profiles"),
2409 G_PARAM_READABLE));
2410 g_object_class_install_property (object_class,
2411 PROP_AVAILABLE_PLUGINS,
2412 g_param_spec_pointer ("available-plugins",
2413 dgettext (GETTEXT_PACKAGE, "Available plugins"),
2414 dgettext (GETTEXT_PACKAGE, "Currently available plugins found in plugin paths"),
2415 G_PARAM_READABLE));
2417 g_object_class_install_property (object_class,
2418 PROP_ACTIVATED_PLUGINS,
2419 g_param_spec_pointer ("activated-plugins",
2420 dgettext (GETTEXT_PACKAGE, "Activated plugins"),
2421 dgettext (GETTEXT_PACKAGE, "Currently activated plugins"),
2422 G_PARAM_READABLE));
2423 g_object_class_install_property (object_class,
2424 PROP_SHELL,
2425 g_param_spec_object ("shell",
2426 dgettext (GETTEXT_PACKAGE, "Anjuta Shell"),
2427 dgettext (GETTEXT_PACKAGE, "Anjuta shell for which the plugins are made"),
2428 G_TYPE_OBJECT,
2429 G_PARAM_READABLE |
2430 G_PARAM_WRITABLE |
2431 G_PARAM_CONSTRUCT));
2432 g_object_class_install_property (object_class,
2433 PROP_STATUS,
2434 g_param_spec_object ("status",
2435 dgettext (GETTEXT_PACKAGE, "Anjuta Status"),
2436 dgettext (GETTEXT_PACKAGE, "Anjuta status to use in loading and unloading of plugins"),
2437 ANJUTA_TYPE_STATUS,
2438 G_PARAM_READABLE |
2439 G_PARAM_WRITABLE |
2440 G_PARAM_CONSTRUCT));
2442 plugin_manager_signals[PLUGIN_ACTIVATED] =
2443 g_signal_new ("plugin-activated",
2444 G_OBJECT_CLASS_TYPE (klass),
2445 G_SIGNAL_RUN_FIRST,
2446 G_STRUCT_OFFSET (AnjutaPluginManagerClass,
2447 plugin_activated),
2448 NULL, NULL,
2449 anjuta_cclosure_marshal_VOID__POINTER_OBJECT,
2450 G_TYPE_NONE, 2,
2451 G_TYPE_POINTER, ANJUTA_TYPE_PLUGIN);
2453 plugin_manager_signals[PLUGIN_DEACTIVATED] =
2454 g_signal_new ("plugin-deactivated",
2455 G_OBJECT_CLASS_TYPE (klass),
2456 G_SIGNAL_RUN_FIRST,
2457 G_STRUCT_OFFSET (AnjutaPluginManagerClass,
2458 plugin_deactivated),
2459 NULL, NULL,
2460 anjuta_cclosure_marshal_VOID__POINTER_OBJECT,
2461 G_TYPE_NONE, 2,
2462 G_TYPE_POINTER, ANJUTA_TYPE_PLUGIN);
2465 GType
2466 anjuta_plugin_manager_get_type (void)
2468 static GType our_type = 0;
2470 if(our_type == 0)
2472 static const GTypeInfo our_info =
2474 sizeof (AnjutaPluginManagerClass), /* class_size */
2475 (GBaseInitFunc) NULL, /* base_init */
2476 (GBaseFinalizeFunc) NULL, /* base_finalize */
2477 (GClassInitFunc) anjuta_plugin_manager_class_init, /* class_init */
2478 (GClassFinalizeFunc) NULL, /* class_finalize */
2479 NULL /* class_data */,
2480 sizeof (AnjutaPluginManager), /* instance_size */
2481 0, /* n_preallocs */
2482 (GInstanceInitFunc) anjuta_plugin_manager_init, /* instance_init */
2483 NULL /* value_table */
2485 our_type = g_type_register_static (G_TYPE_OBJECT,
2486 "AnjutaPluginManager",
2487 &our_info, 0);
2490 return our_type;
2493 AnjutaPluginManager*
2494 anjuta_plugin_manager_new (GObject *shell, AnjutaStatus *status,
2495 GList* plugins_directories)
2497 GObject *manager_object;
2498 AnjutaPluginManager *plugin_manager;
2499 GList *cycles = NULL;
2500 const char *gnome2_path;
2501 char **pathv;
2502 char **p;
2503 GList *node;
2504 GList *plugin_dirs = NULL;
2506 /* Initialize the anjuta plugin system */
2507 manager_object = g_object_new (ANJUTA_TYPE_PLUGIN_MANAGER,
2508 "shell", shell, "status", status, NULL);
2509 plugin_manager = ANJUTA_PLUGIN_MANAGER (manager_object);
2511 if (anjuta_plugin_factory == NULL)
2513 anjuta_plugin_factory = anjuta_c_plugin_factory_new ();
2516 gnome2_path = g_getenv ("GNOME2_PATH");
2517 if (gnome2_path) {
2518 pathv = g_strsplit (gnome2_path, ":", 1);
2520 for (p = pathv; *p != NULL; p++) {
2521 char *path = g_strdup (*p);
2522 plugin_dirs = g_list_prepend (plugin_dirs, path);
2524 g_strfreev (pathv);
2527 node = plugins_directories;
2528 while (node) {
2529 if (!node->data)
2530 continue;
2531 char *path = g_strdup (node->data);
2532 plugin_dirs = g_list_prepend (plugin_dirs, path);
2533 node = g_list_next (node);
2535 plugin_dirs = g_list_reverse (plugin_dirs);
2536 /* load_plugins (); */
2538 node = plugin_dirs;
2539 while (node)
2541 load_plugins_from_directory (plugin_manager, (char*)node->data);
2542 node = g_list_next (node);
2544 resolve_dependencies (plugin_manager, &cycles);
2545 g_list_foreach(plugin_dirs, (GFunc) g_free, NULL);
2546 g_list_free(plugin_dirs);
2547 return plugin_manager;
2550 void
2551 anjuta_plugin_manager_activate_plugins (AnjutaPluginManager *plugin_manager,
2552 GList *plugins_to_activate)
2554 AnjutaPluginManagerPriv *priv;
2555 GdkPixbuf *icon_pixbuf;
2556 GList *node;
2558 priv = plugin_manager->priv;
2560 /* Freeze shell operations */
2561 anjuta_shell_freeze (ANJUTA_SHELL (priv->shell), NULL);
2562 if (plugins_to_activate)
2564 anjuta_status_progress_add_ticks (ANJUTA_STATUS (priv->status),
2565 g_list_length (plugins_to_activate));
2567 node = plugins_to_activate;
2568 while (node)
2570 AnjutaPluginDescription *d;
2571 gchar *plugin_id;
2572 gchar *icon_filename, *label;
2573 gchar *icon_path = NULL;
2575 d = node->data;
2577 icon_pixbuf = NULL;
2578 label = NULL;
2579 if (anjuta_plugin_description_get_string (d, "Anjuta Plugin",
2580 "Icon",
2581 &icon_filename))
2583 gchar *title /*, *description */;
2584 anjuta_plugin_description_get_locale_string (d, "Anjuta Plugin",
2585 "Name",
2586 &title);
2588 anjuta_plugin_description_get_locale_string (d, "Anjuta Plugin",
2589 "Description",
2590 &description);
2592 icon_path = g_strconcat (PACKAGE_PIXMAPS_DIR"/",
2593 icon_filename, NULL);
2594 /* DEBUG_PRINT ("Icon: %s", icon_path); */
2595 /* Avoid space in translated string */
2596 label = g_strconcat (dgettext (GETTEXT_PACKAGE, "Loading:"), " ", title, "...", NULL);
2597 icon_pixbuf = gdk_pixbuf_new_from_file (icon_path, NULL);
2598 if (!icon_pixbuf)
2599 g_warning ("Plugin does not define Icon: No such file %s",
2600 icon_path);
2601 g_free (icon_path);
2602 g_free (icon_filename);
2603 g_free (title);
2606 anjuta_status_progress_tick (ANJUTA_STATUS (priv->status),
2607 icon_pixbuf, label);
2608 g_free (label);
2609 if (icon_pixbuf)
2610 g_object_unref (icon_pixbuf);
2612 if (anjuta_plugin_description_get_string (d, "Anjuta Plugin",
2613 "Location", &plugin_id))
2615 /* Activate the plugin */
2616 anjuta_plugin_manager_get_plugin_by_id (plugin_manager,
2617 plugin_id);
2618 g_free (plugin_id);
2621 node = g_list_next (node);
2624 /* Thaw shell operations */
2625 anjuta_shell_thaw (ANJUTA_SHELL (priv->shell), NULL);
2628 static void
2629 on_collect (gpointer key, gpointer value, gpointer user_data)
2631 gchar *id;
2632 gchar *query = (gchar*) key;
2633 AnjutaPluginDescription *desc = (AnjutaPluginDescription *) value;
2634 GString *write_buffer = (GString *) user_data;
2636 anjuta_plugin_description_get_string (desc, "Anjuta Plugin", "Location",
2637 &id);
2638 g_string_append_printf (write_buffer, "%s=%s;", query, id);
2639 g_free (id);
2643 * anjuta_plugin_manager_get_remembered_plugins:
2644 * @plugin_manager: A #AnjutaPluginManager object
2646 * Get the list of plugins loaded when there is a choice between several
2647 * ones without asking the user.
2649 * The list format is returned as a string with the format detailed in
2650 * anjuta_plugin_manager_set_remembered_plugins().
2652 * Return value: (transfer full): a newly-allocated string that must be freed
2653 * with g_free().
2656 gchar*
2657 anjuta_plugin_manager_get_remembered_plugins (AnjutaPluginManager *plugin_manager)
2659 AnjutaPluginManagerPriv *priv;
2660 GString *write_buffer = g_string_new ("");
2662 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), FALSE);
2664 priv = plugin_manager->priv;
2665 g_hash_table_foreach (priv->remember_plugins, on_collect,
2666 write_buffer);
2667 return g_string_free (write_buffer, FALSE);
2671 * anjuta_plugin_manager_set_remembered_plugins:
2672 * @plugin_manager: A #AnjutaPluginManager object
2673 * @remembered_plugins: A list of prefered plugins
2675 * Set the list of plugins loaded when there is a choice between several
2676 * ones without asking the user.
2677 * The list is a string composed of elements separated by ';'. Each element
2678 * is defined with "key=value", where key is the list of possible plugins and
2679 * the value is the choosen plugin.
2681 * By the example the following element
2682 * <programlisting>
2683 * anjuta-symbol-browser:SymbolBrowserPlugin,anjuta-symbol-db:SymbolDBPlugin,=anjuta-symbol-db:SymbolDBPlugin;
2684 * </programlisting>
2685 * means if Anjuta has to choose between SymbolBrowserPlugin and
2686 * SymbolDBPlugin, it will choose SymbolDBPlugin.
2688 void
2689 anjuta_plugin_manager_set_remembered_plugins (AnjutaPluginManager *plugin_manager,
2690 const gchar *remembered_plugins)
2692 AnjutaPluginManagerPriv *priv;
2693 gchar **strv_lines, **line_idx;
2695 g_return_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager));
2696 g_return_if_fail (remembered_plugins != NULL);
2698 priv = plugin_manager->priv;
2700 g_hash_table_remove_all (priv->remember_plugins);
2702 strv_lines = g_strsplit (remembered_plugins, ";", -1);
2703 line_idx = strv_lines;
2704 while (*line_idx)
2706 gchar **strv_keyvals;
2707 strv_keyvals = g_strsplit (*line_idx, "=", -1);
2708 if (strv_keyvals && strv_keyvals[0] && strv_keyvals[1])
2710 AnjutaPluginHandle *plugin;
2711 plugin = g_hash_table_lookup (priv->plugins_by_name,
2712 strv_keyvals[1]);
2713 if (plugin)
2715 AnjutaPluginDescription *desc;
2716 desc = anjuta_plugin_handle_get_description (plugin);
2718 DEBUG_PRINT ("Restoring remember plugin: %s=%s",
2719 strv_keyvals[0],
2720 strv_keyvals[1]);
2722 g_hash_table_insert (priv->remember_plugins,
2723 g_strdup (strv_keyvals[0]), desc);
2725 g_strfreev (strv_keyvals);
2727 line_idx++;
2729 g_strfreev (strv_lines);