libanjuta: Fix some warnings and made AnjutaPluginDescription a boxed type
[anjuta.git] / libanjuta / anjuta-plugin-manager.c
blobbc74ededcd24dbcc57813ad407f199b7373471d9
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 GHashTable* 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 (plugin_object));
501 if (g_hash_table_lookup (priv->plugins_cache, plugin))
502 g_hash_table_remove (priv->plugins_cache, plugin);
504 g_signal_emit_by_name (plugin_manager, "plugin-activated",
505 anjuta_plugin_handle_get_description (plugin),
506 plugin_object);
509 static void
510 on_plugin_deactivated (AnjutaPlugin *plugin_object, AnjutaPluginHandle *plugin)
512 AnjutaPluginManager *plugin_manager;
513 AnjutaPluginManagerPriv *priv;
515 /* FIXME: Pass plugin_manager directly in signal arguments */
516 plugin_manager = anjuta_shell_get_plugin_manager (plugin_object->shell, NULL);
518 g_return_if_fail (plugin_manager != NULL);
520 priv = plugin_manager->priv;
522 g_hash_table_insert (priv->plugins_cache, plugin, G_OBJECT (plugin_object));
523 g_hash_table_remove (priv->activated_plugins, plugin);
525 g_signal_emit_by_name (plugin_manager, "plugin-deactivated",
526 anjuta_plugin_handle_get_description (plugin),
527 plugin_object);
530 static AnjutaPlugin*
531 activate_plugin (AnjutaPluginManager *plugin_manager,
532 AnjutaPluginHandle *handle, GError **error)
534 AnjutaPluginManagerPriv *priv;
535 IAnjutaPluginFactory* factory;
536 AnjutaPlugin *plugin;
537 const gchar *language;
539 priv = plugin_manager->priv;
541 language = anjuta_plugin_handle_get_language (handle);
543 factory = get_plugin_factory (plugin_manager, language, error);
544 if (factory == NULL) return NULL;
546 plugin = ianjuta_plugin_factory_new_plugin (factory, handle, ANJUTA_SHELL (priv->shell), error);
548 if (plugin == NULL)
550 return NULL;
552 g_signal_connect (plugin, "activated",
553 G_CALLBACK (on_plugin_activated), handle);
554 g_signal_connect (plugin, "deactivated",
555 G_CALLBACK (on_plugin_deactivated), handle);
557 return plugin;
560 static gboolean
561 g_hashtable_foreach_true (gpointer key, gpointer value, gpointer user_data)
563 return TRUE;
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 priv->available_plugins = g_list_reverse (priv->available_plugins);
576 if (g_hash_table_size (priv->activated_plugins) > 0)
578 GList *node;
579 node = priv->available_plugins;
580 while (node)
582 AnjutaPluginHandle *selected_plugin = node->data;
583 if (g_hash_table_lookup (priv->activated_plugins, selected_plugin))
585 plugin_set_update (plugin_manager, selected_plugin, FALSE);
586 /* DEBUG_PRINT ("Unloading plugin: %s",
587 anjuta_plugin_handle_get_id (selected_plugin));
590 node = g_list_next (node);
592 g_hash_table_foreach_remove (priv->activated_plugins,
593 g_hashtable_foreach_true, NULL);
595 if (g_hash_table_size (priv->plugins_cache) > 0)
597 GList *node;
598 node = priv->available_plugins;
599 while (node)
601 GObject *plugin_obj;
602 AnjutaPluginHandle *selected_plugin = node->data;
604 plugin_obj = g_hash_table_lookup (priv->plugins_cache,
605 selected_plugin);
606 if (plugin_obj)
608 /* DEBUG_PRINT ("Destroying plugin: %s",
609 anjuta_plugin_handle_get_id (selected_plugin));
611 g_object_unref (plugin_obj);
613 node = g_list_next (node);
615 g_hash_table_foreach_remove (priv->plugins_cache,
616 g_hashtable_foreach_true, NULL);
618 priv->available_plugins = g_list_reverse (priv->available_plugins);
622 static gboolean
623 should_unload (GHashTable *activated_plugins, AnjutaPluginHandle *plugin_to_unload,
624 AnjutaPluginHandle *plugin)
626 GObject *plugin_obj = g_hash_table_lookup (activated_plugins, plugin);
628 if (!plugin_obj)
629 return FALSE;
631 if (plugin_to_unload == plugin)
632 return TRUE;
634 gboolean dependent =
635 GPOINTER_TO_INT (g_hash_table_lookup (anjuta_plugin_handle_get_dependents (plugin),
636 plugin));
637 return dependent;
640 static gboolean
641 should_load (GHashTable *activated_plugins, AnjutaPluginHandle *plugin_to_load,
642 AnjutaPluginHandle *plugin)
644 GObject *plugin_obj = g_hash_table_lookup (activated_plugins, plugin);
646 if (plugin_obj)
647 return FALSE;
649 if (plugin_to_load == plugin)
650 return anjuta_plugin_handle_get_can_load (plugin);
652 gboolean dependency =
653 GPOINTER_TO_INT (g_hash_table_lookup (anjuta_plugin_handle_get_dependencies (plugin_to_load),
654 plugin));
655 return (dependency && anjuta_plugin_handle_get_can_load (plugin));
658 static AnjutaPluginHandle *
659 plugin_for_iter (GtkListStore *store, GtkTreeIter *iter)
661 AnjutaPluginHandle *plugin;
663 gtk_tree_model_get (GTK_TREE_MODEL (store), iter, COL_PLUGIN, &plugin, -1);
664 return plugin;
667 static void
668 update_enabled (GtkTreeModel *model, GHashTable *activated_plugins)
670 GtkTreeIter iter;
672 if (gtk_tree_model_get_iter_first (model, &iter)) {
673 do {
674 AnjutaPluginHandle *plugin;
675 GObject *plugin_obj;
676 gboolean installed;
678 plugin = plugin_for_iter(GTK_LIST_STORE(model), &iter);
679 plugin_obj = g_hash_table_lookup (activated_plugins, plugin);
680 installed = (plugin_obj != NULL) ? TRUE : FALSE;
681 gtk_tree_model_get (model, &iter, COL_PLUGIN, &plugin, -1);
682 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
683 COL_ENABLED, installed, -1);
684 } while (gtk_tree_model_iter_next (model, &iter));
688 static GHashTable*
689 plugin_set_update (AnjutaPluginManager *plugin_manager,
690 AnjutaPluginHandle* selected_plugin,
691 gboolean load)
693 AnjutaPluginManagerPriv *priv;
694 GObject *plugin_obj;
695 GList *l;
697 priv = plugin_manager->priv;
698 plugin_obj = g_hash_table_lookup (priv->activated_plugins, selected_plugin);
700 if (plugin_obj && load)
702 g_warning ("Trying to install already installed plugin '%s'",
703 anjuta_plugin_handle_get_name (selected_plugin));
704 return priv->activated_plugins;
706 if (!plugin_obj && !load)
708 g_warning ("Trying to uninstall a not installed plugin '%s'",
709 anjuta_plugin_handle_get_name (selected_plugin));
710 return priv->activated_plugins;
713 if (priv->status)
714 anjuta_status_busy_push (priv->status);
716 if (!load)
718 /* reverse priv->available_plugins when unloading, so that plugins are
719 * unloaded in the right order */
720 priv->available_plugins = g_list_reverse (priv->available_plugins);
722 for (l = priv->available_plugins; l != NULL; l = l->next)
724 AnjutaPluginHandle *plugin = l->data;
725 if (should_unload (priv->activated_plugins, selected_plugin, plugin))
727 /* FIXME: Unload the class and sharedlib if possible */
728 AnjutaPlugin *anjuta_plugin = ANJUTA_PLUGIN (plugin_obj);
729 if (!anjuta_plugin_deactivate (ANJUTA_PLUGIN (anjuta_plugin)))
731 anjuta_util_dialog_info (GTK_WINDOW (priv->shell),
732 dgettext (GETTEXT_PACKAGE, "Plugin '%s' does not want to be deactivated"),
733 anjuta_plugin_handle_get_name (plugin));
737 priv->available_plugins = g_list_reverse (priv->available_plugins);
739 else
741 for (l = priv->available_plugins; l != NULL; l = l->next)
743 AnjutaPluginHandle *plugin = l->data;
744 if (should_load (priv->activated_plugins, selected_plugin, plugin))
746 AnjutaPlugin *plugin_obj;
747 GError *error = NULL;
748 plugin_obj = g_hash_table_lookup (priv->plugins_cache, plugin);
749 if (!plugin_obj)
751 plugin_obj = activate_plugin (plugin_manager, plugin,
752 &error);
755 if (plugin_obj)
757 anjuta_plugin_activate (ANJUTA_PLUGIN (plugin_obj));
759 else
761 if (error)
763 gchar* message = g_strdup_printf (dgettext (GETTEXT_PACKAGE, "Could not load %s\n"
764 "This usually means that your installation is corrupted. The "
765 "error message leading to this was:\n%s"),
766 anjuta_plugin_handle_get_name (selected_plugin),
767 error->message);
768 anjuta_util_dialog_error (GTK_WINDOW(plugin_manager->priv->shell),
769 message);
770 g_error_free (error);
771 g_free(message);
777 if (priv->status)
778 anjuta_status_busy_pop (priv->status);
779 return priv->activated_plugins;
782 static void
783 plugin_toggled (GtkCellRendererToggle *cell, char *path_str, gpointer data)
785 AnjutaPluginManager *plugin_manager;
786 AnjutaPluginManagerPriv *priv;
787 GtkListStore *store = GTK_LIST_STORE (data);
788 GtkTreeIter iter;
789 GtkTreePath *path;
790 AnjutaPluginHandle *plugin;
791 gboolean enabled;
792 GList *activated_plugins;
793 GList *node;
794 AnjutaPlugin* plugin_object;
796 path = gtk_tree_path_new_from_string (path_str);
798 plugin_manager = g_object_get_data (G_OBJECT (store), "plugin-manager");
799 priv = plugin_manager->priv;
801 gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path);
802 gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
803 COL_ENABLED, &enabled,
804 COL_PLUGIN, &plugin,
805 -1);
807 /* Activate one plugin can force the loading of other ones, instead of
808 * searching which plugins have to be activated, we just unmerge all
809 * current plugins and merge all plugins after the modification */
811 /* unmerge all plugins */
812 activated_plugins = g_hash_table_get_values (priv->activated_plugins);
813 for (node = g_list_first (activated_plugins); node != NULL; node = g_list_next (node))
815 plugin_object = (AnjutaPlugin *)node->data;
816 if (plugin_object &&
817 IANJUTA_IS_PREFERENCES(plugin_object))
819 ianjuta_preferences_unmerge (IANJUTA_PREFERENCES (plugin_object),
820 anjuta_shell_get_preferences (ANJUTA_SHELL (priv->shell), NULL),
821 NULL);
824 g_list_free (activated_plugins);
826 plugin_set_update (plugin_manager, plugin, !enabled);
828 /* Make sure that it appears in the preferences. This method
829 can only be called when the preferences dialog is active so
830 it should be save
832 activated_plugins = g_hash_table_get_values (priv->activated_plugins);
833 for (node = g_list_first (activated_plugins); node != NULL; node = g_list_next (node))
835 plugin_object = (AnjutaPlugin *)node->data;
836 if (plugin_object &&
837 IANJUTA_IS_PREFERENCES(plugin_object))
839 ianjuta_preferences_merge (IANJUTA_PREFERENCES (plugin_object),
840 anjuta_shell_get_preferences (ANJUTA_SHELL (priv->shell), NULL),
841 NULL);
844 g_list_free (activated_plugins);
846 update_enabled (GTK_TREE_MODEL (store), priv->activated_plugins);
847 gtk_tree_path_free (path);
850 #if 0
851 static void
852 selection_changed (GtkTreeSelection *selection, GtkListStore *store)
854 GtkTreeIter iter;
856 if (gtk_tree_selection_get_selected (selection, NULL,
857 &iter)) {
858 GtkTextBuffer *buffer;
860 GtkWidget *txt = g_object_get_data (G_OBJECT (store),
861 "AboutText");
863 GtkWidget *image = g_object_get_data (G_OBJECT (store),
864 "Icon");
865 AnjutaPluginHandle *plugin = plugin_for_iter (store, &iter);
867 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (txt));
868 gtk_text_buffer_set_text (buffer, plugin->about, -1);
870 if (plugin->icon_path) {
871 gtk_image_set_from_file (GTK_IMAGE (image),
872 plugin->icon_path);
873 gtk_widget_show (GTK_WIDGET (image));
874 } else {
875 gtk_widget_hide (GTK_WIDGET (image));
879 #endif
881 static GtkWidget *
882 create_plugin_tree (void)
884 GtkListStore *store;
885 GtkWidget *tree;
886 GtkCellRenderer *renderer;
887 GtkTreeViewColumn *column;
889 store = gtk_list_store_new (N_COLS,
890 G_TYPE_BOOLEAN,
891 G_TYPE_BOOLEAN,
892 GDK_TYPE_PIXBUF,
893 G_TYPE_STRING,
894 G_TYPE_POINTER);
895 tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
897 renderer = gtk_cell_renderer_toggle_new ();
898 g_signal_connect (G_OBJECT (renderer), "toggled",
899 G_CALLBACK (plugin_toggled), store);
900 column = gtk_tree_view_column_new_with_attributes (dgettext (GETTEXT_PACKAGE, "Load"),
901 renderer,
902 "active",
903 COL_ENABLED,
904 "activatable",
905 COL_ACTIVABLE,
906 NULL);
907 gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
908 gtk_tree_view_column_set_sizing (column,
909 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
911 column = gtk_tree_view_column_new ();
912 renderer = gtk_cell_renderer_pixbuf_new ();
913 gtk_tree_view_column_pack_start (column, renderer, FALSE);
914 gtk_tree_view_column_add_attribute (column, renderer, "pixbuf",
915 COL_ICON);
916 renderer = gtk_cell_renderer_text_new ();
917 g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
918 gtk_tree_view_column_pack_start (column, renderer, TRUE);
919 gtk_tree_view_column_add_attribute (column, renderer, "markup",
920 COL_NAME);
921 gtk_tree_view_column_set_sizing (column,
922 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
923 gtk_tree_view_column_set_title (column, dgettext (GETTEXT_PACKAGE, "Available Plugins"));
924 gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
925 gtk_tree_view_set_expander_column (GTK_TREE_VIEW (tree), column);
927 g_object_unref (store);
928 return tree;
931 /* Sort function for plugins */
932 static gint
933 sort_plugins(gconstpointer a, gconstpointer b)
935 g_return_val_if_fail (a != NULL, 0);
936 g_return_val_if_fail (b != NULL, 0);
938 AnjutaPluginHandle* plugin_a = ANJUTA_PLUGIN_HANDLE (a);
939 AnjutaPluginHandle* plugin_b = ANJUTA_PLUGIN_HANDLE (b);
941 return strcmp (anjuta_plugin_handle_get_name (plugin_a),
942 anjuta_plugin_handle_get_name (plugin_b));
945 /* If show_all == FALSE, show only user activatable plugins
946 * If show_all == TRUE, show all plugins
948 static void
949 populate_plugin_model (AnjutaPluginManager *plugin_manager,
950 GtkListStore *store,
951 GHashTable *plugins_to_show,
952 GHashTable *activated_plugins,
953 gboolean show_all)
955 AnjutaPluginManagerPriv *priv;
956 GList *l;
958 priv = plugin_manager->priv;
959 gtk_list_store_clear (store);
961 priv->available_plugins = g_list_sort (priv->available_plugins, sort_plugins);
963 for (l = priv->available_plugins; l != NULL; l = l->next)
965 AnjutaPluginHandle *plugin = l->data;
967 /* If plugins to show is NULL, show all available plugins */
968 if (plugins_to_show == NULL ||
969 g_hash_table_lookup (plugins_to_show, plugin))
972 gboolean enable = FALSE;
973 if (g_hash_table_lookup (activated_plugins, plugin))
974 enable = TRUE;
976 if (anjuta_plugin_handle_get_name (plugin) &&
977 anjuta_plugin_handle_get_description (plugin) &&
978 (anjuta_plugin_handle_get_user_activatable (plugin) ||
979 show_all))
981 GtkTreeIter iter;
982 gchar *text;
984 text = g_markup_printf_escaped ("<span size=\"larger\" weight=\"bold\">%s</span>\n%s",
985 anjuta_plugin_handle_get_name (plugin),
986 anjuta_plugin_handle_get_about (plugin));
988 gtk_list_store_append (store, &iter);
989 gtk_list_store_set (store, &iter,
990 COL_ACTIVABLE,
991 anjuta_plugin_handle_get_user_activatable (plugin),
992 COL_ENABLED, enable,
993 COL_NAME, text,
994 COL_PLUGIN, plugin,
995 -1);
996 if (anjuta_plugin_handle_get_icon_path (plugin))
998 GdkPixbuf *icon;
999 icon = gdk_pixbuf_new_from_file_at_size (anjuta_plugin_handle_get_icon_path (plugin),
1000 32, 32, NULL);
1001 if (icon) {
1002 gtk_list_store_set (store, &iter,
1003 COL_ICON, icon, -1);
1004 g_object_unref (icon);
1007 g_free (text);
1013 static GtkWidget *
1014 create_remembered_plugins_tree (void)
1016 GtkListStore *store;
1017 GtkWidget *tree;
1018 GtkCellRenderer *renderer;
1019 GtkTreeViewColumn *column;
1021 store = gtk_list_store_new (N_REM_COLS, GDK_TYPE_PIXBUF, G_TYPE_STRING,
1022 G_TYPE_STRING);
1023 tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
1025 column = gtk_tree_view_column_new ();
1026 renderer = gtk_cell_renderer_pixbuf_new ();
1027 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1028 gtk_tree_view_column_add_attribute (column, renderer, "pixbuf",
1029 COL_REM_ICON);
1030 renderer = gtk_cell_renderer_text_new ();
1031 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1032 gtk_tree_view_column_add_attribute (column, renderer, "markup",
1033 COL_REM_NAME);
1034 gtk_tree_view_column_set_sizing (column,
1035 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1036 gtk_tree_view_column_set_title (column, dgettext (GETTEXT_PACKAGE, "Preferred plugins"));
1037 gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
1038 gtk_tree_view_set_expander_column (GTK_TREE_VIEW (tree), column);
1040 g_object_unref (store);
1041 return tree;
1044 static void
1045 foreach_remembered_plugin (gpointer key, gpointer value, gpointer user_data)
1047 AnjutaPluginDescription *desc = (AnjutaPluginDescription *) value;
1048 GtkListStore *store = GTK_LIST_STORE (user_data);
1049 AnjutaPluginManager *manager = g_object_get_data (G_OBJECT (store),
1050 "plugin-manager");
1051 AnjutaPluginHandle *plugin =
1052 g_hash_table_lookup (manager->priv->plugins_by_description, desc);
1053 g_return_if_fail (plugin != NULL);
1055 if (anjuta_plugin_handle_get_name (plugin) &&
1056 anjuta_plugin_handle_get_description (plugin))
1058 GtkTreeIter iter;
1059 gchar *text;
1061 text = g_markup_printf_escaped ("<span size=\"larger\" weight=\"bold\">%s</span>\n%s",
1062 anjuta_plugin_handle_get_name (plugin),
1063 anjuta_plugin_handle_get_about (plugin));
1065 gtk_list_store_append (store, &iter);
1066 gtk_list_store_set (store, &iter,
1067 COL_REM_NAME, text,
1068 COL_REM_PLUGIN_KEY, key,
1069 -1);
1070 if (anjuta_plugin_handle_get_icon_path (plugin))
1072 GdkPixbuf *icon;
1073 icon = gdk_pixbuf_new_from_file_at_size (anjuta_plugin_handle_get_icon_path (plugin),
1074 32, 32, NULL);
1075 if (icon) {
1076 gtk_list_store_set (store, &iter,
1077 COL_REM_ICON, icon, -1);
1078 g_object_unref (icon);
1081 g_free (text);
1085 static void
1086 populate_remembered_plugins_model (AnjutaPluginManager *plugin_manager,
1087 GtkListStore *store)
1089 AnjutaPluginManagerPriv *priv = plugin_manager->priv;
1090 gtk_list_store_clear (store);
1091 g_hash_table_foreach (priv->remember_plugins, foreach_remembered_plugin,
1092 store);
1095 static void
1096 on_show_all_plugins_toggled (GtkToggleButton *button, GtkListStore *store)
1098 AnjutaPluginManager *plugin_manager;
1100 plugin_manager = g_object_get_data (G_OBJECT (button), "__plugin_manager");
1102 populate_plugin_model (plugin_manager, store, NULL,
1103 plugin_manager->priv->activated_plugins,
1104 !gtk_toggle_button_get_active (button));
1107 static void
1108 on_forget_plugin_clicked (GtkWidget *button, GtkTreeView *view)
1110 GtkTreeIter iter;
1111 GtkTreeModel *model;
1112 GtkTreeSelection *selection = gtk_tree_view_get_selection (view);
1113 if (gtk_tree_selection_get_selected (selection, &model, &iter))
1115 gchar *plugin_key;
1116 AnjutaPluginManager *manager = g_object_get_data (G_OBJECT (model),
1117 "plugin-manager");
1118 gtk_tree_model_get (model, &iter, COL_REM_PLUGIN_KEY, &plugin_key, -1);
1119 g_hash_table_remove (manager->priv->remember_plugins, plugin_key);
1120 gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
1121 g_free (plugin_key);
1125 static void
1126 on_forget_plugin_sel_changed (GtkTreeSelection *selection,
1127 GtkWidget *button)
1129 GtkTreeIter iter;
1131 if (gtk_tree_selection_get_selected (selection, NULL, &iter))
1132 gtk_widget_set_sensitive (button, TRUE);
1133 else
1134 gtk_widget_set_sensitive (button, FALSE);
1137 GtkWidget *
1138 anjuta_plugin_manager_get_plugins_page (AnjutaPluginManager *plugin_manager)
1140 GtkWidget *vbox;
1141 GtkWidget *checkbutton;
1142 GtkWidget *tree;
1143 GtkWidget *scrolled;
1144 GtkWidget *toolbar;
1145 GtkToolItem *toolitem;
1146 GtkListStore *store;
1148 /* Plugins page */
1149 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1150 gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
1152 scrolled = gtk_scrolled_window_new (NULL, NULL);
1153 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
1154 GTK_SHADOW_IN);
1155 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
1156 GTK_POLICY_NEVER,
1157 GTK_POLICY_AUTOMATIC);
1158 gtk_style_context_set_junction_sides (gtk_widget_get_style_context (scrolled), GTK_JUNCTION_BOTTOM);
1159 gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 0);
1161 toolbar = gtk_toolbar_new ();
1162 gtk_style_context_add_class (gtk_widget_get_style_context (toolbar), GTK_STYLE_CLASS_INLINE_TOOLBAR);
1163 gtk_style_context_set_junction_sides (gtk_widget_get_style_context (toolbar), GTK_JUNCTION_TOP);
1164 gtk_box_pack_start (GTK_BOX (vbox), toolbar, FALSE, FALSE, 0);
1165 gtk_widget_show (toolbar);
1167 toolitem = gtk_tool_item_new ();
1168 gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (toolitem), 0);
1169 gtk_widget_show (GTK_WIDGET(toolitem));
1171 checkbutton = gtk_check_button_new_with_label (dgettext (GETTEXT_PACKAGE, "Only show user activatable plugins"));
1172 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), TRUE);
1173 gtk_container_add (GTK_CONTAINER (toolitem), checkbutton);
1175 tree = create_plugin_tree ();
1176 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (tree), TRUE);
1177 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree), FALSE);
1178 store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (tree)));
1180 populate_plugin_model (plugin_manager, store, NULL,
1181 plugin_manager->priv->activated_plugins, FALSE);
1183 gtk_container_add (GTK_CONTAINER (scrolled), tree);
1184 g_object_set_data (G_OBJECT (store), "plugin-manager", plugin_manager);
1187 g_object_set_data (G_OBJECT (checkbutton), "__plugin_manager", plugin_manager);
1188 g_signal_connect (G_OBJECT (checkbutton), "toggled",
1189 G_CALLBACK (on_show_all_plugins_toggled),
1190 store);
1191 gtk_widget_show_all (vbox);
1192 return vbox;
1195 GtkWidget *
1196 anjuta_plugin_manager_get_remembered_plugins_page (AnjutaPluginManager *plugin_manager)
1198 GtkWidget *vbox;
1199 GtkWidget *tree;
1200 GtkWidget *scrolled;
1201 GtkListStore *store;
1202 GtkWidget *hbox;
1203 GtkWidget *display_label;
1204 GtkWidget *forget_button;
1205 GtkTreeSelection *selection;
1207 /* Remembered plugin */
1208 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10);
1209 gtk_container_set_border_width (GTK_CONTAINER (vbox), 10);
1211 display_label = gtk_label_new (dgettext (GETTEXT_PACKAGE, "These are the plugins selected by you "
1212 "when Anjuta prompted to choose one of "
1213 "many suitable plugins. Removing the "
1214 "preferred plugin will let Anjuta prompt "
1215 "you again to choose different plugin."));
1216 gtk_label_set_line_wrap (GTK_LABEL (display_label), TRUE);
1217 gtk_box_pack_start (GTK_BOX (vbox), display_label, FALSE, FALSE, 0);
1219 scrolled = gtk_scrolled_window_new (NULL, NULL);
1220 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
1221 GTK_SHADOW_IN);
1222 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
1223 GTK_POLICY_AUTOMATIC,
1224 GTK_POLICY_AUTOMATIC);
1225 gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 0);
1227 tree = create_remembered_plugins_tree ();
1228 store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (tree)));
1230 gtk_container_add (GTK_CONTAINER (scrolled), tree);
1231 g_object_set_data (G_OBJECT (store), "plugin-manager", plugin_manager);
1232 populate_remembered_plugins_model (plugin_manager, store);
1234 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1235 gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
1236 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
1237 forget_button = gtk_button_new_with_label (dgettext (GETTEXT_PACKAGE, "Forget selected plugin"));
1238 gtk_widget_set_sensitive (forget_button, FALSE);
1239 gtk_box_pack_end (GTK_BOX (hbox), forget_button, FALSE, FALSE, 0);
1241 g_signal_connect (forget_button, "clicked",
1242 G_CALLBACK (on_forget_plugin_clicked),
1243 tree);
1244 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree));
1245 g_signal_connect (selection, "changed",
1246 G_CALLBACK (on_forget_plugin_sel_changed),
1247 forget_button);
1248 gtk_widget_show_all (vbox);
1249 return vbox;
1252 static GList *
1253 property_to_list (const char *value)
1255 GList *l = NULL;
1256 char **split_str;
1257 char **p;
1259 split_str = g_strsplit (value, ",", -1);
1260 for (p = split_str; *p != NULL; p++) {
1261 l = g_list_prepend (l, g_strdup (g_strstrip (*p)));
1263 g_strfreev (split_str);
1264 return l;
1267 static IAnjutaPluginFactory*
1268 get_plugin_factory (AnjutaPluginManager *plugin_manager,
1269 const gchar *language,
1270 GError **error)
1272 AnjutaPluginManagerPriv *priv;
1273 AnjutaPluginHandle *plugin;
1274 GList *loader_plugins, *node;
1275 GList *valid_plugins;
1276 GObject *obj = NULL;
1278 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), G_TYPE_INVALID);
1281 if ((language == NULL) || (g_ascii_strcasecmp (language, "C") == 0))
1283 /* Support of C plugin is built-in */
1284 return IANJUTA_PLUGIN_FACTORY (anjuta_plugin_factory);
1287 priv = plugin_manager->priv;
1288 plugin = NULL;
1290 /* Find all plugins implementing the IAnjutaPluginLoader interface. */
1291 loader_plugins = g_hash_table_lookup (priv->plugins_by_interfaces, "IAnjutaPluginLoader");
1293 /* Create a list of loader supporting this language */
1294 node = loader_plugins;
1295 valid_plugins = NULL;
1296 while (node)
1298 AnjutaPluginDescription *desc;
1299 gchar *val;
1300 GList *vals = NULL;
1301 GList *l_node;
1302 gboolean found;
1304 plugin = node->data;
1306 desc = anjuta_plugin_handle_get_description (plugin);
1307 if (anjuta_plugin_description_get_string (desc, "Plugin Loader", "SupportedLanguage", &val))
1309 if (val != NULL)
1311 vals = property_to_list (val);
1312 g_free (val);
1316 found = FALSE;
1317 l_node = vals;
1318 while (l_node)
1320 if (!found && (g_ascii_strcasecmp (l_node->data, language) == 0))
1322 found = TRUE;
1324 g_free (l_node->data);
1325 l_node = g_list_next (l_node);
1327 g_list_free (vals);
1329 if (found)
1331 valid_plugins = g_list_prepend (valid_plugins, plugin);
1334 node = g_list_next (node);
1337 /* Find the first installed plugin from the valid plugins */
1338 node = valid_plugins;
1339 while (node)
1341 plugin = node->data;
1342 obj = g_hash_table_lookup (priv->activated_plugins, plugin);
1343 if (obj) break;
1344 node = g_list_next (node);
1347 /* If no plugin is installed yet, do something */
1348 if ((obj == NULL) && valid_plugins && g_list_length (valid_plugins) == 1)
1350 /* If there is just one plugin, consider it selected */
1351 plugin = valid_plugins->data;
1353 /* Install and return it */
1354 plugin_set_update (plugin_manager, plugin, TRUE);
1355 obj = g_hash_table_lookup (priv->activated_plugins, plugin);
1357 else if ((obj == NULL) && valid_plugins)
1359 /* Prompt the user to select one of these plugins */
1361 GList *descs = NULL;
1362 node = valid_plugins;
1363 while (node)
1365 plugin = node->data;
1366 descs = g_list_prepend (descs, anjuta_plugin_handle_get_description (plugin));
1367 node = g_list_next (node);
1369 descs = g_list_reverse (descs);
1370 obj = anjuta_plugin_manager_select_and_activate (plugin_manager,
1371 dgettext (GETTEXT_PACKAGE, "Select a plugin"),
1372 dgettext (GETTEXT_PACKAGE, "Please select a plugin to activate"),
1373 descs);
1374 g_list_free (descs);
1376 g_list_free (valid_plugins);
1378 if (obj != NULL)
1380 return IANJUTA_PLUGIN_FACTORY (obj);
1383 /* No plugin implementing this interface found */
1384 g_set_error (error, ANJUTA_PLUGIN_MANAGER_ERROR,
1385 ANJUTA_PLUGIN_MANAGER_MISSING_FACTORY,
1386 dgettext (GETTEXT_PACKAGE, "No plugin is able to load other plugins in %s"), language);
1388 return NULL;
1391 static void
1392 on_is_active_plugins_foreach (gpointer key, gpointer data, gpointer user_data)
1394 AnjutaPluginHandle *handle = ANJUTA_PLUGIN_HANDLE (key);
1395 gchar const **search_iface = (gchar const **)user_data;
1397 if (*search_iface != NULL)
1399 GList *interfaces;
1400 GList *found;
1402 interfaces = anjuta_plugin_handle_get_interfaces (handle);
1404 for (found = g_list_first (interfaces); found != NULL; found = g_list_next (found))
1408 found = g_list_find_custom (interfaces, *search_iface, (GCompareFunc)strcmp);
1410 if (found != NULL) *search_iface = NULL;
1415 * anjuta_plugin_manager_is_active_plugin:
1416 * @plugin_manager: A #AnjutaPluginManager object
1417 * @iface_name: The interface implemented by the object to be found
1419 * Searches if a currently loaded plugins implements
1420 * the given interface.
1422 * Return value: True is the plugin is currently loaded.
1425 gboolean
1426 anjuta_plugin_manager_is_active_plugin (AnjutaPluginManager *plugin_manager,
1427 const gchar *iface_name)
1429 const gchar *search_iface = iface_name;
1431 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), FALSE);
1433 g_hash_table_foreach (plugin_manager->priv->activated_plugins,
1434 on_is_active_plugins_foreach,
1435 &search_iface);
1437 return search_iface == NULL;
1441 * anjuta_plugin_manager_get_plugin:
1442 * @plugin_manager: A #AnjutaPluginManager object
1443 * @iface_name: The interface implemented by the object to be found
1445 * Searches the currently available plugins to find the one which
1446 * implements the given interface as primary interface and returns it. If
1447 * the plugin is not yet loaded, it will be loaded and activated.
1448 * It only searches
1449 * from the pool of plugin objects loaded in this shell and can only search
1450 * by primary interface. If there are more objects implementing this primary
1451 * interface, user might be prompted to select one from them (and might give
1452 * the option to use it as default for future queries). A typical usage of this
1453 * function is:
1454 * <programlisting>
1455 * GObject *docman =
1456 * anjuta_plugin_manager_get_plugin (plugin_manager, "IAnjutaDocumentManager", error);
1457 * </programlisting>
1458 * Notice that this function takes the interface name string as string, unlike
1459 * anjuta_plugins_get_interface() which takes the type directly.
1460 * If no plugin implementing this interface can be found, returns NULL.
1462 * Return value: The plugin object (subclass of #AnjutaPlugin) which implements
1463 * the given interface or NULL. See #AnjutaPlugin for more detail on interfaces
1464 * implemented by plugins.
1466 GObject *
1467 anjuta_plugin_manager_get_plugin (AnjutaPluginManager *plugin_manager,
1468 const gchar *iface_name)
1470 AnjutaPluginManagerPriv *priv;
1471 AnjutaPluginHandle *plugin;
1472 GList *valid_plugins, *node;
1474 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
1475 g_return_val_if_fail (iface_name != NULL, NULL);
1477 priv = plugin_manager->priv;
1478 plugin = NULL;
1480 /* Find all plugins implementing this (primary) interface. */
1481 valid_plugins = g_hash_table_lookup (priv->plugins_by_interfaces, iface_name);
1483 /* Find the first installed plugin from the valid plugins */
1484 node = valid_plugins;
1485 while (node)
1487 GObject *obj;
1488 plugin = node->data;
1489 obj = g_hash_table_lookup (priv->activated_plugins, plugin);
1490 if (obj)
1491 return obj;
1492 node = g_list_next (node);
1495 /* If no plugin is installed yet, do something */
1496 if (valid_plugins && g_list_length (valid_plugins) == 1)
1498 /* If there is just one plugin, consider it selected */
1499 GObject *obj;
1500 plugin = valid_plugins->data;
1502 /* Install and return it */
1503 plugin_set_update (plugin_manager, plugin, TRUE);
1504 obj = g_hash_table_lookup (priv->activated_plugins, plugin);
1506 return obj;
1508 else if (valid_plugins)
1510 /* Prompt the user to select one of these plugins */
1511 GObject *obj;
1512 GList *descs = NULL;
1513 node = valid_plugins;
1514 while (node)
1516 plugin = node->data;
1517 descs = g_list_prepend (descs, anjuta_plugin_handle_get_description (plugin));
1518 node = g_list_next (node);
1520 descs = g_list_reverse (descs);
1521 obj = anjuta_plugin_manager_select_and_activate (plugin_manager,
1522 dgettext (GETTEXT_PACKAGE, "Select a plugin"),
1523 dgettext (GETTEXT_PACKAGE, "<b>Please select a plugin to activate</b>"),
1524 descs);
1525 g_list_free (descs);
1526 return obj;
1529 /* No plugin implementing this interface found */
1530 return NULL;
1533 GObject *
1534 anjuta_plugin_manager_get_plugin_by_id (AnjutaPluginManager *plugin_manager,
1535 const gchar *plugin_id)
1537 AnjutaPluginManagerPriv *priv;
1538 AnjutaPluginHandle *plugin;
1540 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
1541 g_return_val_if_fail (plugin_id != NULL, NULL);
1543 priv = plugin_manager->priv;
1544 plugin = g_hash_table_lookup (priv->plugins_by_name, plugin_id);
1545 if (plugin)
1547 GObject *obj;
1548 obj = g_hash_table_lookup (priv->activated_plugins, plugin);
1549 if (obj)
1551 return obj;
1552 } else
1554 plugin_set_update (plugin_manager, plugin, TRUE);
1555 obj = g_hash_table_lookup (priv->activated_plugins, plugin);
1556 return obj;
1559 g_warning ("No plugin found with id \"%s\".", plugin_id);
1560 return NULL;
1563 static void
1564 on_activated_plugins_foreach (gpointer key, gpointer data, gpointer user_data)
1566 AnjutaPluginHandle *plugin = ANJUTA_PLUGIN_HANDLE (key);
1567 GList **active_plugins = (GList **)user_data;
1568 *active_plugins = g_list_prepend (*active_plugins,
1569 anjuta_plugin_handle_get_description (plugin));
1572 static void
1573 on_activated_plugin_objects_foreach (gpointer key, gpointer data, gpointer user_data)
1575 GList **active_plugins = (GList **)user_data;
1576 *active_plugins = g_list_prepend (*active_plugins,
1577 data);
1580 GList*
1581 anjuta_plugin_manager_get_active_plugins (AnjutaPluginManager *plugin_manager)
1583 GList *active_plugins = NULL;
1585 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
1586 g_hash_table_foreach (plugin_manager->priv->activated_plugins,
1587 on_activated_plugins_foreach,
1588 &active_plugins);
1589 return g_list_reverse (active_plugins);
1592 GList*
1593 anjuta_plugin_manager_get_active_plugin_objects (AnjutaPluginManager *plugin_manager)
1595 GList *active_plugins = NULL;
1597 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
1598 g_hash_table_foreach (plugin_manager->priv->activated_plugins,
1599 on_activated_plugin_objects_foreach,
1600 &active_plugins);
1601 return g_list_reverse (active_plugins);
1604 gboolean
1605 anjuta_plugin_manager_unload_plugin_by_id (AnjutaPluginManager *plugin_manager,
1606 const gchar *plugin_id)
1608 AnjutaPluginManagerPriv *priv;
1609 AnjutaPluginHandle *plugin;
1611 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), FALSE);
1612 g_return_val_if_fail (plugin_id != NULL, FALSE);
1614 priv = plugin_manager->priv;
1616 plugin = g_hash_table_lookup (priv->plugins_by_name, plugin_id);
1617 if (plugin)
1619 plugin_set_update (plugin_manager, plugin, FALSE);
1621 /* Check if the plugin has been indeed unloaded */
1622 if (!g_hash_table_lookup (priv->activated_plugins, plugin))
1623 return TRUE;
1624 else
1625 return FALSE;
1627 g_warning ("No plugin found with id \"%s\".", plugin_id);
1628 return FALSE;
1631 static gboolean
1632 find_plugin_for_object (gpointer key, gpointer value, gpointer data)
1634 if (value == data)
1636 g_object_set_data (G_OBJECT (data), "__plugin_plugin", key);
1637 return TRUE;
1639 return FALSE;
1642 gboolean
1643 anjuta_plugin_manager_unload_plugin (AnjutaPluginManager *plugin_manager,
1644 GObject *plugin_object)
1646 AnjutaPluginManagerPriv *priv;
1647 AnjutaPluginHandle *plugin;
1649 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), FALSE);
1650 g_return_val_if_fail (ANJUTA_IS_PLUGIN (plugin_object), FALSE);
1652 priv = plugin_manager->priv;
1654 plugin = NULL;
1656 /* Find the plugin that correspond to this plugin object */
1657 g_hash_table_find (priv->activated_plugins, find_plugin_for_object,
1658 plugin_object);
1659 plugin = g_object_get_data (G_OBJECT (plugin_object), "__plugin_plugin");
1661 if (plugin)
1663 plugin_set_update (plugin_manager, plugin, FALSE);
1665 /* Check if the plugin has been indeed unloaded */
1666 if (!g_hash_table_lookup (priv->activated_plugins, plugin))
1667 return TRUE;
1668 else
1669 return FALSE;
1671 g_warning ("No plugin found with object \"%p\".", plugin_object);
1672 return FALSE;
1675 GList*
1676 anjuta_plugin_manager_list_query (AnjutaPluginManager *plugin_manager,
1677 GList *secs,
1678 GList *anames,
1679 GList *avalues)
1681 AnjutaPluginManagerPriv *priv;
1682 GList *selected_plugins = NULL;
1683 const gchar *sec;
1684 const gchar *aname;
1685 const gchar *avalue;
1686 GList *available;
1688 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
1690 priv = plugin_manager->priv;
1691 available = priv->available_plugins;
1693 if (secs == NULL)
1695 /* If no query is given, select all plugins */
1696 while (available)
1698 AnjutaPluginHandle *plugin = available->data;
1699 AnjutaPluginDescription *desc =
1700 anjuta_plugin_handle_get_description (plugin);
1701 selected_plugins = g_list_prepend (selected_plugins, desc);
1702 available = g_list_next (available);
1704 return g_list_reverse (selected_plugins);
1707 g_return_val_if_fail (secs != NULL, NULL);
1708 g_return_val_if_fail (anames != NULL, NULL);
1709 g_return_val_if_fail (avalues != NULL, NULL);
1711 while (available)
1713 GList* s_node = secs;
1714 GList* n_node = anames;
1715 GList* v_node = avalues;
1717 gboolean satisfied = FALSE;
1719 AnjutaPluginHandle *plugin = available->data;
1720 AnjutaPluginDescription *desc =
1721 anjuta_plugin_handle_get_description (plugin);
1723 while (s_node)
1725 gchar *val;
1726 GList *vals;
1727 GList *node;
1728 gboolean found = FALSE;
1730 satisfied = TRUE;
1732 sec = s_node->data;
1733 aname = n_node->data;
1734 avalue = v_node->data;
1736 if (!anjuta_plugin_description_get_string (desc, sec, aname, &val))
1738 satisfied = FALSE;
1739 break;
1742 vals = property_to_list (val);
1743 g_free (val);
1745 node = vals;
1746 while (node)
1748 if (strchr(node->data, '*') != NULL)
1750 // Star match.
1751 gchar **segments;
1752 gchar **seg_ptr;
1753 const gchar *cursor;
1755 segments = g_strsplit (node->data, "*", -1);
1757 seg_ptr = segments;
1758 cursor = avalue;
1759 while (*seg_ptr != NULL)
1761 if (strlen (*seg_ptr) > 0) {
1762 cursor = strstr (cursor, *seg_ptr);
1763 if (cursor == NULL)
1764 break;
1766 cursor += strlen (*seg_ptr);
1767 seg_ptr++;
1769 if (*seg_ptr == NULL)
1770 found = TRUE;
1771 g_strfreev (segments);
1773 else if (g_ascii_strcasecmp (node->data, avalue) == 0)
1775 // String match.
1776 found = TRUE;
1778 g_free (node->data);
1779 node = g_list_next (node);
1781 g_list_free (vals);
1782 if (!found)
1784 satisfied = FALSE;
1785 break;
1787 s_node = g_list_next (s_node);
1788 n_node = g_list_next (n_node);
1789 v_node = g_list_next (v_node);
1791 if (satisfied)
1793 selected_plugins = g_list_prepend (selected_plugins, desc);
1794 /* DEBUG_PRINT ("Satisfied, Adding %s",
1795 anjuta_plugin_handle_get_name (plugin));*/
1797 available = g_list_next (available);
1800 return g_list_reverse (selected_plugins);
1803 GList*
1804 anjuta_plugin_manager_query (AnjutaPluginManager *plugin_manager,
1805 const gchar *section_name,
1806 const gchar *attribute_name,
1807 const gchar *attribute_value,
1808 ...)
1810 va_list var_args;
1811 GList *secs = NULL;
1812 GList *anames = NULL;
1813 GList *avalues = NULL;
1814 const gchar *sec;
1815 const gchar *aname;
1816 const gchar *avalue;
1817 GList *selected_plugins;
1820 if (section_name == NULL)
1822 /* If no query is given, select all plugins */
1823 return anjuta_plugin_manager_list_query (plugin_manager, NULL, NULL, NULL);
1826 g_return_val_if_fail (section_name != NULL, NULL);
1827 g_return_val_if_fail (attribute_name != NULL, NULL);
1828 g_return_val_if_fail (attribute_value != NULL, NULL);
1830 secs = g_list_prepend (secs, g_strdup (section_name));
1831 anames = g_list_prepend (anames, g_strdup (attribute_name));
1832 avalues = g_list_prepend (avalues, g_strdup (attribute_value));
1834 va_start (var_args, attribute_value);
1837 sec = va_arg (var_args, const gchar *);
1838 if (sec)
1840 aname = va_arg (var_args, const gchar *);
1841 if (aname)
1843 avalue = va_arg (var_args, const gchar *);
1844 if (avalue)
1846 secs = g_list_prepend (secs, g_strdup (sec));
1847 anames = g_list_prepend (anames, g_strdup (aname));
1848 avalues = g_list_prepend (avalues, g_strdup (avalue));
1853 while (sec);
1854 va_end (var_args);
1856 secs = g_list_reverse (secs);
1857 anames = g_list_reverse (anames);
1858 avalues = g_list_reverse (avalues);
1860 selected_plugins = anjuta_plugin_manager_list_query (plugin_manager,
1861 secs,
1862 anames,
1863 avalues);
1865 anjuta_util_glist_strings_free (secs);
1866 anjuta_util_glist_strings_free (anames);
1867 anjuta_util_glist_strings_free (avalues);
1869 return selected_plugins;
1872 enum {
1873 PIXBUF_COLUMN,
1874 PLUGIN_COLUMN,
1875 PLUGIN_DESCRIPTION_COLUMN,
1876 N_COLUMNS
1879 static void
1880 on_plugin_list_row_activated (GtkTreeView *tree_view,
1881 GtkTreePath *path,
1882 GtkTreeViewColumn *column,
1883 GtkDialog *dialog)
1885 gtk_dialog_response (dialog, GTK_RESPONSE_OK);
1889 static void
1890 on_plugin_list_show (GtkTreeView *view,
1891 GtkDirectionType direction,
1892 GtkDialog *dialog)
1894 GtkTreeSelection *selection;
1895 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
1897 g_signal_emit_by_name (G_OBJECT (selection), "changed", GTK_DIALOG(dialog), NULL);
1901 static void
1902 on_plugin_list_selection_changed (GtkTreeSelection *tree_selection,
1903 GtkDialog *dialog)
1905 GtkContainer *action_area;
1906 GList *list;
1907 GtkButton *bt = NULL;
1909 action_area = GTK_CONTAINER (gtk_dialog_get_action_area (dialog));
1910 list = gtk_container_get_children (action_area);
1911 for (; list; list = list->next) {
1912 bt = list->data;
1913 if (!strcmp("gtk-ok", gtk_button_get_label (bt)))
1914 break;
1916 if (bt && gtk_tree_selection_get_selected (tree_selection, NULL, NULL))
1917 gtk_widget_set_sensitive ((GtkWidget *) bt, TRUE);
1918 else
1919 gtk_widget_set_sensitive ((GtkWidget *) bt, FALSE);
1920 g_list_free(list);
1924 * anjuta_plugin_manager_select:
1925 * @plugin_manager: #AnjutaPluginManager object
1926 * @title: Title of the dialog
1927 * @description: label shown on the dialog
1928 * @plugin_descriptions: List of #AnjutaPluginDescription
1930 * Show a dialog where the user can choose between the given plugins
1932 * Returns: The chosen plugin description
1934 AnjutaPluginDescription *
1935 anjuta_plugin_manager_select (AnjutaPluginManager *plugin_manager,
1936 gchar *title, gchar *description,
1937 GList *plugin_descriptions)
1939 AnjutaPluginDescription *desc;
1940 AnjutaPluginManagerPriv *priv;
1941 GtkWidget *dlg;
1942 GtkTreeModel *model;
1943 GtkWidget *view;
1944 GtkTreeViewColumn *column;
1945 GtkCellRenderer *renderer;
1946 GList *node;
1947 GtkWidget *label;
1948 GtkWidget *content_area;
1949 GtkWidget *sc;
1950 GtkWidget *remember_checkbox;
1951 gint response;
1952 GtkTreeIter selected;
1953 GtkTreeSelection *selection;
1954 GtkTreeModel *store;
1955 GList *selection_ids = NULL;
1956 GString *remember_key = g_string_new ("");
1958 g_return_val_if_fail (title != NULL, NULL);
1959 g_return_val_if_fail (description != NULL, NULL);
1960 g_return_val_if_fail (plugin_descriptions != NULL, NULL);
1962 priv = plugin_manager->priv;
1964 if (g_list_length (plugin_descriptions) <= 0)
1965 return NULL;
1967 dlg = gtk_dialog_new_with_buttons (title, GTK_WINDOW (priv->shell),
1968 GTK_DIALOG_DESTROY_WITH_PARENT,
1969 GTK_STOCK_CANCEL,
1970 GTK_RESPONSE_CANCEL,
1971 GTK_STOCK_OK, GTK_RESPONSE_OK,
1972 NULL);
1973 gtk_window_set_default_size (GTK_WINDOW (dlg), 400, 300);
1975 label = gtk_label_new (description);
1976 gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
1977 gtk_widget_show (label);
1978 content_area = gtk_dialog_get_content_area (GTK_DIALOG (dlg));
1979 gtk_box_pack_start (GTK_BOX (content_area), label,
1980 FALSE, FALSE, 5);
1982 sc = gtk_scrolled_window_new (NULL, NULL);
1983 gtk_widget_show (sc);
1984 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sc),
1985 GTK_SHADOW_IN);
1986 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sc),
1987 GTK_POLICY_AUTOMATIC,
1988 GTK_POLICY_AUTOMATIC);
1990 gtk_box_pack_start (GTK_BOX (content_area), sc,
1991 TRUE, TRUE, 5);
1993 model = GTK_TREE_MODEL (gtk_list_store_new (N_COLUMNS, GDK_TYPE_PIXBUF,
1994 G_TYPE_STRING, G_TYPE_POINTER));
1995 view = gtk_tree_view_new_with_model (model);
1996 gtk_widget_show (view);
1997 gtk_container_add (GTK_CONTAINER (sc), view);
1999 column = gtk_tree_view_column_new ();
2000 gtk_tree_view_column_set_sizing (column,
2001 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
2002 gtk_tree_view_column_set_title (column, dgettext (GETTEXT_PACKAGE, "Available Plugins"));
2004 renderer = gtk_cell_renderer_pixbuf_new ();
2005 gtk_tree_view_column_pack_start (column, renderer, FALSE);
2006 gtk_tree_view_column_add_attribute (column, renderer, "pixbuf",
2007 PIXBUF_COLUMN);
2009 renderer = gtk_cell_renderer_text_new ();
2010 gtk_tree_view_column_pack_start (column, renderer, TRUE);
2011 gtk_tree_view_column_add_attribute (column, renderer, "markup",
2012 PLUGIN_COLUMN);
2014 gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
2015 gtk_tree_view_set_expander_column (GTK_TREE_VIEW (view), column);
2017 g_signal_connect (view, "row-activated",
2018 G_CALLBACK (on_plugin_list_row_activated),
2019 GTK_DIALOG(dlg));
2020 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
2021 g_signal_connect(selection, "changed",
2022 G_CALLBACK(on_plugin_list_selection_changed),
2023 GTK_DIALOG(dlg));
2024 g_signal_connect(view, "focus",
2025 G_CALLBACK(on_plugin_list_show),
2026 GTK_DIALOG(dlg));
2028 remember_checkbox =
2029 gtk_check_button_new_with_label (dgettext (GETTEXT_PACKAGE, "Remember this selection"));
2030 gtk_container_set_border_width (GTK_CONTAINER (remember_checkbox), 10);
2031 gtk_widget_show (remember_checkbox);
2032 gtk_box_pack_start (GTK_BOX (content_area), remember_checkbox,
2033 FALSE, FALSE, 0);
2035 node = plugin_descriptions;
2036 while (node)
2038 GdkPixbuf *icon_pixbuf = NULL;
2039 gchar *plugin_name = NULL;
2040 gchar *plugin_desc = NULL;
2041 gchar *icon_filename = NULL;
2042 gchar *location = NULL;
2044 desc = (AnjutaPluginDescription*)node->data;
2046 if (anjuta_plugin_description_get_string (desc,
2047 "Anjuta Plugin",
2048 "Icon",
2049 &icon_filename))
2051 gchar *icon_path = NULL;
2052 icon_path = g_strconcat (PACKAGE_PIXMAPS_DIR"/",
2053 icon_filename, NULL);
2054 g_free (icon_filename);
2055 /* DEBUG_PRINT ("Icon: %s", icon_path); */
2056 icon_pixbuf =
2057 gdk_pixbuf_new_from_file (icon_path, NULL);
2058 if (icon_pixbuf == NULL)
2060 g_warning ("Plugin pixmap not found: %s", plugin_name);
2062 g_free (icon_path);
2064 else
2066 g_warning ("Plugin does not define Icon attribute");
2068 if (!anjuta_plugin_description_get_locale_string (desc,
2069 "Anjuta Plugin",
2070 "Name",
2071 &plugin_name))
2073 g_warning ("Plugin does not define Name attribute");
2075 if (!anjuta_plugin_description_get_locale_string (desc,
2076 "Anjuta Plugin",
2077 "Description",
2078 &plugin_desc))
2080 g_warning ("Plugin does not define Description attribute");
2082 if (plugin_name && plugin_desc)
2084 GtkTreeIter iter;
2085 gchar *text;
2087 if (!anjuta_plugin_description_get_string (desc,
2088 "Anjuta Plugin",
2089 "Location",
2090 &location))
2092 g_warning ("Plugin does not define Location attribute");
2096 text = g_markup_printf_escaped ("<span size=\"larger\" weight=\"bold\">%s</span>\n%s", plugin_name, plugin_desc);
2098 gtk_list_store_append (GTK_LIST_STORE (model), &iter);
2099 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
2100 PLUGIN_COLUMN, text,
2101 PLUGIN_DESCRIPTION_COLUMN, desc, -1);
2102 if (icon_pixbuf) {
2103 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
2104 PIXBUF_COLUMN, icon_pixbuf, -1);
2105 g_object_unref (icon_pixbuf);
2107 g_free (text);
2109 selection_ids = g_list_prepend (selection_ids, location);
2111 g_free (plugin_name);
2112 g_free (plugin_desc);
2113 node = g_list_next (node);
2116 /* Prepare remembering key */
2117 selection_ids = g_list_sort (selection_ids,
2118 (GCompareFunc)strcmp);
2119 node = selection_ids;
2120 while (node)
2122 g_string_append (remember_key, (gchar*)node->data);
2123 g_string_append (remember_key, ",");
2124 node = g_list_next (node);
2126 g_list_foreach (selection_ids, (GFunc) g_free, NULL);
2127 g_list_free (selection_ids);
2129 /* Find if the selection is remembered */
2130 desc = g_hash_table_lookup (priv->remember_plugins, remember_key->str);
2131 if (desc)
2133 g_string_free (remember_key, TRUE);
2134 gtk_widget_destroy (dlg);
2135 return desc;
2138 /* Prompt dialog */
2139 response = gtk_dialog_run (GTK_DIALOG (dlg));
2140 switch (response)
2142 case GTK_RESPONSE_OK:
2143 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
2144 if (gtk_tree_selection_get_selected (selection, &store,
2145 &selected))
2147 gtk_tree_model_get (model, &selected,
2148 PLUGIN_DESCRIPTION_COLUMN, &desc, -1);
2149 if (desc)
2151 /* Remember selection */
2152 if (gtk_toggle_button_get_active
2153 (GTK_TOGGLE_BUTTON (remember_checkbox)))
2155 /* DEBUG_PRINT ("Remembering selection '%s'",
2156 remember_key->str);*/
2157 g_hash_table_insert (priv->remember_plugins,
2158 g_strdup (remember_key->str), desc);
2160 g_string_free (remember_key, TRUE);
2161 gtk_widget_destroy (dlg);
2162 return desc;
2165 break;
2167 g_string_free (remember_key, TRUE);
2168 gtk_widget_destroy (dlg);
2169 return NULL;
2172 GObject*
2173 anjuta_plugin_manager_select_and_activate (AnjutaPluginManager *plugin_manager,
2174 gchar *title,
2175 gchar *description,
2176 GList *plugin_descriptions)
2178 AnjutaPluginDescription *d;
2180 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
2182 d = anjuta_plugin_manager_select (plugin_manager, title, description,
2183 plugin_descriptions);
2184 if (d)
2186 GObject *plugin = NULL;
2187 gchar *location = NULL;
2189 anjuta_plugin_description_get_string (d,
2190 "Anjuta Plugin",
2191 "Location",
2192 &location);
2193 g_return_val_if_fail (location != NULL, NULL);
2194 plugin =
2195 anjuta_plugin_manager_get_plugin_by_id (plugin_manager, location);
2196 g_free (location);
2197 return plugin;
2199 return NULL;
2202 /* Plugin manager */
2204 static void
2205 anjuta_plugin_manager_init (AnjutaPluginManager *object)
2207 object->priv = g_new0 (AnjutaPluginManagerPriv, 1);
2208 object->priv->plugins_by_name = g_hash_table_new (g_str_hash, g_str_equal);
2209 object->priv->plugins_by_interfaces = g_hash_table_new_full (g_str_hash,
2210 g_str_equal,
2211 NULL,
2212 (GDestroyNotify) g_list_free);
2213 object->priv->plugins_by_description = g_hash_table_new (g_direct_hash,
2214 g_direct_equal);
2215 object->priv->activated_plugins = g_hash_table_new (g_direct_hash,
2216 g_direct_equal);
2217 object->priv->plugins_cache = g_hash_table_new (g_direct_hash,
2218 g_direct_equal);
2219 object->priv->remember_plugins = g_hash_table_new_full (g_str_hash,
2220 g_str_equal,
2221 g_free, NULL);
2224 static void
2225 anjuta_plugin_manager_finalize (GObject *object)
2227 AnjutaPluginManagerPriv *priv;
2228 priv = ANJUTA_PLUGIN_MANAGER (object)->priv;
2229 if (priv->available_plugins)
2231 /* anjuta_plugin_manager_unload_all_plugins (ANJUTA_PLUGIN_MANAGER (object)); */
2232 g_list_foreach (priv->available_plugins, (GFunc)g_object_unref, NULL);
2233 g_list_free (priv->available_plugins);
2234 priv->available_plugins = NULL;
2236 if (priv->activated_plugins)
2238 g_hash_table_destroy (priv->activated_plugins);
2239 priv->activated_plugins = NULL;
2241 if (priv->plugins_cache)
2243 g_hash_table_destroy (priv->plugins_cache);
2244 priv->plugins_cache = NULL;
2246 if (priv->plugins_by_name)
2248 g_hash_table_destroy (priv->plugins_by_name);
2249 priv->plugins_by_name = NULL;
2251 if (priv->plugins_by_description)
2253 g_hash_table_destroy (priv->plugins_by_description);
2254 priv->plugins_by_description = NULL;
2256 if (priv->plugins_by_interfaces)
2258 g_hash_table_destroy (priv->plugins_by_interfaces);
2259 priv->plugins_by_interfaces = NULL;
2261 if (priv->plugin_dirs)
2263 g_list_foreach (priv->plugin_dirs, (GFunc)g_free, NULL);
2264 g_list_free (priv->plugin_dirs);
2265 priv->plugin_dirs = NULL;
2267 #if 0
2268 if (anjuta_c_plugin_factory)
2270 g_object_unref (anjuta_c_plugin_factory);
2271 anjuta_c_plugin_factory = NULL;
2273 #endif
2274 G_OBJECT_CLASS (parent_class)->finalize (object);
2277 static void
2278 anjuta_plugin_manager_set_property (GObject *object, guint prop_id,
2279 const GValue *value, GParamSpec *pspec)
2281 AnjutaPluginManagerPriv *priv;
2283 g_return_if_fail (ANJUTA_IS_PLUGIN_MANAGER (object));
2284 priv = ANJUTA_PLUGIN_MANAGER (object)->priv;
2286 switch (prop_id)
2288 case PROP_STATUS:
2289 priv->status = g_value_get_object (value);
2290 break;
2291 case PROP_SHELL:
2292 priv->shell = g_value_get_object (value);
2293 break;
2294 default:
2295 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2296 break;
2300 static void
2301 anjuta_plugin_manager_get_property (GObject *object, guint prop_id,
2302 GValue *value, GParamSpec *pspec)
2304 AnjutaPluginManagerPriv *priv;
2306 g_return_if_fail (ANJUTA_IS_PLUGIN_MANAGER (object));
2307 priv = ANJUTA_PLUGIN_MANAGER (object)->priv;
2309 switch (prop_id)
2311 case PROP_SHELL:
2312 g_value_set_object (value, priv->shell);
2313 break;
2314 case PROP_STATUS:
2315 g_value_set_object (value, priv->status);
2316 break;
2317 case PROP_AVAILABLE_PLUGINS:
2318 g_value_set_pointer (value, priv->available_plugins);
2319 break;
2320 case PROP_ACTIVATED_PLUGINS:
2321 g_value_set_pointer (value, priv->activated_plugins);
2322 break;
2323 default:
2324 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2325 break;
2328 static void
2329 anjuta_plugin_manager_plugin_activated (AnjutaPluginManager *self,
2330 AnjutaPluginDescription* plugin_desc,
2331 GObject *plugin)
2333 /* TODO: Add default signal handler implementation here */
2336 static void
2337 anjuta_plugin_manager_plugin_deactivated (AnjutaPluginManager *self,
2338 AnjutaPluginDescription* plugin_desc,
2339 GObject *plugin)
2341 /* TODO: Add default signal handler implementation here */
2344 static void
2345 anjuta_plugin_manager_class_init (AnjutaPluginManagerClass *klass)
2347 GObjectClass* object_class = G_OBJECT_CLASS (klass);
2348 parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
2350 object_class->finalize = anjuta_plugin_manager_finalize;
2351 object_class->set_property = anjuta_plugin_manager_set_property;
2352 object_class->get_property = anjuta_plugin_manager_get_property;
2354 klass->plugin_activated = anjuta_plugin_manager_plugin_activated;
2355 klass->plugin_deactivated = anjuta_plugin_manager_plugin_deactivated;
2357 g_object_class_install_property (object_class,
2358 PROP_PROFILES,
2359 g_param_spec_pointer ("profiles",
2360 dgettext (GETTEXT_PACKAGE, "Profiles"),
2361 dgettext (GETTEXT_PACKAGE, "Current stack of profiles"),
2362 G_PARAM_READABLE));
2363 g_object_class_install_property (object_class,
2364 PROP_AVAILABLE_PLUGINS,
2365 g_param_spec_pointer ("available-plugins",
2366 dgettext (GETTEXT_PACKAGE, "Available plugins"),
2367 dgettext (GETTEXT_PACKAGE, "Currently available plugins found in plugin paths"),
2368 G_PARAM_READABLE));
2370 g_object_class_install_property (object_class,
2371 PROP_ACTIVATED_PLUGINS,
2372 g_param_spec_pointer ("activated-plugins",
2373 dgettext (GETTEXT_PACKAGE, "Activated plugins"),
2374 dgettext (GETTEXT_PACKAGE, "Currently activated plugins"),
2375 G_PARAM_READABLE));
2376 g_object_class_install_property (object_class,
2377 PROP_SHELL,
2378 g_param_spec_object ("shell",
2379 dgettext (GETTEXT_PACKAGE, "Anjuta Shell"),
2380 dgettext (GETTEXT_PACKAGE, "Anjuta shell for which the plugins are made"),
2381 G_TYPE_OBJECT,
2382 G_PARAM_READABLE |
2383 G_PARAM_WRITABLE |
2384 G_PARAM_CONSTRUCT));
2385 g_object_class_install_property (object_class,
2386 PROP_STATUS,
2387 g_param_spec_object ("status",
2388 dgettext (GETTEXT_PACKAGE, "Anjuta Status"),
2389 dgettext (GETTEXT_PACKAGE, "Anjuta status to use in loading and unloading of plugins"),
2390 ANJUTA_TYPE_STATUS,
2391 G_PARAM_READABLE |
2392 G_PARAM_WRITABLE |
2393 G_PARAM_CONSTRUCT));
2395 plugin_manager_signals[PLUGIN_ACTIVATED] =
2396 g_signal_new ("plugin-activated",
2397 G_OBJECT_CLASS_TYPE (klass),
2398 G_SIGNAL_RUN_FIRST,
2399 G_STRUCT_OFFSET (AnjutaPluginManagerClass,
2400 plugin_activated),
2401 NULL, NULL,
2402 anjuta_cclosure_marshal_VOID__POINTER_OBJECT,
2403 G_TYPE_NONE, 2,
2404 G_TYPE_POINTER, ANJUTA_TYPE_PLUGIN);
2406 plugin_manager_signals[PLUGIN_DEACTIVATED] =
2407 g_signal_new ("plugin-deactivated",
2408 G_OBJECT_CLASS_TYPE (klass),
2409 G_SIGNAL_RUN_FIRST,
2410 G_STRUCT_OFFSET (AnjutaPluginManagerClass,
2411 plugin_deactivated),
2412 NULL, NULL,
2413 anjuta_cclosure_marshal_VOID__POINTER_OBJECT,
2414 G_TYPE_NONE, 2,
2415 G_TYPE_POINTER, ANJUTA_TYPE_PLUGIN);
2418 GType
2419 anjuta_plugin_manager_get_type (void)
2421 static GType our_type = 0;
2423 if(our_type == 0)
2425 static const GTypeInfo our_info =
2427 sizeof (AnjutaPluginManagerClass), /* class_size */
2428 (GBaseInitFunc) NULL, /* base_init */
2429 (GBaseFinalizeFunc) NULL, /* base_finalize */
2430 (GClassInitFunc) anjuta_plugin_manager_class_init, /* class_init */
2431 (GClassFinalizeFunc) NULL, /* class_finalize */
2432 NULL /* class_data */,
2433 sizeof (AnjutaPluginManager), /* instance_size */
2434 0, /* n_preallocs */
2435 (GInstanceInitFunc) anjuta_plugin_manager_init, /* instance_init */
2436 NULL /* value_table */
2438 our_type = g_type_register_static (G_TYPE_OBJECT,
2439 "AnjutaPluginManager",
2440 &our_info, 0);
2443 return our_type;
2446 AnjutaPluginManager*
2447 anjuta_plugin_manager_new (GObject *shell, AnjutaStatus *status,
2448 GList* plugins_directories)
2450 GObject *manager_object;
2451 AnjutaPluginManager *plugin_manager;
2452 GList *cycles = NULL;
2453 const char *gnome2_path;
2454 char **pathv;
2455 char **p;
2456 GList *node;
2457 GList *plugin_dirs = NULL;
2459 /* Initialize the anjuta plugin system */
2460 manager_object = g_object_new (ANJUTA_TYPE_PLUGIN_MANAGER,
2461 "shell", shell, "status", status, NULL);
2462 plugin_manager = ANJUTA_PLUGIN_MANAGER (manager_object);
2464 if (anjuta_plugin_factory == NULL)
2466 anjuta_plugin_factory = anjuta_c_plugin_factory_new ();
2469 gnome2_path = g_getenv ("GNOME2_PATH");
2470 if (gnome2_path) {
2471 pathv = g_strsplit (gnome2_path, ":", 1);
2473 for (p = pathv; *p != NULL; p++) {
2474 char *path = g_strdup (*p);
2475 plugin_dirs = g_list_prepend (plugin_dirs, path);
2477 g_strfreev (pathv);
2480 node = plugins_directories;
2481 while (node) {
2482 if (!node->data)
2483 continue;
2484 char *path = g_strdup (node->data);
2485 plugin_dirs = g_list_prepend (plugin_dirs, path);
2486 node = g_list_next (node);
2488 plugin_dirs = g_list_reverse (plugin_dirs);
2489 /* load_plugins (); */
2491 node = plugin_dirs;
2492 while (node)
2494 load_plugins_from_directory (plugin_manager, (char*)node->data);
2495 node = g_list_next (node);
2497 resolve_dependencies (plugin_manager, &cycles);
2498 g_list_foreach(plugin_dirs, (GFunc) g_free, NULL);
2499 g_list_free(plugin_dirs);
2500 return plugin_manager;
2503 void
2504 anjuta_plugin_manager_activate_plugins (AnjutaPluginManager *plugin_manager,
2505 GList *plugins_to_activate)
2507 AnjutaPluginManagerPriv *priv;
2508 GdkPixbuf *icon_pixbuf;
2509 GList *node;
2511 priv = plugin_manager->priv;
2513 /* Freeze shell operations */
2514 anjuta_shell_freeze (ANJUTA_SHELL (priv->shell), NULL);
2515 if (plugins_to_activate)
2517 anjuta_status_progress_add_ticks (ANJUTA_STATUS (priv->status),
2518 g_list_length (plugins_to_activate));
2520 node = plugins_to_activate;
2521 while (node)
2523 AnjutaPluginDescription *d;
2524 gchar *plugin_id;
2525 gchar *icon_filename, *label;
2526 gchar *icon_path = NULL;
2528 d = node->data;
2530 icon_pixbuf = NULL;
2531 label = NULL;
2532 if (anjuta_plugin_description_get_string (d, "Anjuta Plugin",
2533 "Icon",
2534 &icon_filename))
2536 gchar *title /*, *description */;
2537 anjuta_plugin_description_get_locale_string (d, "Anjuta Plugin",
2538 "Name",
2539 &title);
2541 anjuta_plugin_description_get_locale_string (d, "Anjuta Plugin",
2542 "Description",
2543 &description);
2545 icon_path = g_strconcat (PACKAGE_PIXMAPS_DIR"/",
2546 icon_filename, NULL);
2547 /* DEBUG_PRINT ("Icon: %s", icon_path); */
2548 /* Avoid space in translated string */
2549 label = g_strconcat (dgettext (GETTEXT_PACKAGE, "Loading:"), " ", title, "...", NULL);
2550 icon_pixbuf = gdk_pixbuf_new_from_file (icon_path, NULL);
2551 if (!icon_pixbuf)
2552 g_warning ("Plugin does not define Icon: No such file %s",
2553 icon_path);
2554 g_free (icon_path);
2555 g_free (icon_filename);
2556 g_free (title);
2559 anjuta_status_progress_tick (ANJUTA_STATUS (priv->status),
2560 icon_pixbuf, label);
2561 g_free (label);
2562 if (icon_pixbuf)
2563 g_object_unref (icon_pixbuf);
2565 if (anjuta_plugin_description_get_string (d, "Anjuta Plugin",
2566 "Location", &plugin_id))
2568 /* Activate the plugin */
2569 anjuta_plugin_manager_get_plugin_by_id (plugin_manager,
2570 plugin_id);
2571 g_free (plugin_id);
2574 node = g_list_next (node);
2577 /* Thaw shell operations */
2578 anjuta_shell_thaw (ANJUTA_SHELL (priv->shell), NULL);
2581 static void
2582 on_collect (gpointer key, gpointer value, gpointer user_data)
2584 gchar *id;
2585 gchar *query = (gchar*) key;
2586 AnjutaPluginDescription *desc = (AnjutaPluginDescription *) value;
2587 GString *write_buffer = (GString *) user_data;
2589 anjuta_plugin_description_get_string (desc, "Anjuta Plugin", "Location",
2590 &id);
2591 g_string_append_printf (write_buffer, "%s=%s;", query, id);
2592 g_free (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: a newly-allocated string that must be freed with g_free().
2608 gchar*
2609 anjuta_plugin_manager_get_remembered_plugins (AnjutaPluginManager *plugin_manager)
2611 AnjutaPluginManagerPriv *priv;
2612 GString *write_buffer = g_string_new ("");
2614 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), FALSE);
2616 priv = plugin_manager->priv;
2617 g_hash_table_foreach (priv->remember_plugins, on_collect,
2618 write_buffer);
2619 return g_string_free (write_buffer, FALSE);
2622 static gboolean
2623 on_foreach_remove_true (gpointer k, gpointer v, gpointer d)
2625 return TRUE;
2629 * anjuta_plugin_manager_set_remembered_plugins:
2630 * @plugin_manager: A #AnjutaPluginManager object
2631 * @remembered_plugins: A list of prefered plugins
2633 * Set the list of plugins loaded when there is a choice between several
2634 * ones without asking the user.
2635 * The list is a string composed of elements separated by ';'. Each element
2636 * is defined with "key=value", where key is the list of possible plugins and
2637 * the value is the choosen plugin.
2639 * By the example the following element
2640 * <programlisting>
2641 * anjuta-symbol-browser:SymbolBrowserPlugin,anjuta-symbol-db:SymbolDBPlugin,=anjuta-symbol-db:SymbolDBPlugin;
2642 * </programlisting>
2643 * means if Anjuta has to choose between SymbolBrowserPlugin and
2644 * SymbolDBPlugin, it will choose SymbolDBPlugin.
2646 void
2647 anjuta_plugin_manager_set_remembered_plugins (AnjutaPluginManager *plugin_manager,
2648 const gchar *remembered_plugins)
2650 AnjutaPluginManagerPriv *priv;
2651 gchar **strv_lines, **line_idx;
2653 g_return_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager));
2654 g_return_if_fail (remembered_plugins != NULL);
2656 priv = plugin_manager->priv;
2658 g_hash_table_foreach_remove (priv->remember_plugins,
2659 on_foreach_remove_true, NULL);
2661 strv_lines = g_strsplit (remembered_plugins, ";", -1);
2662 line_idx = strv_lines;
2663 while (*line_idx)
2665 gchar **strv_keyvals;
2666 strv_keyvals = g_strsplit (*line_idx, "=", -1);
2667 if (strv_keyvals && strv_keyvals[0] && strv_keyvals[1])
2669 AnjutaPluginHandle *plugin;
2670 plugin = g_hash_table_lookup (priv->plugins_by_name,
2671 strv_keyvals[1]);
2672 if (plugin)
2674 AnjutaPluginDescription *desc;
2675 desc = anjuta_plugin_handle_get_description (plugin);
2677 DEBUG_PRINT ("Restoring remember plugin: %s=%s",
2678 strv_keyvals[0],
2679 strv_keyvals[1]);
2681 g_hash_table_insert (priv->remember_plugins,
2682 g_strdup (strv_keyvals[0]), desc);
2684 g_strfreev (strv_keyvals);
2686 line_idx++;
2688 g_strfreev (strv_lines);