Updated Occitan translation
[anjuta.git] / libanjuta / anjuta-plugin-manager.c
bloba84307dbbbdd12f9f3b7fba5b36451b61be72470
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;
92 /* disable plugins */
93 GHashTable *disable_plugins;
96 /* Available plugins page treeview */
97 enum {
98 COL_ACTIVABLE,
99 COL_ENABLED,
100 COL_ICON,
101 COL_NAME,
102 COL_PLUGIN,
103 N_COLS
106 /* Remembered plugins page treeview */
107 enum {
108 COL_REM_ICON,
109 COL_REM_NAME,
110 COL_REM_PLUGIN_KEY,
111 N_REM_COLS
114 /* Plugin class types */
116 static AnjutaCPluginFactory *anjuta_plugin_factory = NULL;
118 static GObjectClass* parent_class = NULL;
119 static guint plugin_manager_signals[LAST_SIGNAL] = { 0 };
121 static void plugin_set_update (AnjutaPluginManager *plugin_manager,
122 AnjutaPluginHandle* selected_plugin,
123 gboolean load);
125 static IAnjutaPluginFactory* get_plugin_factory (AnjutaPluginManager *plugin_manager,
126 const gchar *language, GError **error);
128 GQuark
129 anjuta_plugin_manager_error_quark (void)
131 static GQuark quark = 0;
133 if (quark == 0) {
134 quark = g_quark_from_static_string ("anjuta-plugin-manager-quark");
136 return quark;
139 /** Dependency Resolution **/
141 static gboolean
142 collect_cycle (AnjutaPluginManager *plugin_manager,
143 AnjutaPluginHandle *base_plugin, AnjutaPluginHandle *cur_plugin,
144 GList **cycle)
146 AnjutaPluginManagerPriv *priv;
147 GList *l;
149 priv = plugin_manager->priv;
151 for (l = anjuta_plugin_handle_get_dependency_names (cur_plugin);
152 l != NULL; l = l->next)
154 AnjutaPluginHandle *dep = g_hash_table_lookup (priv->plugins_by_name,
155 l->data);
156 if (dep)
158 if (dep == base_plugin)
160 *cycle = g_list_prepend (NULL, dep);
161 /* DEBUG_PRINT ("%s", anjuta_plugin_handle_get_name (dep)); */
162 return TRUE;
164 else
166 if (collect_cycle (plugin_manager, base_plugin, dep, cycle))
168 *cycle = g_list_prepend (*cycle, dep);
169 /* DEBUG_PRINT ("%s", anjuta_plugin_handle_get_name (dep)); */
170 return TRUE;
175 return FALSE;
178 static void
179 add_dependency (AnjutaPluginHandle *dependent, AnjutaPluginHandle *dependency)
181 g_hash_table_insert (anjuta_plugin_handle_get_dependents (dependency),
182 dependent, dependency);
183 g_hash_table_insert (anjuta_plugin_handle_get_dependencies (dependent),
184 dependency, dependent);
187 static void
188 child_dep_foreach_cb (gpointer key, gpointer value, gpointer user_data)
190 add_dependency (ANJUTA_PLUGIN_HANDLE (user_data),
191 ANJUTA_PLUGIN_HANDLE (key));
194 /* Resolves dependencies for a single module recursively. Shortcuts if
195 * the module has already been resolved. Returns a list representing
196 * any cycles found, or NULL if no cycles are found. If a cycle is found,
197 * the graph is left unresolved.
199 static GList*
200 resolve_for_module (AnjutaPluginManager *plugin_manager,
201 AnjutaPluginHandle *plugin, int pass)
203 AnjutaPluginManagerPriv *priv;
204 GList *l;
205 GList *ret = NULL;
207 priv = plugin_manager->priv;
209 if (anjuta_plugin_handle_get_checked (plugin))
211 return NULL;
214 if (anjuta_plugin_handle_get_resolve_pass (plugin) == pass)
216 GList *cycle = NULL;
217 g_warning ("cycle found: %s on pass %d",
218 anjuta_plugin_handle_get_name (plugin),
219 anjuta_plugin_handle_get_resolve_pass (plugin));
220 collect_cycle (plugin_manager, plugin, plugin, &cycle);
221 return cycle;
224 if (anjuta_plugin_handle_get_resolve_pass (plugin) != -1)
226 return NULL;
229 anjuta_plugin_handle_set_can_load (plugin, TRUE);
230 anjuta_plugin_handle_set_resolve_pass (plugin, pass);
232 for (l = anjuta_plugin_handle_get_dependency_names (plugin);
233 l != NULL; l = l->next)
235 char *dep = l->data;
236 AnjutaPluginHandle *child =
237 g_hash_table_lookup (priv->plugins_by_name, dep);
238 if (child)
240 ret = resolve_for_module (plugin_manager, child, pass);
241 if (ret)
243 break;
246 /* Add the dependency's dense dependency list
247 * to the current module's dense dependency list */
248 g_hash_table_foreach (anjuta_plugin_handle_get_dependencies (child),
249 child_dep_foreach_cb, plugin);
250 add_dependency (plugin, child);
252 /* If the child can't load due to dependency problems,
253 * the current module can't either */
254 anjuta_plugin_handle_set_can_load (plugin,
255 anjuta_plugin_handle_get_can_load (child));
256 } else {
257 g_warning ("Dependency %s not found.\n", dep);
258 anjuta_plugin_handle_set_can_load (plugin, FALSE);
259 ret = NULL;
262 anjuta_plugin_handle_set_checked (plugin, TRUE);
264 return ret;
267 /* Clean up the results of a resolving run */
268 static void
269 unresolve_dependencies (AnjutaPluginManager *plugin_manager)
271 AnjutaPluginManagerPriv *priv;
272 GList *l;
274 priv = plugin_manager->priv;
276 for (l = priv->available_plugins; l != NULL; l = l->next)
278 AnjutaPluginHandle *plugin = l->data;
279 anjuta_plugin_handle_unresolve_dependencies (plugin);
283 /* done upto here */
285 static void
286 prune_modules (AnjutaPluginManager *plugin_manager, GList *modules)
288 AnjutaPluginManagerPriv *priv;
289 GList *l;
291 priv = plugin_manager->priv;
293 for (l = modules; l != NULL; l = l->next) {
294 AnjutaPluginHandle *plugin = l->data;
296 g_hash_table_remove (priv->plugins_by_name,
297 anjuta_plugin_handle_get_id (plugin));
298 priv->available_plugins = g_list_remove (priv->available_plugins, plugin);
302 static int
303 dependency_compare (AnjutaPluginHandle *plugin_a,
304 AnjutaPluginHandle *plugin_b)
306 int a = g_hash_table_size (anjuta_plugin_handle_get_dependencies (plugin_a));
307 int b = g_hash_table_size (anjuta_plugin_handle_get_dependencies (plugin_b));
309 return a - b;
312 /* Resolves the dependencies of the priv->available_plugins list. When this
313 * function is complete, the following will be true:
315 * 1) The dependencies and dependents hash tables of the modules will
316 * be filled.
318 * 2) Cycles in the graph will be removed.
320 * 3) Modules which cannot be loaded due to failed dependencies will
321 * be marked as such.
323 * 4) priv->available_plugins will be sorted such that no module depends on a
324 * module after it.
326 * If a cycle in the graph is found, it is pruned from the tree and
327 * returned as a list stored in the cycles list.
329 static void
330 resolve_dependencies (AnjutaPluginManager *plugin_manager, GList **cycles)
332 AnjutaPluginManagerPriv *priv;
333 GList *cycle = NULL;
334 GList *l;
336 priv = plugin_manager->priv;
337 *cycles = NULL;
339 /* Try resolving dependencies. If there is a cycle, prune the
340 * cycle and try to resolve again */
343 int pass = 1;
344 cycle = NULL;
345 for (l = priv->available_plugins; l != NULL && !cycle; l = l->next) {
346 cycle = resolve_for_module (plugin_manager, l->data, pass++);
347 cycle = NULL;
349 if (cycle) {
350 *cycles = g_list_prepend (*cycles, cycle);
351 prune_modules (plugin_manager, cycle);
352 unresolve_dependencies (plugin_manager);
354 } while (cycle);
356 /* Now that there is a fully resolved dependency tree, sort
357 * priv->available_plugins to create a valid load order */
358 priv->available_plugins = g_list_sort (priv->available_plugins,
359 (GCompareFunc)dependency_compare);
362 /* Plugins loading */
364 static gboolean
365 str_has_suffix (const char *haystack, const char *needle)
367 const char *h, *n;
369 if (needle == NULL) {
370 return TRUE;
372 if (haystack == NULL) {
373 return needle[0] == '\0';
376 /* Eat one character at a time. */
377 h = haystack + strlen(haystack);
378 n = needle + strlen(needle);
379 do {
380 if (n == needle) {
381 return TRUE;
383 if (h == haystack) {
384 return FALSE;
386 } while (*--h == *--n);
387 return FALSE;
390 static void
391 load_plugin (AnjutaPluginManager *plugin_manager,
392 const gchar *plugin_desc_path)
394 AnjutaPluginManagerPriv *priv;
395 AnjutaPluginHandle *plugin_handle;
397 g_return_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager));
398 priv = plugin_manager->priv;
400 plugin_handle = anjuta_plugin_handle_new (plugin_desc_path);
401 if (plugin_handle)
403 if (g_hash_table_lookup (priv->plugins_by_name,
404 anjuta_plugin_handle_get_id (plugin_handle)))
406 g_object_unref (plugin_handle);
408 else
410 GList *node;
411 /* Available plugin */
412 priv->available_plugins = g_list_prepend (priv->available_plugins,
413 plugin_handle);
414 /* Index by id */
415 g_hash_table_insert (priv->plugins_by_name,
416 (gchar *)anjuta_plugin_handle_get_id (plugin_handle),
417 plugin_handle);
419 /* Index by description */
420 g_hash_table_insert (priv->plugins_by_description,
421 anjuta_plugin_handle_get_description (plugin_handle),
422 plugin_handle);
424 /* Index by interfaces exported by this plugin */
425 node = anjuta_plugin_handle_get_interfaces (plugin_handle);
426 while (node)
428 GList *objs;
429 gchar *iface;
430 GList *obj_node;
431 gboolean found;
433 iface = node->data;
434 objs = (GList*)g_hash_table_lookup (priv->plugins_by_interfaces, iface);
436 obj_node = objs;
437 found = FALSE;
438 while (obj_node)
440 if (obj_node->data == plugin_handle)
442 found = TRUE;
443 break;
445 obj_node = g_list_next (obj_node);
447 if (!found)
449 g_hash_table_steal (priv->plugins_by_interfaces, iface);
450 objs = g_list_prepend (objs, plugin_handle);
451 g_hash_table_insert (priv->plugins_by_interfaces, iface, objs);
453 node = g_list_next (node);
457 return;
460 static void
461 load_plugins_from_directory (AnjutaPluginManager* plugin_manager,
462 const gchar *dirname)
464 DIR *dir;
465 struct dirent *entry;
467 dir = opendir (dirname);
469 if (!dir)
471 return;
474 for (entry = readdir (dir); entry != NULL; entry = readdir (dir))
476 if (str_has_suffix (entry->d_name, ".plugin"))
478 gchar *pathname;
479 pathname = g_strdup_printf ("%s/%s", dirname, entry->d_name);
480 load_plugin (plugin_manager,pathname);
481 g_free (pathname);
484 closedir (dir);
487 /* Plugin activation and deactivation */
489 static void
490 on_plugin_activated (AnjutaPlugin *plugin_object, AnjutaPluginHandle *plugin)
492 AnjutaPluginManager *plugin_manager;
493 AnjutaPluginManagerPriv *priv;
495 /* FIXME: Pass plugin_manager directly in signal arguments */
496 plugin_manager = anjuta_shell_get_plugin_manager (plugin_object->shell, NULL);
498 g_return_if_fail(plugin_manager != NULL);
500 priv = plugin_manager->priv;
502 g_hash_table_insert (priv->activated_plugins, plugin,
503 g_object_ref (plugin_object));
504 g_hash_table_remove (priv->plugins_cache, plugin);
506 g_signal_emit_by_name (plugin_manager, "plugin-activated",
507 plugin,
508 plugin_object);
511 static void
512 on_plugin_deactivated (AnjutaPlugin *plugin_object, AnjutaPluginHandle *plugin)
514 AnjutaPluginManager *plugin_manager;
515 AnjutaPluginManagerPriv *priv;
517 /* FIXME: Pass plugin_manager directly in signal arguments */
518 plugin_manager = anjuta_shell_get_plugin_manager (plugin_object->shell, NULL);
520 g_return_if_fail (plugin_manager != NULL);
522 priv = plugin_manager->priv;
524 g_hash_table_insert (priv->plugins_cache, plugin, g_object_ref (plugin_object));
525 g_hash_table_remove (priv->activated_plugins, plugin);
527 g_signal_emit_by_name (plugin_manager, "plugin-deactivated",
528 plugin,
529 plugin_object);
532 static AnjutaPlugin*
533 activate_plugin (AnjutaPluginManager *plugin_manager,
534 AnjutaPluginHandle *handle, GError **error)
536 AnjutaPluginManagerPriv *priv;
537 IAnjutaPluginFactory* factory;
538 AnjutaPlugin *plugin;
539 const gchar *language;
541 priv = plugin_manager->priv;
543 language = anjuta_plugin_handle_get_language (handle);
545 factory = get_plugin_factory (plugin_manager, language, error);
546 if (factory == NULL) return NULL;
548 plugin = ianjuta_plugin_factory_new_plugin (factory, handle, ANJUTA_SHELL (priv->shell), error);
550 if (plugin == NULL)
552 return NULL;
554 g_signal_connect (plugin, "activated",
555 G_CALLBACK (on_plugin_activated), handle);
556 g_signal_connect (plugin, "deactivated",
557 G_CALLBACK (on_plugin_deactivated), handle);
559 return plugin;
563 * anjuta_plugin_manager_unload_all_plugins:
564 * @plugin_manager: A #AnjutaPluginManager object
566 * Unload all plugins. Do not take care of the dependencies because all plugins
567 * are unloaded anyway.
569 void
570 anjuta_plugin_manager_unload_all_plugins (AnjutaPluginManager *plugin_manager)
572 AnjutaPluginManagerPriv *priv;
574 priv = plugin_manager->priv;
575 if (g_hash_table_size (priv->activated_plugins) > 0 ||
576 g_hash_table_size (priv->plugins_cache) > 0)
578 if (g_hash_table_size (priv->activated_plugins) > 0)
580 GList *node;
581 for (node = g_list_last (priv->available_plugins); node; node = g_list_previous (node))
583 AnjutaPluginHandle *selected_plugin = node->data;
584 AnjutaPlugin *plugin;
586 plugin = g_hash_table_lookup (priv->activated_plugins, selected_plugin);
587 if (plugin)
589 DEBUG_PRINT ("Deactivating plugin: %s",
590 anjuta_plugin_handle_get_id (selected_plugin));
591 anjuta_plugin_deactivate (plugin);
594 g_hash_table_remove_all (priv->activated_plugins);
596 if (g_hash_table_size (priv->plugins_cache) > 0)
598 GList *node;
600 for (node = g_list_last (priv->available_plugins); node; node = g_list_previous (node))
602 AnjutaPluginHandle *selected_plugin = node->data;
604 g_hash_table_remove (priv->plugins_cache, selected_plugin);
606 g_hash_table_remove_all (priv->plugins_cache);
611 /* Return true if plugin should be unloaded when plugin_to_unloaded is unloaded.
612 * It can be because plugin is or need plugin_to_unload. */
613 static gboolean
614 should_unload (GHashTable *activated_plugins, AnjutaPluginHandle *plugin_to_unload,
615 AnjutaPluginHandle *plugin)
617 GObject *plugin_obj = g_hash_table_lookup (activated_plugins, plugin);
619 if (!plugin_obj)
620 return FALSE;
622 if (plugin_to_unload == plugin)
623 return TRUE;
625 gboolean dependent =
626 GPOINTER_TO_INT (g_hash_table_lookup (anjuta_plugin_handle_get_dependents (plugin_to_unload),
627 plugin));
628 return dependent;
631 /* Return true if plugin should be loaded when plugin_to_loaded is loaded.
632 * It can be because plugin_to_load is or need plugin. */
633 static gboolean
634 should_load (GHashTable *activated_plugins, AnjutaPluginHandle *plugin_to_load,
635 AnjutaPluginHandle *plugin)
637 GObject *plugin_obj = g_hash_table_lookup (activated_plugins, plugin);
639 if (plugin_obj)
640 return FALSE;
642 if (plugin_to_load == plugin)
643 return anjuta_plugin_handle_get_can_load (plugin);
645 gboolean dependency =
646 GPOINTER_TO_INT (g_hash_table_lookup (anjuta_plugin_handle_get_dependencies (plugin_to_load),
647 plugin));
648 return (dependency && anjuta_plugin_handle_get_can_load (plugin));
651 static AnjutaPluginHandle *
652 plugin_for_iter (GtkListStore *store, GtkTreeIter *iter)
654 AnjutaPluginHandle *plugin;
656 gtk_tree_model_get (GTK_TREE_MODEL (store), iter, COL_PLUGIN, &plugin, -1);
657 return plugin;
660 static void
661 update_enabled (GtkTreeModel *model, GHashTable *activated_plugins)
663 GtkTreeIter iter;
665 if (gtk_tree_model_get_iter_first (model, &iter)) {
666 do {
667 AnjutaPluginHandle *plugin;
668 GObject *plugin_obj;
669 gboolean installed;
671 plugin = plugin_for_iter(GTK_LIST_STORE(model), &iter);
672 plugin_obj = g_hash_table_lookup (activated_plugins, plugin);
673 installed = (plugin_obj != NULL) ? TRUE : FALSE;
674 gtk_tree_model_get (model, &iter, COL_PLUGIN, &plugin, -1);
675 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
676 COL_ENABLED, installed, -1);
677 } while (gtk_tree_model_iter_next (model, &iter));
681 static void
682 plugin_set_update (AnjutaPluginManager *plugin_manager,
683 AnjutaPluginHandle* selected_plugin,
684 gboolean load)
686 AnjutaPluginManagerPriv *priv;
687 gboolean loaded;
688 GList *l;
690 priv = plugin_manager->priv;
692 /* Plugins can be loaded or unloaded implicitely because they need or are
693 * needed by another plugin so it is possible that we try to load or unload
694 * respectively an already loaded or already unloaded plugin. */
695 loaded = g_hash_table_lookup (priv->activated_plugins, selected_plugin) != NULL;
696 if ((load && loaded) || (!load && !loaded)) return;
698 if (priv->status)
699 anjuta_status_busy_push (priv->status);
701 if (!load)
703 /* visit priv->available_plugins in reverse order when unloading, so
704 * that plugins are unloaded in the right order */
705 for (l = g_list_last(priv->available_plugins); l != NULL; l = l->prev)
707 AnjutaPluginHandle *plugin = l->data;
708 if (should_unload (priv->activated_plugins, selected_plugin, plugin))
710 AnjutaPlugin *plugin_obj = ANJUTA_PLUGIN (g_hash_table_lookup (priv->activated_plugins, plugin));
711 if (!anjuta_plugin_deactivate (plugin_obj))
713 anjuta_util_dialog_info (GTK_WINDOW (priv->shell),
714 dgettext (GETTEXT_PACKAGE, "Plugin '%s' does not want to be deactivated"),
715 anjuta_plugin_handle_get_name (plugin));
720 else
722 for (l = priv->available_plugins; l != NULL; l = l->next)
724 AnjutaPluginHandle *plugin = l->data;
725 if (should_load (priv->activated_plugins, selected_plugin, plugin))
727 AnjutaPlugin *plugin_obj;
728 GError *error = NULL;
729 plugin_obj = g_hash_table_lookup (priv->plugins_cache, plugin);
730 if (plugin_obj)
731 g_object_ref (plugin_obj);
732 else
734 plugin_obj = activate_plugin (plugin_manager, plugin,
735 &error);
738 if (plugin_obj)
740 anjuta_plugin_activate (ANJUTA_PLUGIN (plugin_obj));
741 g_object_unref (plugin_obj);
743 else
745 if (error)
747 gchar* message = g_strdup_printf (dgettext (GETTEXT_PACKAGE, "Could not load %s\n"
748 "This usually means that your installation is corrupted. The "
749 "error message leading to this was:\n%s"),
750 anjuta_plugin_handle_get_name (selected_plugin),
751 error->message);
752 anjuta_util_dialog_error (GTK_WINDOW(plugin_manager->priv->shell),
753 message);
754 g_error_free (error);
755 g_free(message);
761 if (priv->status)
762 anjuta_status_busy_pop (priv->status);
764 return;
767 static void
768 plugin_toggled (GtkCellRendererToggle *cell, char *path_str, gpointer data)
770 AnjutaPluginManager *plugin_manager;
771 AnjutaPluginManagerPriv *priv;
772 GtkListStore *store = GTK_LIST_STORE (data);
773 GtkTreeIter iter;
774 GtkTreePath *path;
775 AnjutaPluginHandle *plugin;
776 gboolean enabled;
777 GList *activated_plugins;
778 GList *node;
779 AnjutaPlugin* plugin_object;
781 path = gtk_tree_path_new_from_string (path_str);
783 plugin_manager = g_object_get_data (G_OBJECT (store), "plugin-manager");
784 priv = plugin_manager->priv;
786 gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path);
787 gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
788 COL_ENABLED, &enabled,
789 COL_PLUGIN, &plugin,
790 -1);
792 /* Activate one plugin can force the loading of other ones, instead of
793 * searching which plugins have to be activated, we just unmerge all
794 * current plugins and merge all plugins after the modification */
796 /* unmerge all plugins */
797 activated_plugins = g_hash_table_get_values (priv->activated_plugins);
798 for (node = g_list_first (activated_plugins); node != NULL; node = g_list_next (node))
800 plugin_object = (AnjutaPlugin *)node->data;
801 if (plugin_object &&
802 IANJUTA_IS_PREFERENCES(plugin_object))
804 ianjuta_preferences_unmerge (IANJUTA_PREFERENCES (plugin_object),
805 anjuta_shell_get_preferences (ANJUTA_SHELL (priv->shell), NULL),
806 NULL);
809 g_list_free (activated_plugins);
811 plugin_set_update (plugin_manager, plugin, !enabled);
813 /* Make sure that it appears in the preferences. This method
814 can only be called when the preferences dialog is active so
815 it should be save
817 activated_plugins = g_hash_table_get_values (priv->activated_plugins);
818 for (node = g_list_first (activated_plugins); node != NULL; node = g_list_next (node))
820 plugin_object = (AnjutaPlugin *)node->data;
821 if (plugin_object &&
822 IANJUTA_IS_PREFERENCES(plugin_object))
824 ianjuta_preferences_merge (IANJUTA_PREFERENCES (plugin_object),
825 anjuta_shell_get_preferences (ANJUTA_SHELL (priv->shell), NULL),
826 NULL);
829 g_list_free (activated_plugins);
831 update_enabled (GTK_TREE_MODEL (store), priv->activated_plugins);
832 gtk_tree_path_free (path);
835 #if 0
836 static void
837 selection_changed (GtkTreeSelection *selection, GtkListStore *store)
839 GtkTreeIter iter;
841 if (gtk_tree_selection_get_selected (selection, NULL,
842 &iter)) {
843 GtkTextBuffer *buffer;
845 GtkWidget *txt = g_object_get_data (G_OBJECT (store),
846 "AboutText");
848 GtkWidget *image = g_object_get_data (G_OBJECT (store),
849 "Icon");
850 AnjutaPluginHandle *plugin = plugin_for_iter (store, &iter);
852 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (txt));
853 gtk_text_buffer_set_text (buffer, plugin->about, -1);
855 if (plugin->icon_path) {
856 gtk_image_set_from_file (GTK_IMAGE (image),
857 plugin->icon_path);
858 gtk_widget_show (GTK_WIDGET (image));
859 } else {
860 gtk_widget_hide (GTK_WIDGET (image));
864 #endif
866 static GtkWidget *
867 create_plugin_tree (void)
869 GtkListStore *store;
870 GtkWidget *tree;
871 GtkCellRenderer *renderer;
872 GtkTreeViewColumn *column;
874 store = gtk_list_store_new (N_COLS,
875 G_TYPE_BOOLEAN,
876 G_TYPE_BOOLEAN,
877 GDK_TYPE_PIXBUF,
878 G_TYPE_STRING,
879 G_TYPE_POINTER);
880 tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
882 renderer = gtk_cell_renderer_toggle_new ();
883 g_signal_connect (G_OBJECT (renderer), "toggled",
884 G_CALLBACK (plugin_toggled), store);
885 column = gtk_tree_view_column_new_with_attributes (dgettext (GETTEXT_PACKAGE, "Load"),
886 renderer,
887 "active",
888 COL_ENABLED,
889 "activatable",
890 COL_ACTIVABLE,
891 NULL);
892 gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
893 gtk_tree_view_column_set_sizing (column,
894 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
896 column = gtk_tree_view_column_new ();
897 renderer = gtk_cell_renderer_pixbuf_new ();
898 gtk_tree_view_column_pack_start (column, renderer, FALSE);
899 gtk_tree_view_column_add_attribute (column, renderer, "pixbuf",
900 COL_ICON);
901 renderer = gtk_cell_renderer_text_new ();
902 g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
903 gtk_tree_view_column_pack_start (column, renderer, TRUE);
904 gtk_tree_view_column_add_attribute (column, renderer, "markup",
905 COL_NAME);
906 gtk_tree_view_column_set_sizing (column,
907 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
908 gtk_tree_view_column_set_title (column, dgettext (GETTEXT_PACKAGE, "Available Plugins"));
909 gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
910 gtk_tree_view_set_expander_column (GTK_TREE_VIEW (tree), column);
912 g_object_unref (store);
913 return tree;
916 /* Sort function for plugins */
917 static gint
918 sort_plugins(gconstpointer a, gconstpointer b)
920 g_return_val_if_fail (a != NULL, 0);
921 g_return_val_if_fail (b != NULL, 0);
923 AnjutaPluginHandle* plugin_a = ANJUTA_PLUGIN_HANDLE (a);
924 AnjutaPluginHandle* plugin_b = ANJUTA_PLUGIN_HANDLE (b);
926 return strcmp (anjuta_plugin_handle_get_name (plugin_a),
927 anjuta_plugin_handle_get_name (plugin_b));
930 /* If show_all == FALSE, show only user activatable plugins
931 * If show_all == TRUE, show all plugins
933 static void
934 populate_plugin_model (AnjutaPluginManager *plugin_manager,
935 GtkListStore *store,
936 GHashTable *plugins_to_show,
937 GHashTable *activated_plugins,
938 gboolean show_all)
940 AnjutaPluginManagerPriv *priv;
941 GList *sorted_plugins, *l;
943 priv = plugin_manager->priv;
944 gtk_list_store_clear (store);
946 sorted_plugins = g_list_copy (priv->available_plugins);
947 sorted_plugins = g_list_sort (sorted_plugins, sort_plugins);
949 for (l = sorted_plugins; l != NULL; l = l->next)
951 AnjutaPluginHandle *plugin = l->data;
953 /* If plugins to show is NULL, show all available plugins */
954 if (plugins_to_show == NULL ||
955 g_hash_table_lookup (plugins_to_show, plugin))
958 gboolean enable = FALSE;
959 if (g_hash_table_lookup (activated_plugins, plugin))
960 enable = TRUE;
962 if (anjuta_plugin_handle_get_name (plugin) &&
963 anjuta_plugin_handle_get_description (plugin) &&
964 (anjuta_plugin_handle_get_user_activatable (plugin) ||
965 show_all) &&
966 (g_hash_table_lookup (plugin_manager->priv->disable_plugins, plugin) == NULL))
968 GtkTreeIter iter;
969 gchar *text;
971 text = g_markup_printf_escaped ("<span size=\"larger\" weight=\"bold\">%s</span>\n%s",
972 anjuta_plugin_handle_get_name (plugin),
973 anjuta_plugin_handle_get_about (plugin));
975 gtk_list_store_append (store, &iter);
976 gtk_list_store_set (store, &iter,
977 COL_ACTIVABLE,
978 anjuta_plugin_handle_get_user_activatable (plugin),
979 COL_ENABLED, enable,
980 COL_NAME, text,
981 COL_PLUGIN, plugin,
982 -1);
983 if (anjuta_plugin_handle_get_icon_path (plugin))
985 GdkPixbuf *icon;
986 icon = gdk_pixbuf_new_from_file_at_size (anjuta_plugin_handle_get_icon_path (plugin),
987 32, 32, NULL);
988 if (icon) {
989 gtk_list_store_set (store, &iter,
990 COL_ICON, icon, -1);
991 g_object_unref (icon);
994 g_free (text);
999 g_list_free (sorted_plugins);
1002 static GtkWidget *
1003 create_remembered_plugins_tree (void)
1005 GtkListStore *store;
1006 GtkWidget *tree;
1007 GtkCellRenderer *renderer;
1008 GtkTreeViewColumn *column;
1010 store = gtk_list_store_new (N_REM_COLS, GDK_TYPE_PIXBUF, G_TYPE_STRING,
1011 G_TYPE_STRING);
1012 tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
1014 column = gtk_tree_view_column_new ();
1015 renderer = gtk_cell_renderer_pixbuf_new ();
1016 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1017 gtk_tree_view_column_add_attribute (column, renderer, "pixbuf",
1018 COL_REM_ICON);
1019 renderer = gtk_cell_renderer_text_new ();
1020 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1021 gtk_tree_view_column_add_attribute (column, renderer, "markup",
1022 COL_REM_NAME);
1023 gtk_tree_view_column_set_sizing (column,
1024 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1025 gtk_tree_view_column_set_title (column, dgettext (GETTEXT_PACKAGE, "Preferred plugins"));
1026 gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
1027 gtk_tree_view_set_expander_column (GTK_TREE_VIEW (tree), column);
1029 g_object_unref (store);
1030 return tree;
1033 static void
1034 foreach_remembered_plugin (gpointer key, gpointer value, gpointer user_data)
1036 AnjutaPluginHandle *handle = (AnjutaPluginHandle *) value;
1037 GtkListStore *store = GTK_LIST_STORE (user_data);
1038 AnjutaPluginManager *manager = g_object_get_data (G_OBJECT (store),
1039 "plugin-manager");
1041 if (anjuta_plugin_handle_get_name (handle) &&
1042 anjuta_plugin_handle_get_description (handle))
1044 GtkTreeIter iter;
1045 gchar *text;
1047 text = g_markup_printf_escaped ("<span size=\"larger\" weight=\"bold\">%s</span>\n%s",
1048 anjuta_plugin_handle_get_name (handle),
1049 anjuta_plugin_handle_get_about (handle));
1051 gtk_list_store_append (store, &iter);
1052 gtk_list_store_set (store, &iter,
1053 COL_REM_NAME, text,
1054 COL_REM_PLUGIN_KEY, key,
1055 -1);
1056 if (anjuta_plugin_handle_get_icon_path (handle))
1058 GdkPixbuf *icon;
1059 icon = gdk_pixbuf_new_from_file_at_size (anjuta_plugin_handle_get_icon_path (handle),
1060 32, 32, NULL);
1061 if (icon) {
1062 gtk_list_store_set (store, &iter,
1063 COL_REM_ICON, icon, -1);
1064 g_object_unref (icon);
1067 g_free (text);
1071 static void
1072 populate_remembered_plugins_model (AnjutaPluginManager *plugin_manager,
1073 GtkListStore *store)
1075 AnjutaPluginManagerPriv *priv = plugin_manager->priv;
1076 gtk_list_store_clear (store);
1077 g_hash_table_foreach (priv->remember_plugins, foreach_remembered_plugin,
1078 store);
1081 static void
1082 on_show_all_plugins_toggled (GtkToggleButton *button, GtkListStore *store)
1084 AnjutaPluginManager *plugin_manager;
1086 plugin_manager = g_object_get_data (G_OBJECT (button), "__plugin_manager");
1088 populate_plugin_model (plugin_manager, store, NULL,
1089 plugin_manager->priv->activated_plugins,
1090 !gtk_toggle_button_get_active (button));
1093 static void
1094 on_forget_plugin_clicked (GtkWidget *button, GtkTreeView *view)
1096 GtkTreeIter iter;
1097 GtkTreeModel *model;
1098 GtkTreeSelection *selection = gtk_tree_view_get_selection (view);
1099 if (gtk_tree_selection_get_selected (selection, &model, &iter))
1101 gchar *plugin_key;
1102 AnjutaPluginManager *manager = g_object_get_data (G_OBJECT (model),
1103 "plugin-manager");
1104 gtk_tree_model_get (model, &iter, COL_REM_PLUGIN_KEY, &plugin_key, -1);
1105 g_hash_table_remove (manager->priv->remember_plugins, plugin_key);
1106 gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
1107 g_free (plugin_key);
1111 static void
1112 on_forget_plugin_sel_changed (GtkTreeSelection *selection,
1113 GtkWidget *button)
1115 GtkTreeIter iter;
1117 if (gtk_tree_selection_get_selected (selection, NULL, &iter))
1118 gtk_widget_set_sensitive (button, TRUE);
1119 else
1120 gtk_widget_set_sensitive (button, FALSE);
1123 GtkWidget *
1124 anjuta_plugin_manager_get_plugins_page (AnjutaPluginManager *plugin_manager)
1126 GtkWidget *vbox;
1127 GtkWidget *checkbutton;
1128 GtkWidget *tree;
1129 GtkWidget *scrolled;
1130 GtkWidget *toolbar;
1131 GtkToolItem *toolitem;
1132 GtkListStore *store;
1134 /* Plugins page */
1135 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1136 gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
1138 scrolled = gtk_scrolled_window_new (NULL, NULL);
1139 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
1140 GTK_SHADOW_IN);
1141 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
1142 GTK_POLICY_NEVER,
1143 GTK_POLICY_AUTOMATIC);
1144 gtk_style_context_set_junction_sides (gtk_widget_get_style_context (scrolled), GTK_JUNCTION_BOTTOM);
1145 gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 0);
1147 toolbar = gtk_toolbar_new ();
1148 gtk_style_context_add_class (gtk_widget_get_style_context (toolbar), GTK_STYLE_CLASS_INLINE_TOOLBAR);
1149 gtk_style_context_set_junction_sides (gtk_widget_get_style_context (toolbar), GTK_JUNCTION_TOP);
1150 gtk_box_pack_start (GTK_BOX (vbox), toolbar, FALSE, FALSE, 0);
1151 gtk_widget_show (toolbar);
1153 toolitem = gtk_tool_item_new ();
1154 gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (toolitem), 0);
1155 gtk_widget_show (GTK_WIDGET(toolitem));
1157 checkbutton = gtk_check_button_new_with_label (dgettext (GETTEXT_PACKAGE, "Only show user activatable plugins"));
1158 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), TRUE);
1159 gtk_container_add (GTK_CONTAINER (toolitem), checkbutton);
1161 tree = create_plugin_tree ();
1162 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (tree), TRUE);
1163 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree), FALSE);
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_box_new (GTK_ORIENTATION_VERTICAL, 10);
1195 gtk_container_set_border_width (GTK_CONTAINER (vbox), 10);
1197 display_label = gtk_label_new (dgettext (GETTEXT_PACKAGE, "These are the plugins selected by you "
1198 "when you have been prompted to choose one of "
1199 "many suitable plugins. Removing the "
1200 "preferred plugin will let you "
1201 "choose a 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_box_new (GTK_ORIENTATION_HORIZONTAL, 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 (dgettext (GETTEXT_PACKAGE, "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 *handles = NULL;
1348 node = valid_plugins;
1349 while (node)
1351 plugin = node->data;
1352 handles = g_list_prepend (handles, plugin);
1353 node = g_list_next (node);
1355 handles = g_list_reverse (handles);
1356 obj = anjuta_plugin_manager_select_and_activate (plugin_manager,
1357 dgettext (GETTEXT_PACKAGE, "Select a plugin"),
1358 dgettext (GETTEXT_PACKAGE, "Please select a plugin to activate"),
1359 handles);
1360 g_list_free (handles);
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 dgettext (GETTEXT_PACKAGE, "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 /* Filter disable plugins */
1482 valid_plugins = g_list_copy (valid_plugins);
1483 node = valid_plugins;
1484 while (node)
1486 GList *next = g_list_next (node);
1488 if ((g_hash_table_lookup (priv->disable_plugins, node->data) != NULL) &&
1489 (g_hash_table_lookup (priv->activated_plugins, node->data) == NULL))
1492 valid_plugins = g_list_delete_link (valid_plugins, node);
1494 node = next;
1497 /* If no plugin is installed yet, do something */
1498 if (valid_plugins &&
1499 (g_list_length (valid_plugins) == 1))
1501 /* If there is just one plugin, consider it selected */
1502 GObject *obj;
1503 plugin = valid_plugins->data;
1504 g_list_free (valid_plugins);
1506 /* Install and return it */
1507 plugin_set_update (plugin_manager, plugin, TRUE);
1508 obj = g_hash_table_lookup (priv->activated_plugins, plugin);
1510 return obj;
1512 else if (valid_plugins)
1514 /* Prompt the user to select one of these plugins */
1515 GObject *obj;
1516 obj = anjuta_plugin_manager_select_and_activate (plugin_manager,
1517 dgettext (GETTEXT_PACKAGE, "Select a plugin"),
1518 dgettext (GETTEXT_PACKAGE, "<b>Please select a plugin to activate</b>"),
1519 valid_plugins);
1520 g_list_free (valid_plugins);
1521 return obj;
1524 /* No plugin implementing this interface found */
1525 return NULL;
1529 * anjuta_plugin_manager_get_plugin_by_handle:
1530 * @plugin_manager: A #AnjutaPluginManager object
1531 * @handle: A #AnjutaPluginHandle
1533 * Searches the currently available plugins to find the one with the
1534 * specified handle. If the plugin is not yet loaded, it will be loaded
1535 * and activated.
1537 * Return value: The plugin object (subclass of #AnjutaPlugin)
1539 GObject *
1540 anjuta_plugin_manager_get_plugin_by_handle (AnjutaPluginManager *plugin_manager,
1541 AnjutaPluginHandle *handle)
1543 AnjutaPluginManagerPriv *priv;
1544 GObject *obj;
1546 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
1547 g_return_val_if_fail (handle != NULL, NULL);
1549 priv = plugin_manager->priv;
1550 obj = g_hash_table_lookup (priv->activated_plugins, handle);
1551 if (obj == NULL)
1553 plugin_set_update (plugin_manager, handle, TRUE);
1554 obj = g_hash_table_lookup (priv->activated_plugins, handle);
1557 return obj;
1560 static void
1561 on_activated_plugins_foreach (gpointer key, gpointer data, gpointer user_data)
1563 AnjutaPluginHandle *plugin = ANJUTA_PLUGIN_HANDLE (key);
1564 GList **active_plugins = (GList **)user_data;
1565 *active_plugins = g_list_prepend (*active_plugins,
1566 plugin);
1569 static void
1570 on_activated_plugin_objects_foreach (gpointer key, gpointer data, gpointer user_data)
1572 GList **active_plugins = (GList **)user_data;
1573 *active_plugins = g_list_prepend (*active_plugins,
1574 data);
1577 GList*
1578 anjuta_plugin_manager_get_active_plugins (AnjutaPluginManager *plugin_manager)
1580 GList *active_plugins = NULL;
1582 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
1583 g_hash_table_foreach (plugin_manager->priv->activated_plugins,
1584 on_activated_plugins_foreach,
1585 &active_plugins);
1586 return g_list_reverse (active_plugins);
1589 GList*
1590 anjuta_plugin_manager_get_active_plugin_objects (AnjutaPluginManager *plugin_manager)
1592 GList *active_plugins = NULL;
1594 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
1595 g_hash_table_foreach (plugin_manager->priv->activated_plugins,
1596 on_activated_plugin_objects_foreach,
1597 &active_plugins);
1598 return g_list_reverse (active_plugins);
1602 * anjuta_plugin_manager_unload_plugin_by_handle:
1603 * @plugin_manager: A #AnjutaPluginManager object
1604 * @handle: A #AnjutaPluginHandle
1606 * Unload the plugin corresponding to the given handle. If the plugin is
1607 * already unloaded, nothing will be done.
1609 * Return value: %TRUE is the plugin is unloaded. %FALSE if a corresponding
1610 * plugin does not exist or if the plugin cannot be unloaded.
1612 gboolean
1613 anjuta_plugin_manager_unload_plugin_by_handle (AnjutaPluginManager *plugin_manager,
1614 AnjutaPluginHandle *handle)
1616 AnjutaPluginManagerPriv *priv;
1618 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), FALSE);
1619 g_return_val_if_fail (handle != NULL, FALSE);
1621 priv = plugin_manager->priv;
1622 plugin_set_update (plugin_manager, handle, FALSE);
1624 /* Check if the plugin has been indeed unloaded */
1625 return g_hash_table_lookup (priv->activated_plugins, handle) == NULL;
1628 static gboolean
1629 find_plugin_for_object (gpointer key, gpointer value, gpointer data)
1631 if (value == data)
1633 g_object_set_data (G_OBJECT (data), "__plugin_plugin", key);
1634 return TRUE;
1636 return FALSE;
1640 * anjuta_plugin_manager_unload_plugin:
1641 * @plugin_manager: A #AnjutaPluginManager object
1642 * @plugin_object: A #AnjutaPlugin object
1644 * Unload the corresponding plugin. The plugin has to be loaded.
1646 * Return value: %TRUE if the plugin has been unloaded. %FALSE if the plugin is
1647 * already or cannot be unloaded.
1649 gboolean
1650 anjuta_plugin_manager_unload_plugin (AnjutaPluginManager *plugin_manager,
1651 GObject *plugin_object)
1653 AnjutaPluginManagerPriv *priv;
1654 AnjutaPluginHandle *plugin;
1656 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), FALSE);
1657 g_return_val_if_fail (ANJUTA_IS_PLUGIN (plugin_object), FALSE);
1659 priv = plugin_manager->priv;
1661 plugin = NULL;
1663 /* Find the plugin that correspond to this plugin object */
1664 g_hash_table_find (priv->activated_plugins, find_plugin_for_object,
1665 plugin_object);
1666 plugin = g_object_get_data (G_OBJECT (plugin_object), "__plugin_plugin");
1668 if (plugin)
1670 plugin_set_update (plugin_manager, plugin, FALSE);
1672 /* Check if the plugin has been indeed unloaded */
1673 if (!g_hash_table_lookup (priv->activated_plugins, plugin))
1674 return TRUE;
1675 else
1676 return FALSE;
1678 g_warning ("No plugin found with object \"%p\".", plugin_object);
1679 return FALSE;
1682 GList*
1683 anjuta_plugin_manager_list_query (AnjutaPluginManager *plugin_manager,
1684 GList *secs,
1685 GList *anames,
1686 GList *avalues)
1688 AnjutaPluginManagerPriv *priv;
1689 GList *selected_plugins = NULL;
1690 const gchar *sec;
1691 const gchar *aname;
1692 const gchar *avalue;
1693 GList *available;
1695 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
1697 priv = plugin_manager->priv;
1698 available = priv->available_plugins;
1700 if (secs == NULL)
1702 /* If no query is given, select all plugins */
1703 while (available)
1705 AnjutaPluginHandle *plugin = available->data;
1706 if ((g_hash_table_lookup (plugin_manager->priv->disable_plugins, plugin) == NULL) ||
1707 (g_hash_table_lookup (plugin_manager->priv->activated_plugins, plugin) != NULL))
1708 selected_plugins = g_list_prepend (selected_plugins, plugin);
1709 available = g_list_next (available);
1711 return g_list_reverse (selected_plugins);
1714 g_return_val_if_fail (secs != NULL, NULL);
1715 g_return_val_if_fail (anames != NULL, NULL);
1716 g_return_val_if_fail (avalues != NULL, NULL);
1718 for (;available; available = g_list_next (available))
1720 GList* s_node = secs;
1721 GList* n_node = anames;
1722 GList* v_node = avalues;
1724 gboolean satisfied = FALSE;
1726 AnjutaPluginHandle *plugin = available->data;
1727 AnjutaPluginDescription *desc =
1728 anjuta_plugin_handle_get_description (plugin);
1730 if ((g_hash_table_lookup (plugin_manager->priv->disable_plugins, plugin) != NULL) &&
1731 (g_hash_table_lookup (plugin_manager->priv->activated_plugins, plugin) == NULL))
1732 continue;
1734 while (s_node)
1736 gchar *val;
1737 GList *vals;
1738 GList *node;
1739 gboolean found = FALSE;
1741 satisfied = TRUE;
1743 sec = s_node->data;
1744 aname = n_node->data;
1745 avalue = v_node->data;
1747 if (!anjuta_plugin_description_get_string (desc, sec, aname, &val))
1749 satisfied = FALSE;
1750 break;
1753 vals = property_to_list (val);
1754 g_free (val);
1756 node = vals;
1757 while (node)
1759 if (strchr(node->data, '*') != NULL)
1761 // Star match.
1762 gchar **segments;
1763 gchar **seg_ptr;
1764 const gchar *cursor;
1766 segments = g_strsplit (node->data, "*", -1);
1768 seg_ptr = segments;
1769 cursor = avalue;
1770 while (*seg_ptr != NULL)
1772 if (strlen (*seg_ptr) > 0) {
1773 cursor = strstr (cursor, *seg_ptr);
1774 if (cursor == NULL)
1775 break;
1777 cursor += strlen (*seg_ptr);
1778 seg_ptr++;
1780 if (*seg_ptr == NULL)
1781 found = TRUE;
1782 g_strfreev (segments);
1784 else if (g_ascii_strcasecmp (node->data, avalue) == 0)
1786 // String match.
1787 found = TRUE;
1789 g_free (node->data);
1790 node = g_list_next (node);
1792 g_list_free (vals);
1793 if (!found)
1795 satisfied = FALSE;
1796 break;
1798 s_node = g_list_next (s_node);
1799 n_node = g_list_next (n_node);
1800 v_node = g_list_next (v_node);
1802 if (satisfied)
1804 selected_plugins = g_list_prepend (selected_plugins, plugin);
1805 /* DEBUG_PRINT ("Satisfied, Adding %s",
1806 anjuta_plugin_handle_get_name (plugin));*/
1810 return g_list_reverse (selected_plugins);
1813 GList*
1814 anjuta_plugin_manager_query (AnjutaPluginManager *plugin_manager,
1815 const gchar *section_name,
1816 const gchar *attribute_name,
1817 const gchar *attribute_value,
1818 ...)
1820 va_list var_args;
1821 GList *secs = NULL;
1822 GList *anames = NULL;
1823 GList *avalues = NULL;
1824 const gchar *sec;
1825 const gchar *aname;
1826 const gchar *avalue;
1827 GList *selected_plugins;
1830 if (section_name == NULL)
1832 /* If no query is given, select all plugins */
1833 return anjuta_plugin_manager_list_query (plugin_manager, NULL, NULL, NULL);
1836 g_return_val_if_fail (section_name != NULL, NULL);
1837 g_return_val_if_fail (attribute_name != NULL, NULL);
1838 g_return_val_if_fail (attribute_value != NULL, NULL);
1840 secs = g_list_prepend (secs, g_strdup (section_name));
1841 anames = g_list_prepend (anames, g_strdup (attribute_name));
1842 avalues = g_list_prepend (avalues, g_strdup (attribute_value));
1844 va_start (var_args, attribute_value);
1847 sec = va_arg (var_args, const gchar *);
1848 if (sec)
1850 aname = va_arg (var_args, const gchar *);
1851 if (aname)
1853 avalue = va_arg (var_args, const gchar *);
1854 if (avalue)
1856 secs = g_list_prepend (secs, g_strdup (sec));
1857 anames = g_list_prepend (anames, g_strdup (aname));
1858 avalues = g_list_prepend (avalues, g_strdup (avalue));
1863 while (sec);
1864 va_end (var_args);
1866 secs = g_list_reverse (secs);
1867 anames = g_list_reverse (anames);
1868 avalues = g_list_reverse (avalues);
1870 selected_plugins = anjuta_plugin_manager_list_query (plugin_manager,
1871 secs,
1872 anames,
1873 avalues);
1875 anjuta_util_glist_strings_free (secs);
1876 anjuta_util_glist_strings_free (anames);
1877 anjuta_util_glist_strings_free (avalues);
1879 return selected_plugins;
1882 enum {
1883 PIXBUF_COLUMN,
1884 PLUGIN_COLUMN,
1885 PLUGIN_HANDLE_COLUMN,
1886 N_COLUMNS
1889 static void
1890 on_plugin_list_row_activated (GtkTreeView *tree_view,
1891 GtkTreePath *path,
1892 GtkTreeViewColumn *column,
1893 GtkDialog *dialog)
1895 gtk_dialog_response (dialog, GTK_RESPONSE_OK);
1899 static void
1900 on_plugin_list_show (GtkTreeView *view,
1901 GtkDirectionType direction,
1902 GtkDialog *dialog)
1904 GtkTreeSelection *selection;
1905 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
1907 g_signal_emit_by_name (G_OBJECT (selection), "changed", GTK_DIALOG(dialog), NULL);
1911 static void
1912 on_plugin_list_selection_changed (GtkTreeSelection *tree_selection,
1913 GtkDialog *dialog)
1915 GtkContainer *action_area;
1916 GList *list;
1917 GtkButton *bt = NULL;
1919 action_area = GTK_CONTAINER (gtk_dialog_get_action_area (dialog));
1920 list = gtk_container_get_children (action_area);
1921 for (; list; list = list->next) {
1922 bt = list->data;
1923 if (!strcmp("gtk-ok", gtk_button_get_label (bt)))
1924 break;
1926 if (bt && gtk_tree_selection_get_selected (tree_selection, NULL, NULL))
1927 gtk_widget_set_sensitive ((GtkWidget *) bt, TRUE);
1928 else
1929 gtk_widget_set_sensitive ((GtkWidget *) bt, FALSE);
1930 g_list_free(list);
1934 * anjuta_plugin_manager_select:
1935 * @plugin_manager: #AnjutaPluginManager object
1936 * @title: Title of the dialog
1937 * @description: label shown on the dialog
1938 * @plugin_handles: List of #AnjutaPluginHandle
1940 * Show a dialog where the user can choose between the given plugins
1942 * Returns: The chosen plugin handle
1944 AnjutaPluginHandle *
1945 anjuta_plugin_manager_select (AnjutaPluginManager *plugin_manager,
1946 gchar *title, gchar *description,
1947 GList *plugin_handles)
1949 AnjutaPluginManagerPriv *priv;
1950 gint plugin_count;
1951 AnjutaPluginHandle *handle;
1952 GtkWidget *dlg;
1953 GtkTreeModel *model;
1954 GtkWidget *view;
1955 GtkTreeViewColumn *column;
1956 GtkCellRenderer *renderer;
1957 GList *node;
1958 GtkWidget *label;
1959 GtkWidget *content_area;
1960 GtkWidget *sc;
1961 GtkWidget *remember_checkbox;
1962 gint response;
1963 GtkTreeIter selected;
1964 GtkTreeSelection *selection;
1965 GtkTreeModel *store;
1966 GList *selection_ids = NULL;
1967 GString *remember_key = g_string_new ("");
1969 g_return_val_if_fail (title != NULL, NULL);
1970 g_return_val_if_fail (description != NULL, NULL);
1971 g_return_val_if_fail (plugin_handles != NULL, NULL);
1973 priv = plugin_manager->priv;
1975 plugin_count = g_list_length (plugin_handles);
1976 if (plugin_count <= 0)
1977 return NULL;
1979 dlg = gtk_dialog_new_with_buttons (title, GTK_WINDOW (priv->shell),
1980 GTK_DIALOG_DESTROY_WITH_PARENT,
1981 GTK_STOCK_CANCEL,
1982 GTK_RESPONSE_CANCEL,
1983 GTK_STOCK_OK, GTK_RESPONSE_OK,
1984 GTK_STOCK_HELP, GTK_RESPONSE_HELP,
1985 NULL);
1986 gtk_window_set_default_size (GTK_WINDOW (dlg), 520, 200 + (plugin_count > 6 ? 300 : plugin_count * 60));
1988 label = gtk_label_new (description);
1989 gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
1990 gtk_widget_show (label);
1991 content_area = gtk_dialog_get_content_area (GTK_DIALOG (dlg));
1992 gtk_box_pack_start (GTK_BOX (content_area), label,
1993 FALSE, FALSE, 5);
1995 sc = gtk_scrolled_window_new (NULL, NULL);
1996 gtk_widget_show (sc);
1997 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sc),
1998 GTK_SHADOW_IN);
1999 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sc),
2000 GTK_POLICY_AUTOMATIC,
2001 GTK_POLICY_AUTOMATIC);
2003 gtk_box_pack_start (GTK_BOX (content_area), sc,
2004 TRUE, TRUE, 5);
2006 model = GTK_TREE_MODEL (gtk_list_store_new (N_COLUMNS, GDK_TYPE_PIXBUF,
2007 G_TYPE_STRING, G_TYPE_POINTER));
2008 view = gtk_tree_view_new_with_model (model);
2009 gtk_widget_show (view);
2010 gtk_container_add (GTK_CONTAINER (sc), view);
2012 column = gtk_tree_view_column_new ();
2013 gtk_tree_view_column_set_sizing (column,
2014 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
2015 gtk_tree_view_column_set_title (column, dgettext (GETTEXT_PACKAGE, "Available Plugins"));
2017 renderer = gtk_cell_renderer_pixbuf_new ();
2018 gtk_tree_view_column_pack_start (column, renderer, FALSE);
2019 gtk_tree_view_column_add_attribute (column, renderer, "pixbuf",
2020 PIXBUF_COLUMN);
2022 renderer = gtk_cell_renderer_text_new ();
2023 g_object_set (G_OBJECT (renderer), "wrap-mode", PANGO_WRAP_WORD_CHAR,
2024 "wrap-width", 450, NULL);
2025 gtk_tree_view_column_pack_start (column, renderer, TRUE);
2026 gtk_tree_view_column_add_attribute (column, renderer, "markup",
2027 PLUGIN_COLUMN);
2029 gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
2030 gtk_tree_view_set_expander_column (GTK_TREE_VIEW (view), column);
2032 g_signal_connect (view, "row-activated",
2033 G_CALLBACK (on_plugin_list_row_activated),
2034 GTK_DIALOG(dlg));
2035 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
2036 g_signal_connect(selection, "changed",
2037 G_CALLBACK(on_plugin_list_selection_changed),
2038 GTK_DIALOG(dlg));
2039 g_signal_connect(view, "focus",
2040 G_CALLBACK(on_plugin_list_show),
2041 GTK_DIALOG(dlg));
2043 remember_checkbox =
2044 gtk_check_button_new_with_label (dgettext (GETTEXT_PACKAGE, "Remember this selection"));
2045 gtk_container_set_border_width (GTK_CONTAINER (remember_checkbox), 10);
2046 gtk_widget_show (remember_checkbox);
2047 gtk_box_pack_start (GTK_BOX (content_area), remember_checkbox,
2048 FALSE, FALSE, 0);
2050 node = plugin_handles;
2051 while (node)
2053 const gchar *filename;
2054 GdkPixbuf *icon_pixbuf = NULL;
2055 const gchar *name = NULL;
2056 AnjutaPluginDescription *desc;
2058 handle = (AnjutaPluginHandle*)node->data;
2060 filename = anjuta_plugin_handle_get_icon_path (handle);
2061 if (filename != NULL)
2063 icon_pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
2064 if (!icon_pixbuf)
2065 g_warning ("Plugin does not define Icon: No such file %s",
2066 filename);
2068 else
2070 g_warning ("Plugin does not define Icon attribute");
2073 name = anjuta_plugin_handle_get_name (handle);
2074 desc = anjuta_plugin_handle_get_description (handle);
2075 if ((name != NULL) && (desc != NULL))
2077 gchar *plugin_desc;
2078 GtkTreeIter iter;
2079 gchar *text;
2081 if (!anjuta_plugin_description_get_locale_string (desc,
2082 "Anjuta Plugin",
2083 "Description",
2084 &plugin_desc))
2086 g_warning ("Plugin does not define Description attribute");
2088 text = g_markup_printf_escaped ("<span size=\"larger\" weight=\"bold\">%s</span>\n%s", name, plugin_desc);
2089 g_free (plugin_desc);
2091 gtk_list_store_append (GTK_LIST_STORE (model), &iter);
2092 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
2093 PLUGIN_COLUMN, text,
2094 PLUGIN_HANDLE_COLUMN, handle, -1);
2095 if (icon_pixbuf) {
2096 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
2097 PIXBUF_COLUMN, icon_pixbuf, -1);
2099 g_free (text);
2101 selection_ids = g_list_prepend (selection_ids, (gpointer)anjuta_plugin_handle_get_id (handle));
2103 else
2105 g_warning ("Plugin does not define Name attribute");
2108 if (icon_pixbuf)
2109 g_object_unref (icon_pixbuf);
2111 node = g_list_next (node);
2114 /* Prepare remembering key */
2115 selection_ids = g_list_sort (selection_ids,
2116 (GCompareFunc)strcmp);
2117 node = selection_ids;
2118 while (node)
2120 g_string_append (remember_key, (gchar*)node->data);
2121 g_string_append (remember_key, ",");
2122 node = g_list_next (node);
2124 g_list_free (selection_ids);
2126 /* Find if the selection is remembered */
2127 handle = g_hash_table_lookup (priv->remember_plugins, remember_key->str);
2128 if (handle)
2130 g_string_free (remember_key, TRUE);
2131 gtk_widget_destroy (dlg);
2132 return handle;
2135 /* Prompt dialog */
2136 response = gtk_dialog_run (GTK_DIALOG (dlg));
2137 switch (response)
2139 case GTK_RESPONSE_OK:
2140 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
2141 if (gtk_tree_selection_get_selected (selection, &store,
2142 &selected))
2144 gtk_tree_model_get (model, &selected,
2145 PLUGIN_HANDLE_COLUMN, &handle, -1);
2146 if (handle)
2148 /* Remember selection */
2149 if (gtk_toggle_button_get_active
2150 (GTK_TOGGLE_BUTTON (remember_checkbox)))
2152 /* DEBUG_PRINT ("Remembering selection '%s'",
2153 remember_key->str);*/
2154 g_hash_table_insert (priv->remember_plugins,
2155 g_strdup (remember_key->str), handle);
2157 g_string_free (remember_key, TRUE);
2158 gtk_widget_destroy (dlg);
2159 return handle;
2162 break;
2164 g_string_free (remember_key, TRUE);
2165 gtk_widget_destroy (dlg);
2166 return NULL;
2169 GObject*
2170 anjuta_plugin_manager_select_and_activate (AnjutaPluginManager *plugin_manager,
2171 gchar *title,
2172 gchar *description,
2173 GList *plugin_handles)
2175 AnjutaPluginHandle *handle;
2176 GObject *plugin = NULL;
2178 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
2180 handle = anjuta_plugin_manager_select (plugin_manager, title, description,
2181 plugin_handles);
2182 plugin = anjuta_plugin_manager_get_plugin_by_handle (plugin_manager, handle);
2184 return plugin;
2188 * anjuta_plugin_manager_get_plugin_handle:
2189 * @plugin_manager: #AnjutaPluginManager object
2190 * @plugin: #AnjutaPlugin object
2192 * Get the handle corresponding to the plugin or %NULL if the plugin is not
2193 * activated.
2195 * Returns: (transfer none) (allow-none): A #AnjutaPluginHandle or %NULL.
2197 AnjutaPluginHandle*
2198 anjuta_plugin_manager_get_plugin_handle (AnjutaPluginManager *plugin_manager,
2199 GObject *plugin)
2201 GHashTableIter iter;
2202 gpointer key, value;
2204 g_hash_table_iter_init (&iter, plugin_manager->priv->activated_plugins);
2205 while (g_hash_table_iter_next (&iter, &key, &value))
2207 if (G_OBJECT(value) == plugin)
2209 return ANJUTA_PLUGIN_HANDLE (key);
2213 return NULL;
2217 /* Plugin manager */
2219 static void
2220 anjuta_plugin_manager_init (AnjutaPluginManager *object)
2222 object->priv = g_new0 (AnjutaPluginManagerPriv, 1);
2223 object->priv->plugins_by_name = g_hash_table_new (g_str_hash, g_str_equal);
2224 object->priv->plugins_by_interfaces = g_hash_table_new_full (g_str_hash,
2225 g_str_equal,
2226 NULL,
2227 (GDestroyNotify) g_list_free);
2228 object->priv->plugins_by_description = g_hash_table_new (g_direct_hash,
2229 g_direct_equal);
2230 object->priv->activated_plugins = g_hash_table_new_full (g_direct_hash, g_direct_equal,
2231 NULL, g_object_unref);
2232 object->priv->plugins_cache = g_hash_table_new_full (g_direct_hash, g_direct_equal,
2233 NULL, g_object_unref);
2234 object->priv->remember_plugins = g_hash_table_new_full (g_str_hash,
2235 g_str_equal,
2236 NULL, NULL);
2237 object->priv->disable_plugins = g_hash_table_new (g_direct_hash,
2238 g_direct_equal);
2241 static void
2242 anjuta_plugin_manager_dispose (GObject *object)
2244 AnjutaPluginManagerPriv *priv;
2245 priv = ANJUTA_PLUGIN_MANAGER (object)->priv;
2247 if (priv->available_plugins)
2249 g_list_foreach (priv->available_plugins, (GFunc)g_object_unref, NULL);
2250 g_list_free (priv->available_plugins);
2251 priv->available_plugins = NULL;
2253 if (priv->activated_plugins)
2255 g_hash_table_destroy (priv->activated_plugins);
2256 priv->activated_plugins = NULL;
2258 if (priv->plugins_cache)
2260 g_hash_table_destroy (priv->plugins_cache);
2261 priv->plugins_cache = NULL;
2263 if (priv->disable_plugins)
2265 g_hash_table_destroy (priv->disable_plugins);
2266 priv->disable_plugins = NULL;
2268 if (priv->plugins_by_name)
2270 g_hash_table_destroy (priv->plugins_by_name);
2271 priv->plugins_by_name = NULL;
2273 if (priv->plugins_by_description)
2275 g_hash_table_destroy (priv->plugins_by_description);
2276 priv->plugins_by_description = NULL;
2278 if (priv->plugins_by_interfaces)
2280 g_hash_table_destroy (priv->plugins_by_interfaces);
2281 priv->plugins_by_interfaces = NULL;
2283 if (priv->plugin_dirs)
2285 g_list_foreach (priv->plugin_dirs, (GFunc)g_free, NULL);
2286 g_list_free (priv->plugin_dirs);
2287 priv->plugin_dirs = NULL;
2289 #if 0
2290 if (anjuta_c_plugin_factory)
2292 g_object_unref (anjuta_c_plugin_factory);
2293 anjuta_c_plugin_factory = NULL;
2295 #endif
2296 G_OBJECT_CLASS (parent_class)->dispose (object);
2299 static void
2300 anjuta_plugin_manager_set_property (GObject *object, guint prop_id,
2301 const GValue *value, GParamSpec *pspec)
2303 AnjutaPluginManagerPriv *priv;
2305 g_return_if_fail (ANJUTA_IS_PLUGIN_MANAGER (object));
2306 priv = ANJUTA_PLUGIN_MANAGER (object)->priv;
2308 switch (prop_id)
2310 case PROP_STATUS:
2311 priv->status = g_value_get_object (value);
2312 break;
2313 case PROP_SHELL:
2314 priv->shell = g_value_get_object (value);
2315 break;
2316 default:
2317 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2318 break;
2322 static void
2323 anjuta_plugin_manager_get_property (GObject *object, guint prop_id,
2324 GValue *value, GParamSpec *pspec)
2326 AnjutaPluginManagerPriv *priv;
2328 g_return_if_fail (ANJUTA_IS_PLUGIN_MANAGER (object));
2329 priv = ANJUTA_PLUGIN_MANAGER (object)->priv;
2331 switch (prop_id)
2333 case PROP_SHELL:
2334 g_value_set_object (value, priv->shell);
2335 break;
2336 case PROP_STATUS:
2337 g_value_set_object (value, priv->status);
2338 break;
2339 case PROP_AVAILABLE_PLUGINS:
2340 g_value_set_pointer (value, priv->available_plugins);
2341 break;
2342 case PROP_ACTIVATED_PLUGINS:
2343 g_value_set_pointer (value, priv->activated_plugins);
2344 break;
2345 default:
2346 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2347 break;
2350 static void
2351 anjuta_plugin_manager_plugin_activated (AnjutaPluginManager *self,
2352 AnjutaPluginHandle* handle,
2353 GObject *plugin)
2355 /* TODO: Add default signal handler implementation here */
2358 static void
2359 anjuta_plugin_manager_plugin_deactivated (AnjutaPluginManager *self,
2360 AnjutaPluginHandle* handle,
2361 GObject *plugin)
2363 /* TODO: Add default signal handler implementation here */
2366 static void
2367 anjuta_plugin_manager_class_init (AnjutaPluginManagerClass *klass)
2369 GObjectClass* object_class = G_OBJECT_CLASS (klass);
2370 parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
2372 object_class->dispose = anjuta_plugin_manager_dispose;
2373 object_class->set_property = anjuta_plugin_manager_set_property;
2374 object_class->get_property = anjuta_plugin_manager_get_property;
2376 klass->plugin_activated = anjuta_plugin_manager_plugin_activated;
2377 klass->plugin_deactivated = anjuta_plugin_manager_plugin_deactivated;
2379 g_object_class_install_property (object_class,
2380 PROP_PROFILES,
2381 g_param_spec_pointer ("profiles",
2382 dgettext (GETTEXT_PACKAGE, "Profiles"),
2383 dgettext (GETTEXT_PACKAGE, "Current stack of profiles"),
2384 G_PARAM_READABLE));
2385 g_object_class_install_property (object_class,
2386 PROP_AVAILABLE_PLUGINS,
2387 g_param_spec_pointer ("available-plugins",
2388 dgettext (GETTEXT_PACKAGE, "Available plugins"),
2389 dgettext (GETTEXT_PACKAGE, "Currently available plugins found in plugin paths"),
2390 G_PARAM_READABLE));
2392 g_object_class_install_property (object_class,
2393 PROP_ACTIVATED_PLUGINS,
2394 g_param_spec_pointer ("activated-plugins",
2395 dgettext (GETTEXT_PACKAGE, "Activated plugins"),
2396 dgettext (GETTEXT_PACKAGE, "Currently activated plugins"),
2397 G_PARAM_READABLE));
2398 g_object_class_install_property (object_class,
2399 PROP_SHELL,
2400 g_param_spec_object ("shell",
2401 dgettext (GETTEXT_PACKAGE, "Anjuta Shell"),
2402 dgettext (GETTEXT_PACKAGE, "Anjuta shell for which the plugins are made"),
2403 G_TYPE_OBJECT,
2404 G_PARAM_READABLE |
2405 G_PARAM_WRITABLE |
2406 G_PARAM_CONSTRUCT));
2407 g_object_class_install_property (object_class,
2408 PROP_STATUS,
2409 g_param_spec_object ("status",
2410 dgettext (GETTEXT_PACKAGE, "Anjuta Status"),
2411 dgettext (GETTEXT_PACKAGE, "Anjuta status to use in loading and unloading of plugins"),
2412 ANJUTA_TYPE_STATUS,
2413 G_PARAM_READABLE |
2414 G_PARAM_WRITABLE |
2415 G_PARAM_CONSTRUCT));
2417 plugin_manager_signals[PLUGIN_ACTIVATED] =
2418 g_signal_new ("plugin-activated",
2419 G_OBJECT_CLASS_TYPE (klass),
2420 G_SIGNAL_RUN_FIRST,
2421 G_STRUCT_OFFSET (AnjutaPluginManagerClass,
2422 plugin_activated),
2423 NULL, NULL,
2424 anjuta_cclosure_marshal_VOID__POINTER_OBJECT,
2425 G_TYPE_NONE, 2,
2426 G_TYPE_POINTER, ANJUTA_TYPE_PLUGIN);
2428 plugin_manager_signals[PLUGIN_DEACTIVATED] =
2429 g_signal_new ("plugin-deactivated",
2430 G_OBJECT_CLASS_TYPE (klass),
2431 G_SIGNAL_RUN_FIRST,
2432 G_STRUCT_OFFSET (AnjutaPluginManagerClass,
2433 plugin_deactivated),
2434 NULL, NULL,
2435 anjuta_cclosure_marshal_VOID__POINTER_OBJECT,
2436 G_TYPE_NONE, 2,
2437 G_TYPE_POINTER, ANJUTA_TYPE_PLUGIN);
2440 GType
2441 anjuta_plugin_manager_get_type (void)
2443 static GType our_type = 0;
2445 if(our_type == 0)
2447 static const GTypeInfo our_info =
2449 sizeof (AnjutaPluginManagerClass), /* class_size */
2450 (GBaseInitFunc) NULL, /* base_init */
2451 (GBaseFinalizeFunc) NULL, /* base_finalize */
2452 (GClassInitFunc) anjuta_plugin_manager_class_init, /* class_init */
2453 (GClassFinalizeFunc) NULL, /* class_finalize */
2454 NULL /* class_data */,
2455 sizeof (AnjutaPluginManager), /* instance_size */
2456 0, /* n_preallocs */
2457 (GInstanceInitFunc) anjuta_plugin_manager_init, /* instance_init */
2458 NULL /* value_table */
2460 our_type = g_type_register_static (G_TYPE_OBJECT,
2461 "AnjutaPluginManager",
2462 &our_info, 0);
2465 return our_type;
2468 AnjutaPluginManager*
2469 anjuta_plugin_manager_new (GObject *shell, AnjutaStatus *status,
2470 GList* plugins_directories)
2472 GObject *manager_object;
2473 AnjutaPluginManager *plugin_manager;
2474 GList *cycles = NULL;
2475 const char *gnome2_path;
2476 char **pathv;
2477 char **p;
2478 GList *node;
2479 GList *plugin_dirs = NULL;
2481 /* Initialize the anjuta plugin system */
2482 manager_object = g_object_new (ANJUTA_TYPE_PLUGIN_MANAGER,
2483 "shell", shell, "status", status, NULL);
2484 plugin_manager = ANJUTA_PLUGIN_MANAGER (manager_object);
2486 if (anjuta_plugin_factory == NULL)
2488 anjuta_plugin_factory = anjuta_c_plugin_factory_new ();
2491 gnome2_path = g_getenv ("GNOME2_PATH");
2492 if (gnome2_path) {
2493 pathv = g_strsplit (gnome2_path, ":", 1);
2495 for (p = pathv; *p != NULL; p++) {
2496 char *path = g_strdup (*p);
2497 plugin_dirs = g_list_prepend (plugin_dirs, path);
2499 g_strfreev (pathv);
2502 node = plugins_directories;
2503 while (node) {
2504 if (!node->data)
2505 continue;
2506 char *path = g_strdup (node->data);
2507 plugin_dirs = g_list_prepend (plugin_dirs, path);
2508 node = g_list_next (node);
2510 plugin_dirs = g_list_reverse (plugin_dirs);
2511 /* load_plugins (); */
2513 node = plugin_dirs;
2514 while (node)
2516 load_plugins_from_directory (plugin_manager, (char*)node->data);
2517 node = g_list_next (node);
2519 resolve_dependencies (plugin_manager, &cycles);
2520 g_list_foreach(plugin_dirs, (GFunc) g_free, NULL);
2521 g_list_free(plugin_dirs);
2522 return plugin_manager;
2525 void
2526 anjuta_plugin_manager_activate_plugins (AnjutaPluginManager *plugin_manager,
2527 GList *plugins_to_activate)
2529 AnjutaPluginManagerPriv *priv;
2530 GList *node;
2532 priv = plugin_manager->priv;
2534 /* Freeze shell operations */
2535 anjuta_shell_freeze (ANJUTA_SHELL (priv->shell), NULL);
2536 if (plugins_to_activate)
2538 anjuta_status_progress_add_ticks (ANJUTA_STATUS (priv->status),
2539 g_list_length (plugins_to_activate));
2541 node = plugins_to_activate;
2542 while (node)
2544 AnjutaPluginHandle *handle;
2545 const gchar *filename;
2546 GdkPixbuf *icon_pixbuf = NULL;
2547 const gchar *name;
2548 gchar*label= NULL;
2550 handle = node->data;
2552 filename = anjuta_plugin_handle_get_icon_path (handle);
2553 if (filename != NULL)
2555 icon_pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
2556 if (!icon_pixbuf)
2557 g_warning ("Plugin does not define Icon: No such file %s",
2558 filename);
2561 name = anjuta_plugin_handle_get_name (handle);
2562 if (name != NULL)
2564 label = g_strconcat (dgettext (GETTEXT_PACKAGE, "Loading:"), " ", name, "...", NULL);
2567 anjuta_status_progress_tick (ANJUTA_STATUS (priv->status),
2568 icon_pixbuf, label);
2569 g_free (label);
2570 if (icon_pixbuf)
2571 g_object_unref (icon_pixbuf);
2573 /* Activate the plugin */
2574 anjuta_plugin_manager_get_plugin_by_handle (plugin_manager, handle);
2576 node = g_list_next (node);
2579 /* Thaw shell operations */
2580 anjuta_shell_thaw (ANJUTA_SHELL (priv->shell), NULL);
2583 static void
2584 on_collect (gpointer key, gpointer value, gpointer user_data)
2586 const gchar *id;
2587 gchar *query = (gchar*) key;
2588 AnjutaPluginHandle *handle = (AnjutaPluginHandle *) value;
2589 GString *write_buffer = (GString *) user_data;
2591 id = anjuta_plugin_handle_get_id (handle);
2592 g_string_append_printf (write_buffer, "%s=%s;", query, id);
2596 * anjuta_plugin_manager_get_remembered_plugins:
2597 * @plugin_manager: A #AnjutaPluginManager object
2599 * Get the list of plugins loaded when there is a choice between several
2600 * ones without asking the user.
2602 * The list format is returned as a string with the format detailed in
2603 * anjuta_plugin_manager_set_remembered_plugins().
2605 * Return value: (transfer full): a newly-allocated string that must be freed
2606 * with g_free().
2609 gchar*
2610 anjuta_plugin_manager_get_remembered_plugins (AnjutaPluginManager *plugin_manager)
2612 AnjutaPluginManagerPriv *priv;
2613 GString *write_buffer = g_string_new ("");
2615 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), FALSE);
2617 priv = plugin_manager->priv;
2618 g_hash_table_foreach (priv->remember_plugins, on_collect,
2619 write_buffer);
2620 return g_string_free (write_buffer, FALSE);
2624 * anjuta_plugin_manager_set_remembered_plugins:
2625 * @plugin_manager: A #AnjutaPluginManager object
2626 * @remembered_plugins: A list of prefered plugins
2628 * Set the list of plugins loaded when there is a choice between several
2629 * ones without asking the user.
2630 * The list is a string composed of elements separated by ';'. Each element
2631 * is defined with "key=value", where key is the list of possible plugins and
2632 * the value is the choosen plugin.
2634 * By the example the following element
2635 * <programlisting>
2636 * anjuta-symbol-browser:SymbolBrowserPlugin,anjuta-symbol-db:SymbolDBPlugin,=anjuta-symbol-db:SymbolDBPlugin;
2637 * </programlisting>
2638 * means if Anjuta has to choose between SymbolBrowserPlugin and
2639 * SymbolDBPlugin, it will choose SymbolDBPlugin.
2641 void
2642 anjuta_plugin_manager_set_remembered_plugins (AnjutaPluginManager *plugin_manager,
2643 const gchar *remembered_plugins)
2645 AnjutaPluginManagerPriv *priv;
2646 gchar **strv_lines, **line_idx;
2648 g_return_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager));
2649 g_return_if_fail (remembered_plugins != NULL);
2651 priv = plugin_manager->priv;
2653 g_hash_table_remove_all (priv->remember_plugins);
2655 strv_lines = g_strsplit (remembered_plugins, ";", -1);
2656 line_idx = strv_lines;
2657 while (*line_idx)
2659 gchar **strv_keyvals;
2660 strv_keyvals = g_strsplit (*line_idx, "=", -1);
2661 if (strv_keyvals && strv_keyvals[0] && strv_keyvals[1])
2663 AnjutaPluginHandle *handle;
2664 handle = g_hash_table_lookup (priv->plugins_by_name,
2665 strv_keyvals[1]);
2666 if (handle)
2668 g_hash_table_insert (priv->remember_plugins,
2669 g_strdup (strv_keyvals[0]), handle);
2671 g_strfreev (strv_keyvals);
2673 line_idx++;
2675 g_strfreev (strv_lines);
2679 * anjuta_plugin_manager_set_disable_plugins:
2680 * @plugin_manager: A #AnjutaPluginManager object
2681 * @plugins_list: A list of plugins to disable or reenable
2682 * @hide: %TRUE to disable, %FALSE to re-enable plugins in the list
2684 * Disable or re-enable plugins. By default, all plugins are enabled but they
2685 * can be disabled and they will not be proposed when a plugin is requested.
2687 void
2688 anjuta_plugin_manager_set_disable_plugins (AnjutaPluginManager *plugin_manager,
2689 GList *plugin_handles,
2690 gboolean disable)
2692 GList *item;
2694 if (disable)
2696 for (item = g_list_first (plugin_handles); item != NULL; item = g_list_next (item))
2698 g_hash_table_add (plugin_manager->priv->disable_plugins, item->data);
2701 else
2703 for (item = g_list_first (plugin_handles); item != NULL; item = g_list_next (item))
2705 g_hash_table_remove (plugin_manager->priv->disable_plugins, item->data);