search: Move close button to the right edge (see #665945)
[anjuta.git] / libanjuta / anjuta-plugin-manager.c
blob19ecf27fa5e6c2e1ced1fb1b672809f78e1042be
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 *plugin_id;
538 const gchar *language;
539 gboolean resident;
541 priv = plugin_manager->priv;
543 plugin_id = anjuta_plugin_handle_get_id (handle);
545 resident = anjuta_plugin_handle_get_resident (handle);
546 language = anjuta_plugin_handle_get_language (handle);
548 factory = get_plugin_factory (plugin_manager, language, error);
549 if (factory == NULL) return NULL;
551 plugin = ianjuta_plugin_factory_new_plugin (factory, handle, ANJUTA_SHELL (priv->shell), error);
553 if (plugin == NULL)
555 return NULL;
557 g_signal_connect (plugin, "activated",
558 G_CALLBACK (on_plugin_activated), handle);
559 g_signal_connect (plugin, "deactivated",
560 G_CALLBACK (on_plugin_deactivated), handle);
562 return plugin;
565 static gboolean
566 g_hashtable_foreach_true (gpointer key, gpointer value, gpointer user_data)
568 return TRUE;
571 void
572 anjuta_plugin_manager_unload_all_plugins (AnjutaPluginManager *plugin_manager)
574 AnjutaPluginManagerPriv *priv;
576 priv = plugin_manager->priv;
577 if (g_hash_table_size (priv->activated_plugins) > 0 ||
578 g_hash_table_size (priv->plugins_cache) > 0)
580 priv->available_plugins = g_list_reverse (priv->available_plugins);
581 if (g_hash_table_size (priv->activated_plugins) > 0)
583 GList *node;
584 node = priv->available_plugins;
585 while (node)
587 AnjutaPluginHandle *selected_plugin = node->data;
588 if (g_hash_table_lookup (priv->activated_plugins, selected_plugin))
590 plugin_set_update (plugin_manager, selected_plugin, FALSE);
591 /* DEBUG_PRINT ("Unloading plugin: %s",
592 anjuta_plugin_handle_get_id (selected_plugin));
595 node = g_list_next (node);
597 g_hash_table_foreach_remove (priv->activated_plugins,
598 g_hashtable_foreach_true, NULL);
600 if (g_hash_table_size (priv->plugins_cache) > 0)
602 GList *node;
603 node = priv->available_plugins;
604 while (node)
606 GObject *plugin_obj;
607 AnjutaPluginHandle *selected_plugin = node->data;
609 plugin_obj = g_hash_table_lookup (priv->plugins_cache,
610 selected_plugin);
611 if (plugin_obj)
613 /* DEBUG_PRINT ("Destroying plugin: %s",
614 anjuta_plugin_handle_get_id (selected_plugin));
616 g_object_unref (plugin_obj);
618 node = g_list_next (node);
620 g_hash_table_foreach_remove (priv->plugins_cache,
621 g_hashtable_foreach_true, NULL);
623 priv->available_plugins = g_list_reverse (priv->available_plugins);
627 static gboolean
628 should_unload (GHashTable *activated_plugins, AnjutaPluginHandle *plugin_to_unload,
629 AnjutaPluginHandle *plugin)
631 GObject *plugin_obj = g_hash_table_lookup (activated_plugins, plugin);
633 if (!plugin_obj)
634 return FALSE;
636 if (plugin_to_unload == plugin)
637 return TRUE;
639 gboolean dependent =
640 GPOINTER_TO_INT (g_hash_table_lookup (anjuta_plugin_handle_get_dependents (plugin),
641 plugin));
642 return dependent;
645 static gboolean
646 should_load (GHashTable *activated_plugins, AnjutaPluginHandle *plugin_to_load,
647 AnjutaPluginHandle *plugin)
649 GObject *plugin_obj = g_hash_table_lookup (activated_plugins, plugin);
651 if (plugin_obj)
652 return FALSE;
654 if (plugin_to_load == plugin)
655 return anjuta_plugin_handle_get_can_load (plugin);
657 gboolean dependency =
658 GPOINTER_TO_INT (g_hash_table_lookup (anjuta_plugin_handle_get_dependencies (plugin_to_load),
659 plugin));
660 return (dependency && anjuta_plugin_handle_get_can_load (plugin));
663 static AnjutaPluginHandle *
664 plugin_for_iter (GtkListStore *store, GtkTreeIter *iter)
666 AnjutaPluginHandle *plugin;
668 gtk_tree_model_get (GTK_TREE_MODEL (store), iter, COL_PLUGIN, &plugin, -1);
669 return plugin;
672 static void
673 update_enabled (GtkTreeModel *model, GHashTable *activated_plugins)
675 GtkTreeIter iter;
677 if (gtk_tree_model_get_iter_first (model, &iter)) {
678 do {
679 AnjutaPluginHandle *plugin;
680 GObject *plugin_obj;
681 gboolean installed;
683 plugin = plugin_for_iter(GTK_LIST_STORE(model), &iter);
684 plugin_obj = g_hash_table_lookup (activated_plugins, plugin);
685 installed = (plugin_obj != NULL) ? TRUE : FALSE;
686 gtk_tree_model_get (model, &iter, COL_PLUGIN, &plugin, -1);
687 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
688 COL_ENABLED, installed, -1);
689 } while (gtk_tree_model_iter_next (model, &iter));
693 static GHashTable*
694 plugin_set_update (AnjutaPluginManager *plugin_manager,
695 AnjutaPluginHandle* selected_plugin,
696 gboolean load)
698 AnjutaPluginManagerPriv *priv;
699 GObject *plugin_obj;
700 GList *l;
702 priv = plugin_manager->priv;
703 plugin_obj = g_hash_table_lookup (priv->activated_plugins, selected_plugin);
705 if (plugin_obj && load)
707 g_warning ("Trying to install already installed plugin '%s'",
708 anjuta_plugin_handle_get_name (selected_plugin));
709 return priv->activated_plugins;
711 if (!plugin_obj && !load)
713 g_warning ("Trying to uninstall a not installed plugin '%s'",
714 anjuta_plugin_handle_get_name (selected_plugin));
715 return priv->activated_plugins;
718 if (priv->status)
719 anjuta_status_busy_push (priv->status);
721 if (!load)
723 /* reverse priv->available_plugins when unloading, so that plugins are
724 * unloaded in the right order */
725 priv->available_plugins = g_list_reverse (priv->available_plugins);
727 for (l = priv->available_plugins; l != NULL; l = l->next)
729 AnjutaPluginHandle *plugin = l->data;
730 if (should_unload (priv->activated_plugins, selected_plugin, plugin))
732 /* FIXME: Unload the class and sharedlib if possible */
733 AnjutaPlugin *anjuta_plugin = ANJUTA_PLUGIN (plugin_obj);
734 if (!anjuta_plugin_deactivate (ANJUTA_PLUGIN (anjuta_plugin)))
736 anjuta_util_dialog_info (GTK_WINDOW (priv->shell),
737 dgettext (GETTEXT_PACKAGE, "Plugin '%s' does not want to be deactivated"),
738 anjuta_plugin_handle_get_name (plugin));
742 priv->available_plugins = g_list_reverse (priv->available_plugins);
744 else
746 for (l = priv->available_plugins; l != NULL; l = l->next)
748 AnjutaPluginHandle *plugin = l->data;
749 if (should_load (priv->activated_plugins, selected_plugin, plugin))
751 AnjutaPlugin *plugin_obj;
752 GError *error = NULL;
753 plugin_obj = g_hash_table_lookup (priv->plugins_cache, plugin);
754 if (!plugin_obj)
756 plugin_obj = activate_plugin (plugin_manager, plugin,
757 &error);
760 if (plugin_obj)
762 anjuta_plugin_activate (ANJUTA_PLUGIN (plugin_obj));
764 else
766 if (error)
768 gchar* message = g_strdup_printf (dgettext (GETTEXT_PACKAGE, "Could not load %s\n"
769 "This usually means that your installation is corrupted. The "
770 "error message leading to this was:\n%s"),
771 anjuta_plugin_handle_get_name (selected_plugin),
772 error->message);
773 anjuta_util_dialog_error (GTK_WINDOW(plugin_manager->priv->shell),
774 message);
775 g_error_free (error);
776 g_free(message);
782 if (priv->status)
783 anjuta_status_busy_pop (priv->status);
784 return priv->activated_plugins;
787 static void
788 plugin_toggled (GtkCellRendererToggle *cell, char *path_str, gpointer data)
790 AnjutaPluginManager *plugin_manager;
791 AnjutaPluginManagerPriv *priv;
792 GtkListStore *store = GTK_LIST_STORE (data);
793 GtkTreeIter iter;
794 GtkTreePath *path;
795 AnjutaPluginHandle *plugin;
796 gboolean enabled;
797 GList *activated_plugins;
798 GList *node;
799 AnjutaPlugin* plugin_object;
801 path = gtk_tree_path_new_from_string (path_str);
803 plugin_manager = g_object_get_data (G_OBJECT (store), "plugin-manager");
804 priv = plugin_manager->priv;
806 gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path);
807 gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
808 COL_ENABLED, &enabled,
809 COL_PLUGIN, &plugin,
810 -1);
812 /* Activate one plugin can force the loading of other ones, instead of
813 * searching which plugins have to be activated, we just unmerge all
814 * current plugins and merge all plugins after the modification */
816 /* unmerge all plugins */
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_unmerge (IANJUTA_PREFERENCES (plugin_object),
825 anjuta_shell_get_preferences (ANJUTA_SHELL (priv->shell), NULL),
826 NULL);
829 g_list_free (activated_plugins);
831 plugin_set_update (plugin_manager, plugin, !enabled);
833 /* Make sure that it appears in the preferences. This method
834 can only be called when the preferences dialog is active so
835 it should be save
837 activated_plugins = g_hash_table_get_values (priv->activated_plugins);
838 for (node = g_list_first (activated_plugins); node != NULL; node = g_list_next (node))
840 plugin_object = (AnjutaPlugin *)node->data;
841 if (plugin_object &&
842 IANJUTA_IS_PREFERENCES(plugin_object))
844 ianjuta_preferences_merge (IANJUTA_PREFERENCES (plugin_object),
845 anjuta_shell_get_preferences (ANJUTA_SHELL (priv->shell), NULL),
846 NULL);
849 g_list_free (activated_plugins);
851 update_enabled (GTK_TREE_MODEL (store), priv->activated_plugins);
852 gtk_tree_path_free (path);
855 #if 0
856 static void
857 selection_changed (GtkTreeSelection *selection, GtkListStore *store)
859 GtkTreeIter iter;
861 if (gtk_tree_selection_get_selected (selection, NULL,
862 &iter)) {
863 GtkTextBuffer *buffer;
865 GtkWidget *txt = g_object_get_data (G_OBJECT (store),
866 "AboutText");
868 GtkWidget *image = g_object_get_data (G_OBJECT (store),
869 "Icon");
870 AnjutaPluginHandle *plugin = plugin_for_iter (store, &iter);
872 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (txt));
873 gtk_text_buffer_set_text (buffer, plugin->about, -1);
875 if (plugin->icon_path) {
876 gtk_image_set_from_file (GTK_IMAGE (image),
877 plugin->icon_path);
878 gtk_widget_show (GTK_WIDGET (image));
879 } else {
880 gtk_widget_hide (GTK_WIDGET (image));
884 #endif
886 static GtkWidget *
887 create_plugin_tree (void)
889 GtkListStore *store;
890 GtkWidget *tree;
891 GtkCellRenderer *renderer;
892 GtkTreeViewColumn *column;
894 store = gtk_list_store_new (N_COLS,
895 G_TYPE_BOOLEAN,
896 G_TYPE_BOOLEAN,
897 GDK_TYPE_PIXBUF,
898 G_TYPE_STRING,
899 G_TYPE_POINTER);
900 tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
902 renderer = gtk_cell_renderer_toggle_new ();
903 g_signal_connect (G_OBJECT (renderer), "toggled",
904 G_CALLBACK (plugin_toggled), store);
905 column = gtk_tree_view_column_new_with_attributes (dgettext (GETTEXT_PACKAGE, "Load"),
906 renderer,
907 "active",
908 COL_ENABLED,
909 "activatable",
910 COL_ACTIVABLE,
911 NULL);
912 gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
913 gtk_tree_view_column_set_sizing (column,
914 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
916 column = gtk_tree_view_column_new ();
917 renderer = gtk_cell_renderer_pixbuf_new ();
918 gtk_tree_view_column_pack_start (column, renderer, FALSE);
919 gtk_tree_view_column_add_attribute (column, renderer, "pixbuf",
920 COL_ICON);
921 renderer = gtk_cell_renderer_text_new ();
922 g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
923 gtk_tree_view_column_pack_start (column, renderer, TRUE);
924 gtk_tree_view_column_add_attribute (column, renderer, "markup",
925 COL_NAME);
926 gtk_tree_view_column_set_sizing (column,
927 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
928 gtk_tree_view_column_set_title (column, dgettext (GETTEXT_PACKAGE, "Available Plugins"));
929 gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
930 gtk_tree_view_set_expander_column (GTK_TREE_VIEW (tree), column);
932 g_object_unref (store);
933 return tree;
936 /* Sort function for plugins */
937 static gint
938 sort_plugins(gconstpointer a, gconstpointer b)
940 g_return_val_if_fail (a != NULL, 0);
941 g_return_val_if_fail (b != NULL, 0);
943 AnjutaPluginHandle* plugin_a = ANJUTA_PLUGIN_HANDLE (a);
944 AnjutaPluginHandle* plugin_b = ANJUTA_PLUGIN_HANDLE (b);
946 return strcmp (anjuta_plugin_handle_get_name (plugin_a),
947 anjuta_plugin_handle_get_name (plugin_b));
950 /* If show_all == FALSE, show only user activatable plugins
951 * If show_all == TRUE, show all plugins
953 static void
954 populate_plugin_model (AnjutaPluginManager *plugin_manager,
955 GtkListStore *store,
956 GHashTable *plugins_to_show,
957 GHashTable *activated_plugins,
958 gboolean show_all)
960 AnjutaPluginManagerPriv *priv;
961 GList *l;
963 priv = plugin_manager->priv;
964 gtk_list_store_clear (store);
966 priv->available_plugins = g_list_sort (priv->available_plugins, sort_plugins);
968 for (l = priv->available_plugins; l != NULL; l = l->next)
970 AnjutaPluginHandle *plugin = l->data;
972 /* If plugins to show is NULL, show all available plugins */
973 if (plugins_to_show == NULL ||
974 g_hash_table_lookup (plugins_to_show, plugin))
977 gboolean enable = FALSE;
978 if (g_hash_table_lookup (activated_plugins, plugin))
979 enable = TRUE;
981 if (anjuta_plugin_handle_get_name (plugin) &&
982 anjuta_plugin_handle_get_description (plugin) &&
983 (anjuta_plugin_handle_get_user_activatable (plugin) ||
984 show_all))
986 GtkTreeIter iter;
987 gchar *text;
989 text = g_markup_printf_escaped ("<span size=\"larger\" weight=\"bold\">%s</span>\n%s",
990 anjuta_plugin_handle_get_name (plugin),
991 anjuta_plugin_handle_get_about (plugin));
993 gtk_list_store_append (store, &iter);
994 gtk_list_store_set (store, &iter,
995 COL_ACTIVABLE,
996 anjuta_plugin_handle_get_user_activatable (plugin),
997 COL_ENABLED, enable,
998 COL_NAME, text,
999 COL_PLUGIN, plugin,
1000 -1);
1001 if (anjuta_plugin_handle_get_icon_path (plugin))
1003 GdkPixbuf *icon;
1004 icon = gdk_pixbuf_new_from_file_at_size (anjuta_plugin_handle_get_icon_path (plugin),
1005 32, 32, NULL);
1006 if (icon) {
1007 gtk_list_store_set (store, &iter,
1008 COL_ICON, icon, -1);
1009 g_object_unref (icon);
1012 g_free (text);
1018 static GtkWidget *
1019 create_remembered_plugins_tree (void)
1021 GtkListStore *store;
1022 GtkWidget *tree;
1023 GtkCellRenderer *renderer;
1024 GtkTreeViewColumn *column;
1026 store = gtk_list_store_new (N_REM_COLS, GDK_TYPE_PIXBUF, G_TYPE_STRING,
1027 G_TYPE_STRING);
1028 tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
1030 column = gtk_tree_view_column_new ();
1031 renderer = gtk_cell_renderer_pixbuf_new ();
1032 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1033 gtk_tree_view_column_add_attribute (column, renderer, "pixbuf",
1034 COL_REM_ICON);
1035 renderer = gtk_cell_renderer_text_new ();
1036 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1037 gtk_tree_view_column_add_attribute (column, renderer, "markup",
1038 COL_REM_NAME);
1039 gtk_tree_view_column_set_sizing (column,
1040 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1041 gtk_tree_view_column_set_title (column, dgettext (GETTEXT_PACKAGE, "Preferred plugins"));
1042 gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
1043 gtk_tree_view_set_expander_column (GTK_TREE_VIEW (tree), column);
1045 g_object_unref (store);
1046 return tree;
1049 static void
1050 foreach_remembered_plugin (gpointer key, gpointer value, gpointer user_data)
1052 AnjutaPluginDescription *desc = (AnjutaPluginDescription *) value;
1053 GtkListStore *store = GTK_LIST_STORE (user_data);
1054 AnjutaPluginManager *manager = g_object_get_data (G_OBJECT (store),
1055 "plugin-manager");
1056 AnjutaPluginHandle *plugin =
1057 g_hash_table_lookup (manager->priv->plugins_by_description, desc);
1058 g_return_if_fail (plugin != NULL);
1060 if (anjuta_plugin_handle_get_name (plugin) &&
1061 anjuta_plugin_handle_get_description (plugin))
1063 GtkTreeIter iter;
1064 gchar *text;
1066 text = g_markup_printf_escaped ("<span size=\"larger\" weight=\"bold\">%s</span>\n%s",
1067 anjuta_plugin_handle_get_name (plugin),
1068 anjuta_plugin_handle_get_about (plugin));
1070 gtk_list_store_append (store, &iter);
1071 gtk_list_store_set (store, &iter,
1072 COL_REM_NAME, text,
1073 COL_REM_PLUGIN_KEY, key,
1074 -1);
1075 if (anjuta_plugin_handle_get_icon_path (plugin))
1077 GdkPixbuf *icon;
1078 icon = gdk_pixbuf_new_from_file_at_size (anjuta_plugin_handle_get_icon_path (plugin),
1079 32, 32, NULL);
1080 if (icon) {
1081 gtk_list_store_set (store, &iter,
1082 COL_REM_ICON, icon, -1);
1083 g_object_unref (icon);
1086 g_free (text);
1090 static void
1091 populate_remembered_plugins_model (AnjutaPluginManager *plugin_manager,
1092 GtkListStore *store)
1094 AnjutaPluginManagerPriv *priv = plugin_manager->priv;
1095 gtk_list_store_clear (store);
1096 g_hash_table_foreach (priv->remember_plugins, foreach_remembered_plugin,
1097 store);
1100 static void
1101 on_show_all_plugins_toggled (GtkToggleButton *button, GtkListStore *store)
1103 AnjutaPluginManager *plugin_manager;
1105 plugin_manager = g_object_get_data (G_OBJECT (button), "__plugin_manager");
1107 populate_plugin_model (plugin_manager, store, NULL,
1108 plugin_manager->priv->activated_plugins,
1109 !gtk_toggle_button_get_active (button));
1112 static void
1113 on_forget_plugin_clicked (GtkWidget *button, GtkTreeView *view)
1115 GtkTreeIter iter;
1116 GtkTreeModel *model;
1117 GtkTreeSelection *selection = gtk_tree_view_get_selection (view);
1118 if (gtk_tree_selection_get_selected (selection, &model, &iter))
1120 gchar *plugin_key;
1121 AnjutaPluginManager *manager = g_object_get_data (G_OBJECT (model),
1122 "plugin-manager");
1123 gtk_tree_model_get (model, &iter, COL_REM_PLUGIN_KEY, &plugin_key, -1);
1124 g_hash_table_remove (manager->priv->remember_plugins, plugin_key);
1125 gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
1126 g_free (plugin_key);
1130 static void
1131 on_forget_plugin_sel_changed (GtkTreeSelection *selection,
1132 GtkWidget *button)
1134 GtkTreeIter iter;
1136 if (gtk_tree_selection_get_selected (selection, NULL, &iter))
1137 gtk_widget_set_sensitive (button, TRUE);
1138 else
1139 gtk_widget_set_sensitive (button, FALSE);
1142 GtkWidget *
1143 anjuta_plugin_manager_get_plugins_page (AnjutaPluginManager *plugin_manager)
1145 GtkWidget *vbox;
1146 GtkWidget *checkbutton;
1147 GtkWidget *tree;
1148 GtkWidget *scrolled;
1149 GtkWidget *toolbar;
1150 GtkWidget *toolitem;
1151 GtkListStore *store;
1153 /* Plugins page */
1154 vbox = gtk_vbox_new (FALSE, 0);
1155 gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
1157 scrolled = gtk_scrolled_window_new (NULL, NULL);
1158 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
1159 GTK_SHADOW_IN);
1160 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
1161 GTK_POLICY_NEVER,
1162 GTK_POLICY_AUTOMATIC);
1163 gtk_style_context_set_junction_sides (gtk_widget_get_style_context (scrolled), GTK_JUNCTION_BOTTOM);
1164 gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 0);
1166 toolbar = gtk_toolbar_new ();
1167 gtk_style_context_add_class (gtk_widget_get_style_context (toolbar), GTK_STYLE_CLASS_INLINE_TOOLBAR);
1168 gtk_style_context_set_junction_sides (gtk_widget_get_style_context (toolbar), GTK_JUNCTION_TOP);
1169 gtk_box_pack_start (GTK_BOX (vbox), toolbar, FALSE, FALSE, 0);
1170 gtk_widget_show (toolbar);
1172 toolitem = gtk_tool_item_new ();
1173 gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (toolitem), 0);
1174 gtk_widget_show (toolitem);
1176 checkbutton = gtk_check_button_new_with_label (dgettext (GETTEXT_PACKAGE, "Only show user activatable plugins"));
1177 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), TRUE);
1178 gtk_container_add (GTK_CONTAINER (toolitem), checkbutton);
1180 tree = create_plugin_tree ();
1181 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (tree), TRUE);
1182 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree), FALSE);
1183 store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (tree)));
1185 populate_plugin_model (plugin_manager, store, NULL,
1186 plugin_manager->priv->activated_plugins, FALSE);
1188 gtk_container_add (GTK_CONTAINER (scrolled), tree);
1189 g_object_set_data (G_OBJECT (store), "plugin-manager", plugin_manager);
1192 g_object_set_data (G_OBJECT (checkbutton), "__plugin_manager", plugin_manager);
1193 g_signal_connect (G_OBJECT (checkbutton), "toggled",
1194 G_CALLBACK (on_show_all_plugins_toggled),
1195 store);
1196 gtk_widget_show_all (vbox);
1197 return vbox;
1200 GtkWidget *
1201 anjuta_plugin_manager_get_remembered_plugins_page (AnjutaPluginManager *plugin_manager)
1203 GtkWidget *vbox;
1204 GtkWidget *tree;
1205 GtkWidget *scrolled;
1206 GtkListStore *store;
1207 GtkWidget *hbox;
1208 GtkWidget *display_label;
1209 GtkWidget *forget_button;
1210 GtkTreeSelection *selection;
1212 /* Remembered plugin */
1213 vbox = gtk_vbox_new (FALSE, 10);
1214 gtk_container_set_border_width (GTK_CONTAINER (vbox), 10);
1216 display_label = gtk_label_new (dgettext (GETTEXT_PACKAGE, "These are the plugins selected by you "
1217 "when Anjuta prompted to choose one of "
1218 "many suitable plugins. Removing the "
1219 "preferred plugin will let Anjuta prompt "
1220 "you again to choose different plugin."));
1221 gtk_label_set_line_wrap (GTK_LABEL (display_label), TRUE);
1222 gtk_box_pack_start (GTK_BOX (vbox), display_label, FALSE, FALSE, 0);
1224 scrolled = gtk_scrolled_window_new (NULL, NULL);
1225 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
1226 GTK_SHADOW_IN);
1227 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
1228 GTK_POLICY_AUTOMATIC,
1229 GTK_POLICY_AUTOMATIC);
1230 gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 0);
1232 tree = create_remembered_plugins_tree ();
1233 store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (tree)));
1235 gtk_container_add (GTK_CONTAINER (scrolled), tree);
1236 g_object_set_data (G_OBJECT (store), "plugin-manager", plugin_manager);
1237 populate_remembered_plugins_model (plugin_manager, store);
1239 hbox = gtk_hbox_new (FALSE, 0);
1240 gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
1241 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
1242 forget_button = gtk_button_new_with_label (dgettext (GETTEXT_PACKAGE, "Forget selected plugin"));
1243 gtk_widget_set_sensitive (forget_button, FALSE);
1244 gtk_box_pack_end (GTK_BOX (hbox), forget_button, FALSE, FALSE, 0);
1246 g_signal_connect (forget_button, "clicked",
1247 G_CALLBACK (on_forget_plugin_clicked),
1248 tree);
1249 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree));
1250 g_signal_connect (selection, "changed",
1251 G_CALLBACK (on_forget_plugin_sel_changed),
1252 forget_button);
1253 gtk_widget_show_all (vbox);
1254 return vbox;
1257 static GList *
1258 property_to_list (const char *value)
1260 GList *l = NULL;
1261 char **split_str;
1262 char **p;
1264 split_str = g_strsplit (value, ",", -1);
1265 for (p = split_str; *p != NULL; p++) {
1266 l = g_list_prepend (l, g_strdup (g_strstrip (*p)));
1268 g_strfreev (split_str);
1269 return l;
1272 static IAnjutaPluginFactory*
1273 get_plugin_factory (AnjutaPluginManager *plugin_manager,
1274 const gchar *language,
1275 GError **error)
1277 AnjutaPluginManagerPriv *priv;
1278 AnjutaPluginHandle *plugin;
1279 GList *loader_plugins, *node;
1280 GList *valid_plugins;
1281 GObject *obj = NULL;
1283 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), G_TYPE_INVALID);
1286 if ((language == NULL) || (g_ascii_strcasecmp (language, "C") == 0))
1288 /* Support of C plugin is built-in */
1289 return IANJUTA_PLUGIN_FACTORY (anjuta_plugin_factory);
1292 priv = plugin_manager->priv;
1293 plugin = NULL;
1295 /* Find all plugins implementing the IAnjutaPluginLoader interface. */
1296 loader_plugins = g_hash_table_lookup (priv->plugins_by_interfaces, "IAnjutaPluginLoader");
1298 /* Create a list of loader supporting this language */
1299 node = loader_plugins;
1300 valid_plugins = NULL;
1301 while (node)
1303 AnjutaPluginDescription *desc;
1304 gchar *val;
1305 GList *vals = NULL;
1306 GList *l_node;
1307 gboolean found;
1309 plugin = node->data;
1311 desc = anjuta_plugin_handle_get_description (plugin);
1312 if (anjuta_plugin_description_get_string (desc, "Plugin Loader", "SupportedLanguage", &val))
1314 if (val != NULL)
1316 vals = property_to_list (val);
1317 g_free (val);
1321 found = FALSE;
1322 l_node = vals;
1323 while (l_node)
1325 if (!found && (g_ascii_strcasecmp (l_node->data, language) == 0))
1327 found = TRUE;
1329 g_free (l_node->data);
1330 l_node = g_list_next (l_node);
1332 g_list_free (vals);
1334 if (found)
1336 valid_plugins = g_list_prepend (valid_plugins, plugin);
1339 node = g_list_next (node);
1342 /* Find the first installed plugin from the valid plugins */
1343 node = valid_plugins;
1344 while (node)
1346 plugin = node->data;
1347 obj = g_hash_table_lookup (priv->activated_plugins, plugin);
1348 if (obj) break;
1349 node = g_list_next (node);
1352 /* If no plugin is installed yet, do something */
1353 if ((obj == NULL) && valid_plugins && g_list_length (valid_plugins) == 1)
1355 /* If there is just one plugin, consider it selected */
1356 plugin = valid_plugins->data;
1358 /* Install and return it */
1359 plugin_set_update (plugin_manager, plugin, TRUE);
1360 obj = g_hash_table_lookup (priv->activated_plugins, plugin);
1362 else if ((obj == NULL) && valid_plugins)
1364 /* Prompt the user to select one of these plugins */
1366 GList *descs = NULL;
1367 node = valid_plugins;
1368 while (node)
1370 plugin = node->data;
1371 descs = g_list_prepend (descs, anjuta_plugin_handle_get_description (plugin));
1372 node = g_list_next (node);
1374 descs = g_list_reverse (descs);
1375 obj = anjuta_plugin_manager_select_and_activate (plugin_manager,
1376 dgettext (GETTEXT_PACKAGE, "Select a plugin"),
1377 dgettext (GETTEXT_PACKAGE, "Please select a plugin to activate"),
1378 descs);
1379 g_list_free (descs);
1381 g_list_free (valid_plugins);
1383 if (obj != NULL)
1385 return IANJUTA_PLUGIN_FACTORY (obj);
1388 /* No plugin implementing this interface found */
1389 g_set_error (error, ANJUTA_PLUGIN_MANAGER_ERROR,
1390 ANJUTA_PLUGIN_MANAGER_MISSING_FACTORY,
1391 dgettext (GETTEXT_PACKAGE, "No plugin is able to load other plugins in %s"), language);
1393 return NULL;
1396 static void
1397 on_is_active_plugins_foreach (gpointer key, gpointer data, gpointer user_data)
1399 AnjutaPluginHandle *handle = ANJUTA_PLUGIN_HANDLE (key);
1400 gchar const **search_iface = (gchar const **)user_data;
1402 if (*search_iface != NULL)
1404 GList *interfaces;
1405 GList *found;
1407 interfaces = anjuta_plugin_handle_get_interfaces (handle);
1409 for (found = g_list_first (interfaces); found != NULL; found = g_list_next (found))
1413 found = g_list_find_custom (interfaces, *search_iface, (GCompareFunc)strcmp);
1415 if (found != NULL) *search_iface = NULL;
1420 * anjuta_plugin_manager_is_active_plugin:
1421 * @plugin_manager: A #AnjutaPluginManager object
1422 * @iface_name: The interface implemented by the object to be found
1424 * Searches if a currently loaded plugins implements
1425 * the given interface.
1427 * Return value: True is the plugin is currently loaded.
1430 gboolean
1431 anjuta_plugin_manager_is_active_plugin (AnjutaPluginManager *plugin_manager,
1432 const gchar *iface_name)
1434 const gchar *search_iface = iface_name;
1436 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), FALSE);
1438 g_hash_table_foreach (plugin_manager->priv->activated_plugins,
1439 on_is_active_plugins_foreach,
1440 &search_iface);
1442 return search_iface == NULL;
1446 * anjuta_plugin_manager_get_plugin:
1447 * @plugin_manager: A #AnjutaPluginManager object
1448 * @iface_name: The interface implemented by the object to be found
1450 * Searches the currently available plugins to find the one which
1451 * implements the given interface as primary interface and returns it. If
1452 * the plugin is not yet loaded, it will be loaded and activated.
1453 * It only searches
1454 * from the pool of plugin objects loaded in this shell and can only search
1455 * by primary interface. If there are more objects implementing this primary
1456 * interface, user might be prompted to select one from them (and might give
1457 * the option to use it as default for future queries). A typical usage of this
1458 * function is:
1459 * <programlisting>
1460 * GObject *docman =
1461 * anjuta_plugin_manager_get_plugin (plugin_manager, "IAnjutaDocumentManager", error);
1462 * </programlisting>
1463 * Notice that this function takes the interface name string as string, unlike
1464 * anjuta_plugins_get_interface() which takes the type directly.
1465 * If no plugin implementing this interface can be found, returns NULL.
1467 * Return value: The plugin object (subclass of #AnjutaPlugin) which implements
1468 * the given interface or NULL. See #AnjutaPlugin for more detail on interfaces
1469 * implemented by plugins.
1471 GObject *
1472 anjuta_plugin_manager_get_plugin (AnjutaPluginManager *plugin_manager,
1473 const gchar *iface_name)
1475 AnjutaPluginManagerPriv *priv;
1476 AnjutaPluginHandle *plugin;
1477 GList *valid_plugins, *node;
1479 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
1480 g_return_val_if_fail (iface_name != NULL, NULL);
1482 priv = plugin_manager->priv;
1483 plugin = NULL;
1485 /* Find all plugins implementing this (primary) interface. */
1486 valid_plugins = g_hash_table_lookup (priv->plugins_by_interfaces, iface_name);
1488 /* Find the first installed plugin from the valid plugins */
1489 node = valid_plugins;
1490 while (node)
1492 GObject *obj;
1493 plugin = node->data;
1494 obj = g_hash_table_lookup (priv->activated_plugins, plugin);
1495 if (obj)
1496 return obj;
1497 node = g_list_next (node);
1500 /* If no plugin is installed yet, do something */
1501 if (valid_plugins && g_list_length (valid_plugins) == 1)
1503 /* If there is just one plugin, consider it selected */
1504 GObject *obj;
1505 plugin = valid_plugins->data;
1507 /* Install and return it */
1508 plugin_set_update (plugin_manager, plugin, TRUE);
1509 obj = g_hash_table_lookup (priv->activated_plugins, plugin);
1511 return obj;
1513 else if (valid_plugins)
1515 /* Prompt the user to select one of these plugins */
1516 GObject *obj;
1517 GList *descs = NULL;
1518 node = valid_plugins;
1519 while (node)
1521 plugin = node->data;
1522 descs = g_list_prepend (descs, anjuta_plugin_handle_get_description (plugin));
1523 node = g_list_next (node);
1525 descs = g_list_reverse (descs);
1526 obj = anjuta_plugin_manager_select_and_activate (plugin_manager,
1527 dgettext (GETTEXT_PACKAGE, "Select a plugin"),
1528 dgettext (GETTEXT_PACKAGE, "<b>Please select a plugin to activate</b>"),
1529 descs);
1530 g_list_free (descs);
1531 return obj;
1534 /* No plugin implementing this interface found */
1535 return NULL;
1538 GObject *
1539 anjuta_plugin_manager_get_plugin_by_id (AnjutaPluginManager *plugin_manager,
1540 const gchar *plugin_id)
1542 AnjutaPluginManagerPriv *priv;
1543 AnjutaPluginHandle *plugin;
1545 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
1546 g_return_val_if_fail (plugin_id != NULL, NULL);
1548 priv = plugin_manager->priv;
1549 plugin = g_hash_table_lookup (priv->plugins_by_name, plugin_id);
1550 if (plugin)
1552 GObject *obj;
1553 obj = g_hash_table_lookup (priv->activated_plugins, plugin);
1554 if (obj)
1556 return obj;
1557 } else
1559 plugin_set_update (plugin_manager, plugin, TRUE);
1560 obj = g_hash_table_lookup (priv->activated_plugins, plugin);
1561 return obj;
1564 g_warning ("No plugin found with id \"%s\".", plugin_id);
1565 return NULL;
1568 static void
1569 on_activated_plugins_foreach (gpointer key, gpointer data, gpointer user_data)
1571 AnjutaPluginHandle *plugin = ANJUTA_PLUGIN_HANDLE (key);
1572 GList **active_plugins = (GList **)user_data;
1573 *active_plugins = g_list_prepend (*active_plugins,
1574 anjuta_plugin_handle_get_description (plugin));
1577 static void
1578 on_activated_plugin_objects_foreach (gpointer key, gpointer data, gpointer user_data)
1580 GList **active_plugins = (GList **)user_data;
1581 *active_plugins = g_list_prepend (*active_plugins,
1582 data);
1585 GList*
1586 anjuta_plugin_manager_get_active_plugins (AnjutaPluginManager *plugin_manager)
1588 GList *active_plugins = NULL;
1590 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
1591 g_hash_table_foreach (plugin_manager->priv->activated_plugins,
1592 on_activated_plugins_foreach,
1593 &active_plugins);
1594 return g_list_reverse (active_plugins);
1597 GList*
1598 anjuta_plugin_manager_get_active_plugin_objects (AnjutaPluginManager *plugin_manager)
1600 GList *active_plugins = NULL;
1602 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
1603 g_hash_table_foreach (plugin_manager->priv->activated_plugins,
1604 on_activated_plugin_objects_foreach,
1605 &active_plugins);
1606 return g_list_reverse (active_plugins);
1609 gboolean
1610 anjuta_plugin_manager_unload_plugin_by_id (AnjutaPluginManager *plugin_manager,
1611 const gchar *plugin_id)
1613 AnjutaPluginManagerPriv *priv;
1614 AnjutaPluginHandle *plugin;
1616 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), FALSE);
1617 g_return_val_if_fail (plugin_id != NULL, FALSE);
1619 priv = plugin_manager->priv;
1621 plugin = g_hash_table_lookup (priv->plugins_by_name, plugin_id);
1622 if (plugin)
1624 plugin_set_update (plugin_manager, plugin, FALSE);
1626 /* Check if the plugin has been indeed unloaded */
1627 if (!g_hash_table_lookup (priv->activated_plugins, plugin))
1628 return TRUE;
1629 else
1630 return FALSE;
1632 g_warning ("No plugin found with id \"%s\".", plugin_id);
1633 return FALSE;
1636 static gboolean
1637 find_plugin_for_object (gpointer key, gpointer value, gpointer data)
1639 if (value == data)
1641 g_object_set_data (G_OBJECT (data), "__plugin_plugin", key);
1642 return TRUE;
1644 return FALSE;
1647 gboolean
1648 anjuta_plugin_manager_unload_plugin (AnjutaPluginManager *plugin_manager,
1649 GObject *plugin_object)
1651 AnjutaPluginManagerPriv *priv;
1652 AnjutaPluginHandle *plugin;
1654 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), FALSE);
1655 g_return_val_if_fail (ANJUTA_IS_PLUGIN (plugin_object), FALSE);
1657 priv = plugin_manager->priv;
1659 plugin = NULL;
1661 /* Find the plugin that correspond to this plugin object */
1662 g_hash_table_find (priv->activated_plugins, find_plugin_for_object,
1663 plugin_object);
1664 plugin = g_object_get_data (G_OBJECT (plugin_object), "__plugin_plugin");
1666 if (plugin)
1668 plugin_set_update (plugin_manager, plugin, FALSE);
1670 /* Check if the plugin has been indeed unloaded */
1671 if (!g_hash_table_lookup (priv->activated_plugins, plugin))
1672 return TRUE;
1673 else
1674 return FALSE;
1676 g_warning ("No plugin found with object \"%p\".", plugin_object);
1677 return FALSE;
1680 GList*
1681 anjuta_plugin_manager_list_query (AnjutaPluginManager *plugin_manager,
1682 GList *secs,
1683 GList *anames,
1684 GList *avalues)
1686 AnjutaPluginManagerPriv *priv;
1687 GList *selected_plugins = NULL;
1688 const gchar *sec;
1689 const gchar *aname;
1690 const gchar *avalue;
1691 GList *available;
1693 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
1695 priv = plugin_manager->priv;
1696 available = priv->available_plugins;
1698 if (secs == NULL)
1700 /* If no query is given, select all plugins */
1701 while (available)
1703 AnjutaPluginHandle *plugin = available->data;
1704 AnjutaPluginDescription *desc =
1705 anjuta_plugin_handle_get_description (plugin);
1706 selected_plugins = g_list_prepend (selected_plugins, desc);
1707 available = g_list_next (available);
1709 return g_list_reverse (selected_plugins);
1712 g_return_val_if_fail (secs != NULL, NULL);
1713 g_return_val_if_fail (anames != NULL, NULL);
1714 g_return_val_if_fail (avalues != NULL, NULL);
1716 while (available)
1718 GList* s_node = secs;
1719 GList* n_node = anames;
1720 GList* v_node = avalues;
1722 gboolean satisfied = FALSE;
1724 AnjutaPluginHandle *plugin = available->data;
1725 AnjutaPluginDescription *desc =
1726 anjuta_plugin_handle_get_description (plugin);
1728 while (s_node)
1730 gchar *val;
1731 GList *vals;
1732 GList *node;
1733 gboolean found = FALSE;
1735 satisfied = TRUE;
1737 sec = s_node->data;
1738 aname = n_node->data;
1739 avalue = v_node->data;
1741 if (!anjuta_plugin_description_get_string (desc, sec, aname, &val))
1743 satisfied = FALSE;
1744 break;
1747 vals = property_to_list (val);
1748 g_free (val);
1750 node = vals;
1751 while (node)
1753 if (strchr(node->data, '*') != NULL)
1755 // Star match.
1756 gchar **segments;
1757 gchar **seg_ptr;
1758 const gchar *cursor;
1760 segments = g_strsplit (node->data, "*", -1);
1762 seg_ptr = segments;
1763 cursor = avalue;
1764 while (*seg_ptr != NULL)
1766 if (strlen (*seg_ptr) > 0) {
1767 cursor = strstr (cursor, *seg_ptr);
1768 if (cursor == NULL)
1769 break;
1771 cursor += strlen (*seg_ptr);
1772 seg_ptr++;
1774 if (*seg_ptr == NULL)
1775 found = TRUE;
1776 g_strfreev (segments);
1778 else if (g_ascii_strcasecmp (node->data, avalue) == 0)
1780 // String match.
1781 found = TRUE;
1783 g_free (node->data);
1784 node = g_list_next (node);
1786 g_list_free (vals);
1787 if (!found)
1789 satisfied = FALSE;
1790 break;
1792 s_node = g_list_next (s_node);
1793 n_node = g_list_next (n_node);
1794 v_node = g_list_next (v_node);
1796 if (satisfied)
1798 selected_plugins = g_list_prepend (selected_plugins, desc);
1799 /* DEBUG_PRINT ("Satisfied, Adding %s",
1800 anjuta_plugin_handle_get_name (plugin));*/
1802 available = g_list_next (available);
1805 return g_list_reverse (selected_plugins);
1808 GList*
1809 anjuta_plugin_manager_query (AnjutaPluginManager *plugin_manager,
1810 const gchar *section_name,
1811 const gchar *attribute_name,
1812 const gchar *attribute_value,
1813 ...)
1815 va_list var_args;
1816 GList *secs = NULL;
1817 GList *anames = NULL;
1818 GList *avalues = NULL;
1819 const gchar *sec;
1820 const gchar *aname;
1821 const gchar *avalue;
1822 GList *selected_plugins;
1825 if (section_name == NULL)
1827 /* If no query is given, select all plugins */
1828 return anjuta_plugin_manager_list_query (plugin_manager, NULL, NULL, NULL);
1831 g_return_val_if_fail (section_name != NULL, NULL);
1832 g_return_val_if_fail (attribute_name != NULL, NULL);
1833 g_return_val_if_fail (attribute_value != NULL, NULL);
1835 secs = g_list_prepend (secs, g_strdup (section_name));
1836 anames = g_list_prepend (anames, g_strdup (attribute_name));
1837 avalues = g_list_prepend (avalues, g_strdup (attribute_value));
1839 va_start (var_args, attribute_value);
1842 sec = va_arg (var_args, const gchar *);
1843 if (sec)
1845 aname = va_arg (var_args, const gchar *);
1846 if (aname)
1848 avalue = va_arg (var_args, const gchar *);
1849 if (avalue)
1851 secs = g_list_prepend (secs, g_strdup (sec));
1852 anames = g_list_prepend (anames, g_strdup (aname));
1853 avalues = g_list_prepend (avalues, g_strdup (avalue));
1858 while (sec);
1859 va_end (var_args);
1861 secs = g_list_reverse (secs);
1862 anames = g_list_reverse (anames);
1863 avalues = g_list_reverse (avalues);
1865 selected_plugins = anjuta_plugin_manager_list_query (plugin_manager,
1866 secs,
1867 anames,
1868 avalues);
1870 anjuta_util_glist_strings_free (secs);
1871 anjuta_util_glist_strings_free (anames);
1872 anjuta_util_glist_strings_free (avalues);
1874 return selected_plugins;
1877 enum {
1878 PIXBUF_COLUMN,
1879 PLUGIN_COLUMN,
1880 PLUGIN_DESCRIPTION_COLUMN,
1881 N_COLUMNS
1884 static void
1885 on_plugin_list_row_activated (GtkTreeView *tree_view,
1886 GtkTreePath *path,
1887 GtkTreeViewColumn *column,
1888 GtkDialog *dialog)
1890 gtk_dialog_response (dialog, GTK_RESPONSE_OK);
1894 static void
1895 on_plugin_list_show (GtkTreeView *view,
1896 GtkDirectionType direction,
1897 GtkDialog *dialog)
1899 GtkTreeSelection *selection;
1900 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
1902 g_signal_emit_by_name (G_OBJECT (selection), "changed", GTK_DIALOG(dialog), NULL);
1906 static void
1907 on_plugin_list_selection_changed (GtkTreeSelection *tree_selection,
1908 GtkDialog *dialog)
1910 GtkContainer *action_area;
1911 GList *list;
1912 GtkButton *bt = NULL;
1914 action_area = GTK_CONTAINER (gtk_dialog_get_action_area (dialog));
1915 list = gtk_container_get_children (action_area);
1916 for (; list; list = list->next) {
1917 bt = list->data;
1918 if (!strcmp("gtk-ok", gtk_button_get_label (bt)))
1919 break;
1921 if (bt && gtk_tree_selection_get_selected (tree_selection, NULL, NULL))
1922 gtk_widget_set_sensitive ((GtkWidget *) bt, TRUE);
1923 else
1924 gtk_widget_set_sensitive ((GtkWidget *) bt, FALSE);
1925 g_list_free(list);
1929 * anjuta_plugin_manager_select:
1930 * @plugin_manager: #AnjutaPluginManager object
1931 * @title: Title of the dialog
1932 * @description: label shown on the dialog
1933 * @plugin_descriptions: List of #AnjutaPluginDescription
1935 * Show a dialog where the user can choose between the given plugins
1937 * Returns: The chosen plugin description
1939 AnjutaPluginDescription *
1940 anjuta_plugin_manager_select (AnjutaPluginManager *plugin_manager,
1941 gchar *title, gchar *description,
1942 GList *plugin_descriptions)
1944 AnjutaPluginDescription *desc;
1945 AnjutaPluginManagerPriv *priv;
1946 GtkWidget *dlg;
1947 GtkTreeModel *model;
1948 GtkWidget *view;
1949 GtkTreeViewColumn *column;
1950 GtkCellRenderer *renderer;
1951 GList *node;
1952 GtkWidget *label;
1953 GtkWidget *content_area;
1954 GtkWidget *sc;
1955 GtkWidget *remember_checkbox;
1956 gint response;
1957 GtkTreeIter selected;
1958 GtkTreeSelection *selection;
1959 GtkTreeModel *store;
1960 GList *selection_ids = NULL;
1961 GString *remember_key = g_string_new ("");
1963 g_return_val_if_fail (title != NULL, NULL);
1964 g_return_val_if_fail (description != NULL, NULL);
1965 g_return_val_if_fail (plugin_descriptions != NULL, NULL);
1967 priv = plugin_manager->priv;
1969 if (g_list_length (plugin_descriptions) <= 0)
1970 return NULL;
1972 dlg = gtk_dialog_new_with_buttons (title, GTK_WINDOW (priv->shell),
1973 GTK_DIALOG_DESTROY_WITH_PARENT,
1974 GTK_STOCK_CANCEL,
1975 GTK_RESPONSE_CANCEL,
1976 GTK_STOCK_OK, GTK_RESPONSE_OK,
1977 NULL);
1978 gtk_window_set_default_size (GTK_WINDOW (dlg), 400, 300);
1980 label = gtk_label_new (description);
1981 gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
1982 gtk_widget_show (label);
1983 content_area = gtk_dialog_get_content_area (GTK_DIALOG (dlg));
1984 gtk_box_pack_start (GTK_BOX (content_area), label,
1985 FALSE, FALSE, 5);
1987 sc = gtk_scrolled_window_new (NULL, NULL);
1988 gtk_widget_show (sc);
1989 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sc),
1990 GTK_SHADOW_IN);
1991 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sc),
1992 GTK_POLICY_AUTOMATIC,
1993 GTK_POLICY_AUTOMATIC);
1995 gtk_box_pack_start (GTK_BOX (content_area), sc,
1996 TRUE, TRUE, 5);
1998 model = GTK_TREE_MODEL (gtk_list_store_new (N_COLUMNS, GDK_TYPE_PIXBUF,
1999 G_TYPE_STRING, G_TYPE_POINTER));
2000 view = gtk_tree_view_new_with_model (model);
2001 gtk_widget_show (view);
2002 gtk_container_add (GTK_CONTAINER (sc), view);
2004 column = gtk_tree_view_column_new ();
2005 gtk_tree_view_column_set_sizing (column,
2006 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
2007 gtk_tree_view_column_set_title (column, dgettext (GETTEXT_PACKAGE, "Available Plugins"));
2009 renderer = gtk_cell_renderer_pixbuf_new ();
2010 gtk_tree_view_column_pack_start (column, renderer, FALSE);
2011 gtk_tree_view_column_add_attribute (column, renderer, "pixbuf",
2012 PIXBUF_COLUMN);
2014 renderer = gtk_cell_renderer_text_new ();
2015 gtk_tree_view_column_pack_start (column, renderer, TRUE);
2016 gtk_tree_view_column_add_attribute (column, renderer, "markup",
2017 PLUGIN_COLUMN);
2019 gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
2020 gtk_tree_view_set_expander_column (GTK_TREE_VIEW (view), column);
2022 g_signal_connect (view, "row-activated",
2023 G_CALLBACK (on_plugin_list_row_activated),
2024 GTK_DIALOG(dlg));
2025 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
2026 g_signal_connect(selection, "changed",
2027 G_CALLBACK(on_plugin_list_selection_changed),
2028 GTK_DIALOG(dlg));
2029 g_signal_connect(view, "focus",
2030 G_CALLBACK(on_plugin_list_show),
2031 GTK_DIALOG(dlg));
2033 remember_checkbox =
2034 gtk_check_button_new_with_label (dgettext (GETTEXT_PACKAGE, "Remember this selection"));
2035 gtk_container_set_border_width (GTK_CONTAINER (remember_checkbox), 10);
2036 gtk_widget_show (remember_checkbox);
2037 gtk_box_pack_start (GTK_BOX (content_area), remember_checkbox,
2038 FALSE, FALSE, 0);
2040 node = plugin_descriptions;
2041 while (node)
2043 GdkPixbuf *icon_pixbuf = NULL;
2044 gchar *plugin_name = NULL;
2045 gchar *plugin_desc = NULL;
2046 gchar *icon_filename = NULL;
2047 gchar *location = NULL;
2049 desc = (AnjutaPluginDescription*)node->data;
2051 if (anjuta_plugin_description_get_string (desc,
2052 "Anjuta Plugin",
2053 "Icon",
2054 &icon_filename))
2056 gchar *icon_path = NULL;
2057 icon_path = g_strconcat (PACKAGE_PIXMAPS_DIR"/",
2058 icon_filename, NULL);
2059 g_free (icon_filename);
2060 /* DEBUG_PRINT ("Icon: %s", icon_path); */
2061 icon_pixbuf =
2062 gdk_pixbuf_new_from_file (icon_path, NULL);
2063 if (icon_pixbuf == NULL)
2065 g_warning ("Plugin pixmap not found: %s", plugin_name);
2067 g_free (icon_path);
2069 else
2071 g_warning ("Plugin does not define Icon attribute");
2073 if (!anjuta_plugin_description_get_locale_string (desc,
2074 "Anjuta Plugin",
2075 "Name",
2076 &plugin_name))
2078 g_warning ("Plugin does not define Name attribute");
2080 if (!anjuta_plugin_description_get_locale_string (desc,
2081 "Anjuta Plugin",
2082 "Description",
2083 &plugin_desc))
2085 g_warning ("Plugin does not define Description attribute");
2087 if (plugin_name && plugin_desc)
2089 GtkTreeIter iter;
2090 gchar *text;
2092 if (!anjuta_plugin_description_get_string (desc,
2093 "Anjuta Plugin",
2094 "Location",
2095 &location))
2097 g_warning ("Plugin does not define Location attribute");
2101 text = g_markup_printf_escaped ("<span size=\"larger\" weight=\"bold\">%s</span>\n%s", plugin_name, plugin_desc);
2103 gtk_list_store_append (GTK_LIST_STORE (model), &iter);
2104 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
2105 PLUGIN_COLUMN, text,
2106 PLUGIN_DESCRIPTION_COLUMN, desc, -1);
2107 if (icon_pixbuf) {
2108 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
2109 PIXBUF_COLUMN, icon_pixbuf, -1);
2110 g_object_unref (icon_pixbuf);
2112 g_free (text);
2114 selection_ids = g_list_prepend (selection_ids, location);
2116 g_free (plugin_name);
2117 g_free (plugin_desc);
2118 node = g_list_next (node);
2121 /* Prepare remembering key */
2122 selection_ids = g_list_sort (selection_ids,
2123 (GCompareFunc)strcmp);
2124 node = selection_ids;
2125 while (node)
2127 g_string_append (remember_key, (gchar*)node->data);
2128 g_string_append (remember_key, ",");
2129 node = g_list_next (node);
2131 g_list_foreach (selection_ids, (GFunc) g_free, NULL);
2132 g_list_free (selection_ids);
2134 /* Find if the selection is remembered */
2135 desc = g_hash_table_lookup (priv->remember_plugins, remember_key->str);
2136 if (desc)
2138 g_string_free (remember_key, TRUE);
2139 gtk_widget_destroy (dlg);
2140 return desc;
2143 /* Prompt dialog */
2144 response = gtk_dialog_run (GTK_DIALOG (dlg));
2145 switch (response)
2147 case GTK_RESPONSE_OK:
2148 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
2149 if (gtk_tree_selection_get_selected (selection, &store,
2150 &selected))
2152 gtk_tree_model_get (model, &selected,
2153 PLUGIN_DESCRIPTION_COLUMN, &desc, -1);
2154 if (desc)
2156 /* Remember selection */
2157 if (gtk_toggle_button_get_active
2158 (GTK_TOGGLE_BUTTON (remember_checkbox)))
2160 /* DEBUG_PRINT ("Remembering selection '%s'",
2161 remember_key->str);*/
2162 g_hash_table_insert (priv->remember_plugins,
2163 g_strdup (remember_key->str), desc);
2165 g_string_free (remember_key, TRUE);
2166 gtk_widget_destroy (dlg);
2167 return desc;
2170 break;
2172 g_string_free (remember_key, TRUE);
2173 gtk_widget_destroy (dlg);
2174 return NULL;
2177 GObject*
2178 anjuta_plugin_manager_select_and_activate (AnjutaPluginManager *plugin_manager,
2179 gchar *title,
2180 gchar *description,
2181 GList *plugin_descriptions)
2183 AnjutaPluginDescription *d;
2185 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
2187 d = anjuta_plugin_manager_select (plugin_manager, title, description,
2188 plugin_descriptions);
2189 if (d)
2191 GObject *plugin = NULL;
2192 gchar *location = NULL;
2194 anjuta_plugin_description_get_string (d,
2195 "Anjuta Plugin",
2196 "Location",
2197 &location);
2198 g_return_val_if_fail (location != NULL, NULL);
2199 plugin =
2200 anjuta_plugin_manager_get_plugin_by_id (plugin_manager, location);
2201 g_free (location);
2202 return plugin;
2204 return NULL;
2207 /* Plugin manager */
2209 static void
2210 anjuta_plugin_manager_init (AnjutaPluginManager *object)
2212 object->priv = g_new0 (AnjutaPluginManagerPriv, 1);
2213 object->priv->plugins_by_name = g_hash_table_new (g_str_hash, g_str_equal);
2214 object->priv->plugins_by_interfaces = g_hash_table_new_full (g_str_hash,
2215 g_str_equal,
2216 NULL,
2217 (GDestroyNotify) g_list_free);
2218 object->priv->plugins_by_description = g_hash_table_new (g_direct_hash,
2219 g_direct_equal);
2220 object->priv->activated_plugins = g_hash_table_new (g_direct_hash,
2221 g_direct_equal);
2222 object->priv->plugins_cache = g_hash_table_new (g_direct_hash,
2223 g_direct_equal);
2224 object->priv->remember_plugins = g_hash_table_new_full (g_str_hash,
2225 g_str_equal,
2226 g_free, NULL);
2229 static void
2230 anjuta_plugin_manager_finalize (GObject *object)
2232 AnjutaPluginManagerPriv *priv;
2233 priv = ANJUTA_PLUGIN_MANAGER (object)->priv;
2234 if (priv->available_plugins)
2236 /* anjuta_plugin_manager_unload_all_plugins (ANJUTA_PLUGIN_MANAGER (object)); */
2237 g_list_foreach (priv->available_plugins, (GFunc)g_object_unref, NULL);
2238 g_list_free (priv->available_plugins);
2239 priv->available_plugins = NULL;
2241 if (priv->activated_plugins)
2243 g_hash_table_destroy (priv->activated_plugins);
2244 priv->activated_plugins = NULL;
2246 if (priv->plugins_cache)
2248 g_hash_table_destroy (priv->plugins_cache);
2249 priv->plugins_cache = NULL;
2251 if (priv->plugins_by_name)
2253 g_hash_table_destroy (priv->plugins_by_name);
2254 priv->plugins_by_name = NULL;
2256 if (priv->plugins_by_description)
2258 g_hash_table_destroy (priv->plugins_by_description);
2259 priv->plugins_by_description = NULL;
2261 if (priv->plugins_by_interfaces)
2263 g_hash_table_destroy (priv->plugins_by_interfaces);
2264 priv->plugins_by_interfaces = NULL;
2266 if (priv->plugin_dirs)
2268 g_list_foreach (priv->plugin_dirs, (GFunc)g_free, NULL);
2269 g_list_free (priv->plugin_dirs);
2270 priv->plugin_dirs = NULL;
2272 #if 0
2273 if (anjuta_c_plugin_factory)
2275 g_object_unref (anjuta_c_plugin_factory);
2276 anjuta_c_plugin_factory = NULL;
2278 #endif
2279 G_OBJECT_CLASS (parent_class)->finalize (object);
2282 static void
2283 anjuta_plugin_manager_set_property (GObject *object, guint prop_id,
2284 const GValue *value, GParamSpec *pspec)
2286 AnjutaPluginManagerPriv *priv;
2288 g_return_if_fail (ANJUTA_IS_PLUGIN_MANAGER (object));
2289 priv = ANJUTA_PLUGIN_MANAGER (object)->priv;
2291 switch (prop_id)
2293 case PROP_STATUS:
2294 priv->status = g_value_get_object (value);
2295 break;
2296 case PROP_SHELL:
2297 priv->shell = g_value_get_object (value);
2298 break;
2299 default:
2300 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2301 break;
2305 static void
2306 anjuta_plugin_manager_get_property (GObject *object, guint prop_id,
2307 GValue *value, GParamSpec *pspec)
2309 AnjutaPluginManagerPriv *priv;
2311 g_return_if_fail (ANJUTA_IS_PLUGIN_MANAGER (object));
2312 priv = ANJUTA_PLUGIN_MANAGER (object)->priv;
2314 switch (prop_id)
2316 case PROP_SHELL:
2317 g_value_set_object (value, priv->shell);
2318 break;
2319 case PROP_STATUS:
2320 g_value_set_object (value, priv->status);
2321 break;
2322 case PROP_AVAILABLE_PLUGINS:
2323 g_value_set_pointer (value, priv->available_plugins);
2324 break;
2325 case PROP_ACTIVATED_PLUGINS:
2326 g_value_set_pointer (value, priv->activated_plugins);
2327 break;
2328 default:
2329 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2330 break;
2333 static void
2334 anjuta_plugin_manager_plugin_activated (AnjutaPluginManager *self,
2335 AnjutaPluginDescription* plugin_desc,
2336 GObject *plugin)
2338 /* TODO: Add default signal handler implementation here */
2341 static void
2342 anjuta_plugin_manager_plugin_deactivated (AnjutaPluginManager *self,
2343 AnjutaPluginDescription* plugin_desc,
2344 GObject *plugin)
2346 /* TODO: Add default signal handler implementation here */
2349 static void
2350 anjuta_plugin_manager_class_init (AnjutaPluginManagerClass *klass)
2352 GObjectClass* object_class = G_OBJECT_CLASS (klass);
2353 parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
2355 object_class->finalize = anjuta_plugin_manager_finalize;
2356 object_class->set_property = anjuta_plugin_manager_set_property;
2357 object_class->get_property = anjuta_plugin_manager_get_property;
2359 klass->plugin_activated = anjuta_plugin_manager_plugin_activated;
2360 klass->plugin_deactivated = anjuta_plugin_manager_plugin_deactivated;
2362 g_object_class_install_property (object_class,
2363 PROP_PROFILES,
2364 g_param_spec_pointer ("profiles",
2365 dgettext (GETTEXT_PACKAGE, "Profiles"),
2366 dgettext (GETTEXT_PACKAGE, "Current stack of profiles"),
2367 G_PARAM_READABLE));
2368 g_object_class_install_property (object_class,
2369 PROP_AVAILABLE_PLUGINS,
2370 g_param_spec_pointer ("available-plugins",
2371 dgettext (GETTEXT_PACKAGE, "Available plugins"),
2372 dgettext (GETTEXT_PACKAGE, "Currently available plugins found in plugin paths"),
2373 G_PARAM_READABLE));
2375 g_object_class_install_property (object_class,
2376 PROP_ACTIVATED_PLUGINS,
2377 g_param_spec_pointer ("activated-plugins",
2378 dgettext (GETTEXT_PACKAGE, "Activated plugins"),
2379 dgettext (GETTEXT_PACKAGE, "Currently activated plugins"),
2380 G_PARAM_READABLE));
2381 g_object_class_install_property (object_class,
2382 PROP_SHELL,
2383 g_param_spec_object ("shell",
2384 dgettext (GETTEXT_PACKAGE, "Anjuta Shell"),
2385 dgettext (GETTEXT_PACKAGE, "Anjuta shell for which the plugins are made"),
2386 G_TYPE_OBJECT,
2387 G_PARAM_READABLE |
2388 G_PARAM_WRITABLE |
2389 G_PARAM_CONSTRUCT));
2390 g_object_class_install_property (object_class,
2391 PROP_STATUS,
2392 g_param_spec_object ("status",
2393 dgettext (GETTEXT_PACKAGE, "Anjuta Status"),
2394 dgettext (GETTEXT_PACKAGE, "Anjuta status to use in loading and unloading of plugins"),
2395 ANJUTA_TYPE_STATUS,
2396 G_PARAM_READABLE |
2397 G_PARAM_WRITABLE |
2398 G_PARAM_CONSTRUCT));
2400 plugin_manager_signals[PLUGIN_ACTIVATED] =
2401 g_signal_new ("plugin-activated",
2402 G_OBJECT_CLASS_TYPE (klass),
2403 G_SIGNAL_RUN_FIRST,
2404 G_STRUCT_OFFSET (AnjutaPluginManagerClass,
2405 plugin_activated),
2406 NULL, NULL,
2407 anjuta_cclosure_marshal_VOID__POINTER_OBJECT,
2408 G_TYPE_NONE, 2,
2409 G_TYPE_POINTER, ANJUTA_TYPE_PLUGIN);
2411 plugin_manager_signals[PLUGIN_DEACTIVATED] =
2412 g_signal_new ("plugin-deactivated",
2413 G_OBJECT_CLASS_TYPE (klass),
2414 G_SIGNAL_RUN_FIRST,
2415 G_STRUCT_OFFSET (AnjutaPluginManagerClass,
2416 plugin_deactivated),
2417 NULL, NULL,
2418 anjuta_cclosure_marshal_VOID__POINTER_OBJECT,
2419 G_TYPE_NONE, 2,
2420 G_TYPE_POINTER, ANJUTA_TYPE_PLUGIN);
2423 GType
2424 anjuta_plugin_manager_get_type (void)
2426 static GType our_type = 0;
2428 if(our_type == 0)
2430 static const GTypeInfo our_info =
2432 sizeof (AnjutaPluginManagerClass), /* class_size */
2433 (GBaseInitFunc) NULL, /* base_init */
2434 (GBaseFinalizeFunc) NULL, /* base_finalize */
2435 (GClassInitFunc) anjuta_plugin_manager_class_init, /* class_init */
2436 (GClassFinalizeFunc) NULL, /* class_finalize */
2437 NULL /* class_data */,
2438 sizeof (AnjutaPluginManager), /* instance_size */
2439 0, /* n_preallocs */
2440 (GInstanceInitFunc) anjuta_plugin_manager_init, /* instance_init */
2441 NULL /* value_table */
2443 our_type = g_type_register_static (G_TYPE_OBJECT,
2444 "AnjutaPluginManager",
2445 &our_info, 0);
2448 return our_type;
2451 AnjutaPluginManager*
2452 anjuta_plugin_manager_new (GObject *shell, AnjutaStatus *status,
2453 GList* plugins_directories)
2455 GObject *manager_object;
2456 AnjutaPluginManager *plugin_manager;
2457 GList *cycles = NULL;
2458 const char *gnome2_path;
2459 char **pathv;
2460 char **p;
2461 GList *node;
2462 GList *plugin_dirs = NULL;
2464 /* Initialize the anjuta plugin system */
2465 manager_object = g_object_new (ANJUTA_TYPE_PLUGIN_MANAGER,
2466 "shell", shell, "status", status, NULL);
2467 plugin_manager = ANJUTA_PLUGIN_MANAGER (manager_object);
2469 if (anjuta_plugin_factory == NULL)
2471 anjuta_plugin_factory = anjuta_c_plugin_factory_new ();
2474 gnome2_path = g_getenv ("GNOME2_PATH");
2475 if (gnome2_path) {
2476 pathv = g_strsplit (gnome2_path, ":", 1);
2478 for (p = pathv; *p != NULL; p++) {
2479 char *path = g_strdup (*p);
2480 plugin_dirs = g_list_prepend (plugin_dirs, path);
2482 g_strfreev (pathv);
2485 node = plugins_directories;
2486 while (node) {
2487 if (!node->data)
2488 continue;
2489 char *path = g_strdup (node->data);
2490 plugin_dirs = g_list_prepend (plugin_dirs, path);
2491 node = g_list_next (node);
2493 plugin_dirs = g_list_reverse (plugin_dirs);
2494 /* load_plugins (); */
2496 node = plugin_dirs;
2497 while (node)
2499 load_plugins_from_directory (plugin_manager, (char*)node->data);
2500 node = g_list_next (node);
2502 resolve_dependencies (plugin_manager, &cycles);
2503 g_list_foreach(plugin_dirs, (GFunc) g_free, NULL);
2504 g_list_free(plugin_dirs);
2505 return plugin_manager;
2508 void
2509 anjuta_plugin_manager_activate_plugins (AnjutaPluginManager *plugin_manager,
2510 GList *plugins_to_activate)
2512 AnjutaPluginManagerPriv *priv;
2513 GdkPixbuf *icon_pixbuf;
2514 GList *node;
2516 priv = plugin_manager->priv;
2518 /* Freeze shell operations */
2519 anjuta_shell_freeze (ANJUTA_SHELL (priv->shell), NULL);
2520 if (plugins_to_activate)
2522 anjuta_status_progress_add_ticks (ANJUTA_STATUS (priv->status),
2523 g_list_length (plugins_to_activate));
2525 node = plugins_to_activate;
2526 while (node)
2528 AnjutaPluginDescription *d;
2529 gchar *plugin_id;
2530 gchar *icon_filename, *label;
2531 gchar *icon_path = NULL;
2533 d = node->data;
2535 icon_pixbuf = NULL;
2536 label = NULL;
2537 if (anjuta_plugin_description_get_string (d, "Anjuta Plugin",
2538 "Icon",
2539 &icon_filename))
2541 gchar *title /*, *description */;
2542 anjuta_plugin_description_get_locale_string (d, "Anjuta Plugin",
2543 "Name",
2544 &title);
2546 anjuta_plugin_description_get_locale_string (d, "Anjuta Plugin",
2547 "Description",
2548 &description);
2550 icon_path = g_strconcat (PACKAGE_PIXMAPS_DIR"/",
2551 icon_filename, NULL);
2552 /* DEBUG_PRINT ("Icon: %s", icon_path); */
2553 /* Avoid space in translated string */
2554 label = g_strconcat (dgettext (GETTEXT_PACKAGE, "Loading:"), " ", title, "...", NULL);
2555 icon_pixbuf = gdk_pixbuf_new_from_file (icon_path, NULL);
2556 if (!icon_pixbuf)
2557 g_warning ("Plugin does not define Icon: No such file %s",
2558 icon_path);
2559 g_free (icon_path);
2560 g_free (icon_filename);
2561 g_free (title);
2564 anjuta_status_progress_tick (ANJUTA_STATUS (priv->status),
2565 icon_pixbuf, label);
2566 g_free (label);
2567 if (icon_pixbuf)
2568 g_object_unref (icon_pixbuf);
2570 if (anjuta_plugin_description_get_string (d, "Anjuta Plugin",
2571 "Location", &plugin_id))
2573 GObject *plugin_obj;
2575 plugin_obj =
2576 anjuta_plugin_manager_get_plugin_by_id (plugin_manager,
2577 plugin_id);
2578 g_free (plugin_id);
2581 node = g_list_next (node);
2584 /* Thaw shell operations */
2585 anjuta_shell_thaw (ANJUTA_SHELL (priv->shell), NULL);
2588 static void
2589 on_collect (gpointer key, gpointer value, gpointer user_data)
2591 gchar *id;
2592 gchar *query = (gchar*) key;
2593 AnjutaPluginDescription *desc = (AnjutaPluginDescription *) value;
2594 GString *write_buffer = (GString *) user_data;
2596 anjuta_plugin_description_get_string (desc, "Anjuta Plugin", "Location",
2597 &id);
2598 g_string_append_printf (write_buffer, "%s=%s;", query, id);
2599 g_free (id);
2603 * anjuta_plugin_manager_get_remembered_plugins:
2604 * @plugin_manager: A #AnjutaPluginManager object
2606 * Get the list of plugins loaded when there is a choice between several
2607 * ones without asking the user.
2609 * The list format is returned as a string with the format detailed in
2610 * anjuta_plugin_manager_set_remembered_plugins().
2612 * Return value: a newly-allocated string that must be freed with g_free().
2615 gchar*
2616 anjuta_plugin_manager_get_remembered_plugins (AnjutaPluginManager *plugin_manager)
2618 AnjutaPluginManagerPriv *priv;
2619 GString *write_buffer = g_string_new ("");
2621 g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), FALSE);
2623 priv = plugin_manager->priv;
2624 g_hash_table_foreach (priv->remember_plugins, on_collect,
2625 write_buffer);
2626 return g_string_free (write_buffer, FALSE);
2629 static gboolean
2630 on_foreach_remove_true (gpointer k, gpointer v, gpointer d)
2632 return TRUE;
2636 * anjuta_plugin_manager_set_remembered_plugins:
2637 * @plugin_manager: A #AnjutaPluginManager object
2638 * @remembered_plugins: A list of prefered plugins
2640 * Set the list of plugins loaded when there is a choice between several
2641 * ones without asking the user.
2642 * The list is a string composed of elements separated by ';'. Each element
2643 * is defined with "key=value", where key is the list of possible plugins and
2644 * the value is the choosen plugin.
2646 * By the example the following element
2647 * <programlisting>
2648 * anjuta-symbol-browser:SymbolBrowserPlugin,anjuta-symbol-db:SymbolDBPlugin,=anjuta-symbol-db:SymbolDBPlugin;
2649 * </programlisting>
2650 * means if Anjuta has to choose between SymbolBrowserPlugin and
2651 * SymbolDBPlugin, it will choose SymbolDBPlugin.
2653 void
2654 anjuta_plugin_manager_set_remembered_plugins (AnjutaPluginManager *plugin_manager,
2655 const gchar *remembered_plugins)
2657 AnjutaPluginManagerPriv *priv;
2658 gchar **strv_lines, **line_idx;
2660 g_return_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager));
2661 g_return_if_fail (remembered_plugins != NULL);
2663 priv = plugin_manager->priv;
2665 g_hash_table_foreach_remove (priv->remember_plugins,
2666 on_foreach_remove_true, NULL);
2668 strv_lines = g_strsplit (remembered_plugins, ";", -1);
2669 line_idx = strv_lines;
2670 while (*line_idx)
2672 gchar **strv_keyvals;
2673 strv_keyvals = g_strsplit (*line_idx, "=", -1);
2674 if (strv_keyvals && strv_keyvals[0] && strv_keyvals[1])
2676 AnjutaPluginHandle *plugin;
2677 plugin = g_hash_table_lookup (priv->plugins_by_name,
2678 strv_keyvals[1]);
2679 if (plugin)
2681 AnjutaPluginDescription *desc;
2682 desc = anjuta_plugin_handle_get_description (plugin);
2684 DEBUG_PRINT ("Restoring remember plugin: %s=%s",
2685 strv_keyvals[0],
2686 strv_keyvals[1]);
2688 g_hash_table_insert (priv->remember_plugins,
2689 g_strdup (strv_keyvals[0]), desc);
2691 g_strfreev (strv_keyvals);
2693 line_idx++;
2695 g_strfreev (strv_lines);