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.
10  *
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.
15  *
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
19  */
20 
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
27  *
28  */
29 
30 #ifdef HAVE_CONFIG_H
31 #  include <config.h>
32 #endif
33 
34 #include <sys/types.h>
35 #include <dirent.h>
36 #include <string.h>
37 
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>
46 
47 
48 enum
49 {
50 	PROP_0,
51 
52 	PROP_SHELL,
53 	PROP_STATUS,
54 	PROP_PROFILES,
55 	PROP_AVAILABLE_PLUGINS,
56 	PROP_ACTIVATED_PLUGINS
57 };
58 
59 enum
60 {
61 	PROFILE_PUSHED,
62 	PROFILE_POPPED,
63 	PLUGINS_TO_LOAD,
64 	PLUGINS_TO_UNLOAD,
65 	PLUGIN_ACTIVATED,
66 	PLUGIN_DEACTIVATED,
67 
68 	LAST_SIGNAL
69 };
70 
71 struct _AnjutaPluginManagerPriv
72 {
73 	GObject      *shell;
74 	AnjutaStatus *status;
75 	GList        *plugin_dirs;
76 	GList        *available_plugins;
77 
78 	/* Indexes => plugin handles */
79 	GHashTable   *plugins_by_interfaces;
80 	GHashTable   *plugins_by_name;
81 	GHashTable   *plugins_by_description;
82 
83 	/* Plugins that are currently activated */
84 	GHashTable   *activated_plugins;
85 
86 	/* Plugins that have been previously loaded but current deactivated */
87 	GHashTable   *plugins_cache;
88 
89 	/* Remember plugin selection */
90 	GHashTable   *remember_plugins;
91 
92 	/* disable plugins */
93 	GHashTable   *disable_plugins;
94 };
95 
96 /* Available plugins page treeview */
97 enum {
98 	COL_ACTIVABLE,
99 	COL_ENABLED,
100 	COL_ICON,
101 	COL_NAME,
102 	COL_PLUGIN,
103 	N_COLS
104 };
105 
106 /* Remembered plugins page treeview */
107 enum {
108 	COL_REM_ICON,
109 	COL_REM_NAME,
110 	COL_REM_PLUGIN_KEY,
111 	N_REM_COLS
112 };
113 
114 /* Plugin class types */
115 
116 static AnjutaCPluginFactory *anjuta_plugin_factory = NULL;
117 
118 static GObjectClass* parent_class = NULL;
119 static guint plugin_manager_signals[LAST_SIGNAL] = { 0 };
120 
121 static void plugin_set_update (AnjutaPluginManager *plugin_manager,
122                                AnjutaPluginHandle* selected_plugin,
123                                gboolean load);
124 
125 static IAnjutaPluginFactory* get_plugin_factory (AnjutaPluginManager *plugin_manager,
126                                                  const gchar *language, GError **error);
127 
128 GQuark
anjuta_plugin_manager_error_quark(void)129 anjuta_plugin_manager_error_quark (void)
130 {
131 	static GQuark quark = 0;
132 
133 	if (quark == 0) {
134 		quark = g_quark_from_static_string ("anjuta-plugin-manager-quark");
135 	}
136 	return quark;
137 }
138 
139 /* Dependency Resolution */
140 
141 static gboolean
collect_cycle(AnjutaPluginManager * plugin_manager,AnjutaPluginHandle * base_plugin,AnjutaPluginHandle * cur_plugin,GList ** cycle)142 collect_cycle (AnjutaPluginManager *plugin_manager,
143 			   AnjutaPluginHandle *base_plugin, AnjutaPluginHandle *cur_plugin,
144 			   GList **cycle)
145 {
146 	AnjutaPluginManagerPriv *priv;
147 	GList *l;
148 
149 	priv = plugin_manager->priv;
150 
151 	for (l = anjuta_plugin_handle_get_dependency_names (cur_plugin);
152 		 l != NULL; l = l->next)
153 	{
154 		AnjutaPluginHandle *dep = g_hash_table_lookup (priv->plugins_by_name,
155 													   l->data);
156 		if (dep)
157 		{
158 			if (dep == base_plugin)
159 			{
160 				*cycle = g_list_prepend (NULL, dep);
161 				/* DEBUG_PRINT ("%s", anjuta_plugin_handle_get_name (dep)); */
162 				return TRUE;
163 			}
164 			else
165 			{
166 				if (collect_cycle (plugin_manager, base_plugin, dep, cycle))
167 				{
168 					*cycle = g_list_prepend (*cycle, dep);
169 					/* DEBUG_PRINT ("%s", anjuta_plugin_handle_get_name (dep)); */
170 					return TRUE;
171 				}
172 			}
173 		}
174 	}
175 	return FALSE;
176 }
177 
178 static void
add_dependency(AnjutaPluginHandle * dependent,AnjutaPluginHandle * dependency)179 add_dependency (AnjutaPluginHandle *dependent, AnjutaPluginHandle *dependency)
180 {
181 	g_hash_table_insert (anjuta_plugin_handle_get_dependents (dependency),
182 						 dependent, dependency);
183 	g_hash_table_insert (anjuta_plugin_handle_get_dependencies (dependent),
184 						 dependency, dependent);
185 }
186 
187 static void
child_dep_foreach_cb(gpointer key,gpointer value,gpointer user_data)188 child_dep_foreach_cb (gpointer key, gpointer value, gpointer user_data)
189 {
190 	add_dependency (ANJUTA_PLUGIN_HANDLE (user_data),
191 					ANJUTA_PLUGIN_HANDLE (key));
192 }
193 
194 /* Resolves dependencies for a single module recursively.  Shortcuts if
195  * the module has already been resolved.  Returns a list representing
196  * any cycles found, or NULL if no cycles are found.  If a cycle is found,
197  * the graph is left unresolved.
198  */
199 static GList*
resolve_for_module(AnjutaPluginManager * plugin_manager,AnjutaPluginHandle * plugin,int pass)200 resolve_for_module (AnjutaPluginManager *plugin_manager,
201 					AnjutaPluginHandle *plugin, int pass)
202 {
203 	AnjutaPluginManagerPriv *priv;
204 	GList *l;
205 	GList *ret = NULL;
206 
207 	priv = plugin_manager->priv;
208 
209 	if (anjuta_plugin_handle_get_checked (plugin))
210 	{
211 		return NULL;
212 	}
213 
214 	if (anjuta_plugin_handle_get_resolve_pass (plugin) == pass)
215 	{
216 		GList *cycle = NULL;
217 		g_warning ("cycle found: %s on pass %d",
218 				   anjuta_plugin_handle_get_name (plugin),
219 				   anjuta_plugin_handle_get_resolve_pass (plugin));
220 		collect_cycle (plugin_manager, plugin, plugin, &cycle);
221 		return cycle;
222 	}
223 
224 	if (anjuta_plugin_handle_get_resolve_pass (plugin) != -1)
225 	{
226 		return NULL;
227 	}
228 
229 	anjuta_plugin_handle_set_can_load (plugin, TRUE);
230 	anjuta_plugin_handle_set_resolve_pass (plugin, pass);
231 
232 	for (l = anjuta_plugin_handle_get_dependency_names (plugin);
233 		 l != NULL; l = l->next)
234 	{
235 		char *dep = l->data;
236 		AnjutaPluginHandle *child =
237 			g_hash_table_lookup (priv->plugins_by_name, dep);
238 		if (child)
239 		{
240 			ret = resolve_for_module (plugin_manager, child, pass);
241 			if (ret)
242 			{
243 				break;
244 			}
245 
246 			/* Add the dependency's dense dependency list
247 			 * to the current module's dense dependency list */
248 			g_hash_table_foreach (anjuta_plugin_handle_get_dependencies (child),
249 					      child_dep_foreach_cb, plugin);
250 			add_dependency (plugin, child);
251 
252 			/* If the child can't load due to dependency problems,
253 			 * the current module can't either */
254 			anjuta_plugin_handle_set_can_load (plugin,
255 					anjuta_plugin_handle_get_can_load (child));
256 		} else {
257 			g_warning ("Dependency %s not found.\n", dep);
258 			anjuta_plugin_handle_set_can_load (plugin, FALSE);
259 			ret = NULL;
260 		}
261 	}
262 	anjuta_plugin_handle_set_checked (plugin, TRUE);
263 
264 	return ret;
265 }
266 
267 /* Clean up the results of a resolving run */
268 static void
unresolve_dependencies(AnjutaPluginManager * plugin_manager)269 unresolve_dependencies (AnjutaPluginManager *plugin_manager)
270 {
271 	AnjutaPluginManagerPriv *priv;
272 	GList *l;
273 
274 	priv = plugin_manager->priv;
275 
276 	for (l = priv->available_plugins; l != NULL; l = l->next)
277 	{
278 		AnjutaPluginHandle *plugin = l->data;
279 		anjuta_plugin_handle_unresolve_dependencies (plugin);
280 	}
281 }
282 
283 /* done upto here */
284 
285 static void
prune_modules(AnjutaPluginManager * plugin_manager,GList * modules)286 prune_modules (AnjutaPluginManager *plugin_manager, GList *modules)
287 {
288 	AnjutaPluginManagerPriv *priv;
289 	GList *l;
290 
291 	priv = plugin_manager->priv;
292 
293 	for (l = modules; l != NULL; l = l->next) {
294 		AnjutaPluginHandle *plugin = l->data;
295 
296 		g_hash_table_remove (priv->plugins_by_name,
297 							 anjuta_plugin_handle_get_id (plugin));
298 		priv->available_plugins = g_list_remove (priv->available_plugins, plugin);
299 	}
300 }
301 
302 static int
dependency_compare(AnjutaPluginHandle * plugin_a,AnjutaPluginHandle * plugin_b)303 dependency_compare (AnjutaPluginHandle *plugin_a,
304 					AnjutaPluginHandle *plugin_b)
305 {
306 	int a = g_hash_table_size (anjuta_plugin_handle_get_dependencies (plugin_a));
307 	int b = g_hash_table_size (anjuta_plugin_handle_get_dependencies (plugin_b));
308 
309 	return a - b;
310 }
311 
312 /* Resolves the dependencies of the priv->available_plugins list.  When this
313  * function is complete, the following will be true:
314  *
315  * 1) The dependencies and dependents hash tables of the modules will
316  * be filled.
317  *
318  * 2) Cycles in the graph will be removed.
319  *
320  * 3) Modules which cannot be loaded due to failed dependencies will
321  * be marked as such.
322  *
323  * 4) priv->available_plugins will be sorted such that no module depends on a
324  * module after it.
325  *
326  * If a cycle in the graph is found, it is pruned from the tree and
327  * returned as a list stored in the cycles list.
328  */
329 static void
resolve_dependencies(AnjutaPluginManager * plugin_manager,GList ** cycles)330 resolve_dependencies (AnjutaPluginManager *plugin_manager, GList **cycles)
331 {
332 	AnjutaPluginManagerPriv *priv;
333 	GList *cycle = NULL;
334 	GList *l;
335 
336 	priv = plugin_manager->priv;
337 	*cycles = NULL;
338 
339 	/* Try resolving dependencies.  If there is a cycle, prune the
340 	 * cycle and try to resolve again */
341 	do
342 	{
343 		int pass = 1;
344 		cycle = NULL;
345 		for (l = priv->available_plugins; l != NULL && !cycle; l = l->next) {
346 			cycle = resolve_for_module (plugin_manager, l->data, pass++);
347 			cycle = NULL;
348 		}
349 		if (cycle) {
350 			*cycles = g_list_prepend (*cycles, cycle);
351 			prune_modules (plugin_manager, cycle);
352 			unresolve_dependencies (plugin_manager);
353 		}
354 	} while (cycle);
355 
356 	/* Now that there is a fully resolved dependency tree, sort
357 	 * priv->available_plugins to create a valid load order */
358 	priv->available_plugins = g_list_sort (priv->available_plugins,
359 										   (GCompareFunc)dependency_compare);
360 }
361 
362 /* Plugins loading */
363 
364 static gboolean
str_has_suffix(const char * haystack,const char * needle)365 str_has_suffix (const char *haystack, const char *needle)
366 {
367 	const char *h, *n;
368 
369 	if (needle == NULL) {
370 		return TRUE;
371 	}
372 	if (haystack == NULL) {
373 		return needle[0] == '\0';
374 	}
375 
376 	/* Eat one character at a time. */
377 	h = haystack + strlen(haystack);
378 	n = needle + strlen(needle);
379 	do {
380 		if (n == needle) {
381 			return TRUE;
382 		}
383 		if (h == haystack) {
384 			return FALSE;
385 		}
386 	} while (*--h == *--n);
387 	return FALSE;
388 }
389 
390 static void
load_plugin(AnjutaPluginManager * plugin_manager,const gchar * plugin_desc_path)391 load_plugin (AnjutaPluginManager *plugin_manager,
392 			 const gchar *plugin_desc_path)
393 {
394 	AnjutaPluginManagerPriv *priv;
395 	AnjutaPluginHandle *plugin_handle;
396 
397 	g_return_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager));
398 	priv = plugin_manager->priv;
399 
400 	plugin_handle = anjuta_plugin_handle_new (plugin_desc_path);
401 	if (plugin_handle)
402 	{
403 		if (g_hash_table_lookup (priv->plugins_by_name,
404 								 anjuta_plugin_handle_get_id (plugin_handle)))
405 		{
406 			g_object_unref (plugin_handle);
407 		}
408 		else
409 		{
410 			GList *node;
411 			/* Available plugin */
412 			priv->available_plugins = g_list_prepend (priv->available_plugins,
413 													  plugin_handle);
414 			/* Index by id */
415 			g_hash_table_insert (priv->plugins_by_name,
416 								 (gchar *)anjuta_plugin_handle_get_id (plugin_handle),
417 								 plugin_handle);
418 
419 			/* Index by description */
420 			g_hash_table_insert (priv->plugins_by_description,
421 								 anjuta_plugin_handle_get_description (plugin_handle),
422 								 plugin_handle);
423 
424 			/* Index by interfaces exported by this plugin */
425 			node = anjuta_plugin_handle_get_interfaces (plugin_handle);
426 			while (node)
427 			{
428 				GList *objs;
429 				gchar *iface;
430 				GList *obj_node;
431 				gboolean found;
432 
433 				iface = node->data;
434 				objs = (GList*)g_hash_table_lookup (priv->plugins_by_interfaces, iface);
435 
436 				obj_node = objs;
437 				found = FALSE;
438 				while (obj_node)
439 				{
440 					if (obj_node->data == plugin_handle)
441 					{
442 						found = TRUE;
443 						break;
444 					}
445 					obj_node = g_list_next (obj_node);
446 				}
447 				if (!found)
448 				{
449 					g_hash_table_steal (priv->plugins_by_interfaces, iface);
450 					objs = g_list_prepend (objs, plugin_handle);
451 					g_hash_table_insert (priv->plugins_by_interfaces, iface, objs);
452 				}
453 				node = g_list_next (node);
454 			}
455 		}
456 	}
457 	return;
458 }
459 
460 static void
load_plugins_from_directory(AnjutaPluginManager * plugin_manager,const gchar * dirname)461 load_plugins_from_directory (AnjutaPluginManager* plugin_manager,
462 							 const gchar *dirname)
463 {
464 	DIR *dir;
465 	struct dirent *entry;
466 
467 	dir = opendir (dirname);
468 
469 	if (!dir)
470 	{
471 		return;
472 	}
473 
474 	for (entry = readdir (dir); entry != NULL; entry = readdir (dir))
475 	{
476 		if (str_has_suffix (entry->d_name, ".plugin"))
477 		{
478 			gchar *pathname;
479 			pathname = g_strdup_printf ("%s/%s", dirname, entry->d_name);
480 			load_plugin (plugin_manager,pathname);
481 			g_free (pathname);
482 		}
483 	}
484 	closedir (dir);
485 }
486 
487 /* Plugin activation and deactivation */
488 
489 static void
on_plugin_activated(AnjutaPlugin * plugin_object,AnjutaPluginHandle * plugin)490 on_plugin_activated (AnjutaPlugin *plugin_object, AnjutaPluginHandle *plugin)
491 {
492 	AnjutaPluginManager *plugin_manager;
493 	AnjutaPluginManagerPriv *priv;
494 
495 	/* FIXME: Pass plugin_manager directly in signal arguments */
496 	plugin_manager = anjuta_shell_get_plugin_manager (plugin_object->shell, NULL);
497 
498 	g_return_if_fail(plugin_manager != NULL);
499 
500 	priv = plugin_manager->priv;
501 
502 	g_hash_table_insert (priv->activated_plugins, plugin,
503 						 g_object_ref (plugin_object));
504 	g_hash_table_remove (priv->plugins_cache, plugin);
505 
506 	g_signal_emit_by_name (plugin_manager, "plugin-activated",
507 						   plugin,
508 						   plugin_object);
509 }
510 
511 static void
on_plugin_deactivated(AnjutaPlugin * plugin_object,AnjutaPluginHandle * plugin)512 on_plugin_deactivated (AnjutaPlugin *plugin_object, AnjutaPluginHandle *plugin)
513 {
514 	AnjutaPluginManager *plugin_manager;
515 	AnjutaPluginManagerPriv *priv;
516 
517 	/* FIXME: Pass plugin_manager directly in signal arguments */
518 	plugin_manager = anjuta_shell_get_plugin_manager (plugin_object->shell, NULL);
519 
520 	g_return_if_fail (plugin_manager != NULL);
521 
522 	priv = plugin_manager->priv;
523 
524 	g_hash_table_insert (priv->plugins_cache, plugin, g_object_ref (plugin_object));
525 	g_hash_table_remove (priv->activated_plugins, plugin);
526 
527 	g_signal_emit_by_name (plugin_manager, "plugin-deactivated",
528 						   plugin,
529 						   plugin_object);
530 }
531 
532 static AnjutaPlugin*
activate_plugin(AnjutaPluginManager * plugin_manager,AnjutaPluginHandle * handle,GError ** error)533 activate_plugin (AnjutaPluginManager *plugin_manager,
534 				 AnjutaPluginHandle *handle, GError **error)
535 {
536 	AnjutaPluginManagerPriv *priv;
537 	IAnjutaPluginFactory* factory;
538 	AnjutaPlugin *plugin;
539 	const gchar *language;
540 
541 	priv = plugin_manager->priv;
542 
543 	language = anjuta_plugin_handle_get_language (handle);
544 
545 	factory = get_plugin_factory (plugin_manager, language, error);
546 	if (factory == NULL) return NULL;
547 
548 	plugin = ianjuta_plugin_factory_new_plugin (factory, handle, ANJUTA_SHELL (priv->shell), error);
549 
550 	if (plugin == NULL)
551 	{
552 		return NULL;
553 	}
554 	g_signal_connect (plugin, "activated",
555 					  G_CALLBACK (on_plugin_activated), handle);
556 	g_signal_connect (plugin, "deactivated",
557 					  G_CALLBACK (on_plugin_deactivated), handle);
558 
559 	return plugin;
560 }
561 
562 /**
563  * anjuta_plugin_manager_unload_all_plugins:
564  * @plugin_manager: A #AnjutaPluginManager object
565  *
566  * Unload all plugins. Do not take care of the dependencies because all plugins
567  * are unloaded anyway.
568  */
569 void
anjuta_plugin_manager_unload_all_plugins(AnjutaPluginManager * plugin_manager)570 anjuta_plugin_manager_unload_all_plugins (AnjutaPluginManager *plugin_manager)
571 {
572 	AnjutaPluginManagerPriv *priv;
573 
574 	priv = plugin_manager->priv;
575 	if (g_hash_table_size (priv->activated_plugins) > 0 ||
576 		g_hash_table_size (priv->plugins_cache) > 0)
577 	{
578 		if (g_hash_table_size (priv->activated_plugins) > 0)
579 		{
580 			GList *node;
581 			for (node = g_list_last (priv->available_plugins); node; node = g_list_previous (node))
582 			{
583 				AnjutaPluginHandle *selected_plugin = node->data;
584 				AnjutaPlugin *plugin;
585 
586 				plugin = g_hash_table_lookup (priv->activated_plugins, selected_plugin);
587 				if (plugin)
588 				{
589 					DEBUG_PRINT ("Deactivating plugin: %s",
590 					             anjuta_plugin_handle_get_id (selected_plugin));
591 					anjuta_plugin_deactivate (plugin);
592 				}
593 			}
594 			g_hash_table_remove_all (priv->activated_plugins);
595 		}
596 		if (g_hash_table_size (priv->plugins_cache) > 0)
597 		{
598 			GList *node;
599 
600 			for (node = g_list_last (priv->available_plugins); node; node = g_list_previous (node))
601 			{
602 				AnjutaPluginHandle *selected_plugin = node->data;
603 
604 				g_hash_table_remove (priv->plugins_cache, selected_plugin);
605 			}
606 			g_hash_table_remove_all (priv->plugins_cache);
607 		}
608 	}
609 }
610 
611 /* Return true if plugin should be unloaded when plugin_to_unloaded is unloaded.
612  * It can be because plugin is or need plugin_to_unload. */
613 static gboolean
should_unload(GHashTable * activated_plugins,AnjutaPluginHandle * plugin_to_unload,AnjutaPluginHandle * plugin)614 should_unload (GHashTable *activated_plugins, AnjutaPluginHandle *plugin_to_unload,
615 			   AnjutaPluginHandle *plugin)
616 {
617 	GObject *plugin_obj = g_hash_table_lookup (activated_plugins, plugin);
618 
619 	if (!plugin_obj)
620 		return FALSE;
621 
622 	if (plugin_to_unload == plugin)
623 		return TRUE;
624 
625 	gboolean dependent =
626 		GPOINTER_TO_INT (g_hash_table_lookup (anjuta_plugin_handle_get_dependents (plugin_to_unload),
627 											  plugin));
628 	return dependent;
629 }
630 
631 /* Return true if plugin should be loaded when plugin_to_loaded is loaded.
632  * It can be because plugin_to_load is or need plugin. */
633 static gboolean
should_load(GHashTable * activated_plugins,AnjutaPluginHandle * plugin_to_load,AnjutaPluginHandle * plugin)634 should_load (GHashTable *activated_plugins, AnjutaPluginHandle *plugin_to_load,
635 			 AnjutaPluginHandle *plugin)
636 {
637 	GObject *plugin_obj = g_hash_table_lookup (activated_plugins, plugin);
638 
639 	if (plugin_obj)
640 		return FALSE;
641 
642 	if (plugin_to_load == plugin)
643 		return anjuta_plugin_handle_get_can_load (plugin);
644 
645 	gboolean dependency =
646 		GPOINTER_TO_INT (g_hash_table_lookup (anjuta_plugin_handle_get_dependencies (plugin_to_load),
647 						 					  plugin));
648 	return (dependency && anjuta_plugin_handle_get_can_load (plugin));
649 }
650 
651 static AnjutaPluginHandle *
plugin_for_iter(GtkListStore * store,GtkTreeIter * iter)652 plugin_for_iter (GtkListStore *store, GtkTreeIter *iter)
653 {
654 	AnjutaPluginHandle *plugin;
655 
656 	gtk_tree_model_get (GTK_TREE_MODEL (store), iter, COL_PLUGIN, &plugin, -1);
657 	return plugin;
658 }
659 
660 static void
update_enabled(GtkTreeModel * model,GHashTable * activated_plugins)661 update_enabled (GtkTreeModel *model, GHashTable *activated_plugins)
662 {
663 	GtkTreeIter iter;
664 
665 	if (gtk_tree_model_get_iter_first (model, &iter)) {
666 		do {
667 			AnjutaPluginHandle *plugin;
668 			GObject *plugin_obj;
669 			gboolean installed;
670 
671 			plugin = plugin_for_iter(GTK_LIST_STORE(model), &iter);
672 			plugin_obj = g_hash_table_lookup (activated_plugins, plugin);
673 			installed = (plugin_obj != NULL) ? TRUE : FALSE;
674 			gtk_tree_model_get (model, &iter, COL_PLUGIN, &plugin, -1);
675 			gtk_list_store_set (GTK_LIST_STORE (model), &iter,
676 								COL_ENABLED, installed, -1);
677 		} while (gtk_tree_model_iter_next (model, &iter));
678 	}
679 }
680 
681 static void
plugin_set_update(AnjutaPluginManager * plugin_manager,AnjutaPluginHandle * selected_plugin,gboolean load)682 plugin_set_update (AnjutaPluginManager *plugin_manager,
683 				 AnjutaPluginHandle* selected_plugin,
684 				 gboolean load)
685 {
686 	AnjutaPluginManagerPriv *priv;
687 	gboolean loaded;
688 	GList *l;
689 
690 	priv = plugin_manager->priv;
691 
692 	/* Plugins can be loaded or unloaded implicitely because they need or are
693 	 * needed by another plugin so it is possible that we try to load or unload
694 	 * respectively an already loaded or already unloaded plugin. */
695 	loaded = g_hash_table_lookup (priv->activated_plugins, selected_plugin) != NULL;
696 	if ((load && loaded) || (!load && !loaded)) return;
697 
698 	if (priv->status)
699 		anjuta_status_busy_push (priv->status);
700 
701 	if (!load)
702 	{
703 		/* visit priv->available_plugins in reverse order when unloading, so
704 		 * that plugins are unloaded in the right order */
705 		for (l = g_list_last(priv->available_plugins); l != NULL; l = l->prev)
706 		{
707 			AnjutaPluginHandle *plugin = l->data;
708 			if (should_unload (priv->activated_plugins, selected_plugin, plugin))
709 			{
710 				AnjutaPlugin *plugin_obj = ANJUTA_PLUGIN (g_hash_table_lookup (priv->activated_plugins, plugin));
711 				if (!anjuta_plugin_deactivate (plugin_obj))
712 				{
713 					anjuta_util_dialog_info (GTK_WINDOW (priv->shell),
714 								 dgettext (GETTEXT_PACKAGE, "Plugin '%s' does not want to be deactivated"),
715 								 anjuta_plugin_handle_get_name (plugin));
716 				}
717 			}
718 		}
719 	}
720 	else
721 	{
722 		for (l = priv->available_plugins; l != NULL; l = l->next)
723 		{
724 			AnjutaPluginHandle *plugin = l->data;
725 			if (should_load (priv->activated_plugins, selected_plugin, plugin))
726 			{
727 				AnjutaPlugin *plugin_obj;
728 				GError *error = NULL;
729 				plugin_obj = g_hash_table_lookup (priv->plugins_cache, plugin);
730 				if (plugin_obj)
731 					g_object_ref (plugin_obj);
732 				else
733 				{
734 					plugin_obj = activate_plugin (plugin_manager, plugin,
735 												  &error);
736 				}
737 
738 				if (plugin_obj)
739 				{
740 					anjuta_plugin_activate (ANJUTA_PLUGIN (plugin_obj));
741 					g_object_unref (plugin_obj);
742 				}
743 				else
744 				{
745 					if (error)
746 					{
747 						gchar* message = g_strdup_printf (dgettext (GETTEXT_PACKAGE, "Could not load %s\n"
748 							"This usually means that your installation is corrupted. The "
749 							"error message leading to this was:\n%s"),
750 														  anjuta_plugin_handle_get_name (selected_plugin),
751 														  error->message);
752 						anjuta_util_dialog_error (GTK_WINDOW(plugin_manager->priv->shell),
753 												  message);
754 						g_error_free (error);
755 						g_free(message);
756 					}
757 				}
758 			}
759 		}
760 	}
761 	if (priv->status)
762 		anjuta_status_busy_pop (priv->status);
763 
764 	return;
765 }
766 
767 static void
plugin_toggled(GtkCellRendererToggle * cell,char * path_str,gpointer data)768 plugin_toggled (GtkCellRendererToggle *cell, char *path_str, gpointer data)
769 {
770 	AnjutaPluginManager *plugin_manager;
771 	AnjutaPluginManagerPriv *priv;
772 	GtkListStore *store = GTK_LIST_STORE (data);
773 	GtkTreeIter iter;
774 	GtkTreePath *path;
775 	AnjutaPluginHandle *plugin;
776 	gboolean enabled;
777 	GList *activated_plugins;
778 	GList *node;
779 	AnjutaPlugin* plugin_object;
780 
781 	path = gtk_tree_path_new_from_string (path_str);
782 
783 	plugin_manager = g_object_get_data (G_OBJECT (store), "plugin-manager");
784 	priv = plugin_manager->priv;
785 
786 	gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path);
787 	gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
788 			    COL_ENABLED, &enabled,
789 			    COL_PLUGIN, &plugin,
790 			    -1);
791 
792 	/* Activate one plugin can force the loading of other ones, instead of
793 	 * searching which plugins have to be activated, we just unmerge all
794 	 * current plugins and merge all plugins after the modification */
795 
796 	/* unmerge all plugins */
797 	activated_plugins = g_hash_table_get_values (priv->activated_plugins);
798 	for (node = g_list_first (activated_plugins); node != NULL; node = g_list_next (node))
799 	{
800 		plugin_object = (AnjutaPlugin *)node->data;
801 		if (plugin_object &&
802 			IANJUTA_IS_PREFERENCES(plugin_object))
803 		{
804 			ianjuta_preferences_unmerge (IANJUTA_PREFERENCES (plugin_object),
805 									   anjuta_shell_get_preferences (ANJUTA_SHELL (priv->shell), NULL),
806 									   NULL);
807 		}
808 	}
809 	g_list_free (activated_plugins);
810 
811 	plugin_set_update (plugin_manager, plugin, !enabled);
812 
813 	/* Make sure that it appears in the preferences. This method
814 		can only be called when the preferences dialog is active so
815 		it should be save
816 	*/
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))
819 	{
820 		plugin_object = (AnjutaPlugin *)node->data;
821 		if (plugin_object &&
822 			IANJUTA_IS_PREFERENCES(plugin_object))
823 		{
824 			ianjuta_preferences_merge (IANJUTA_PREFERENCES (plugin_object),
825 									   anjuta_shell_get_preferences (ANJUTA_SHELL (priv->shell), NULL),
826 									   NULL);
827 		}
828 	}
829 	g_list_free (activated_plugins);
830 
831 	update_enabled (GTK_TREE_MODEL (store), priv->activated_plugins);
832 	gtk_tree_path_free (path);
833 }
834 
835 #if 0
836 static void
837 selection_changed (GtkTreeSelection *selection, GtkListStore *store)
838 {
839 	GtkTreeIter iter;
840 
841 	if (gtk_tree_selection_get_selected (selection, NULL,
842 					     &iter)) {
843 		GtkTextBuffer *buffer;
844 
845 		GtkWidget *txt = g_object_get_data (G_OBJECT (store),
846 						    "AboutText");
847 
848 		GtkWidget *image = g_object_get_data (G_OBJECT (store),
849 						      "Icon");
850 		AnjutaPluginHandle *plugin = plugin_for_iter (store, &iter);
851 
852 		buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (txt));
853 		gtk_text_buffer_set_text (buffer, plugin->about, -1);
854 
855 		if (plugin->icon_path) {
856 			gtk_image_set_from_file (GTK_IMAGE (image),
857 						 plugin->icon_path);
858 			gtk_widget_show (GTK_WIDGET (image));
859 		} else {
860 			gtk_widget_hide (GTK_WIDGET (image));
861 		}
862 	}
863 }
864 #endif
865 
866 static GtkWidget *
create_plugin_tree(void)867 create_plugin_tree (void)
868 {
869 	GtkListStore *store;
870 	GtkWidget *tree;
871 	GtkCellRenderer *renderer;
872 	GtkTreeViewColumn *column;
873 
874 	store = gtk_list_store_new (N_COLS,
875 								G_TYPE_BOOLEAN,
876 								G_TYPE_BOOLEAN,
877 								GDK_TYPE_PIXBUF,
878 								G_TYPE_STRING,
879 								G_TYPE_POINTER);
880 	tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
881 
882 	renderer = gtk_cell_renderer_toggle_new ();
883 	g_signal_connect (G_OBJECT (renderer), "toggled",
884 			  G_CALLBACK (plugin_toggled), store);
885 	column = gtk_tree_view_column_new_with_attributes (dgettext (GETTEXT_PACKAGE, "Load"),
886 													   renderer,
887 													   "active",
888 													   COL_ENABLED,
889 													   "activatable",
890 													   COL_ACTIVABLE,
891 													   NULL);
892 	gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
893 	gtk_tree_view_column_set_sizing (column,
894 									 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
895 
896 	column = gtk_tree_view_column_new ();
897 	renderer = gtk_cell_renderer_pixbuf_new ();
898 	gtk_tree_view_column_pack_start (column, renderer, FALSE);
899 	gtk_tree_view_column_add_attribute (column, renderer, "pixbuf",
900 										COL_ICON);
901 	renderer = gtk_cell_renderer_text_new ();
902 	g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
903 	gtk_tree_view_column_pack_start (column, renderer, TRUE);
904 	gtk_tree_view_column_add_attribute (column, renderer, "markup",
905 										COL_NAME);
906 	gtk_tree_view_column_set_sizing (column,
907 									 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
908 	gtk_tree_view_column_set_title (column, dgettext (GETTEXT_PACKAGE, "Available Plugins"));
909 	gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
910 	gtk_tree_view_set_expander_column (GTK_TREE_VIEW (tree), column);
911 
912 	g_object_unref (store);
913 	return tree;
914 }
915 
916 /* Sort function for plugins */
917 static gint
sort_plugins(gconstpointer a,gconstpointer b)918 sort_plugins(gconstpointer a, gconstpointer b)
919 {
920 	g_return_val_if_fail (a != NULL, 0);
921 	g_return_val_if_fail (b != NULL, 0);
922 
923 	AnjutaPluginHandle* plugin_a = ANJUTA_PLUGIN_HANDLE (a);
924 	AnjutaPluginHandle* plugin_b = ANJUTA_PLUGIN_HANDLE (b);
925 
926 	return strcmp (anjuta_plugin_handle_get_name (plugin_a),
927 				   anjuta_plugin_handle_get_name (plugin_b));
928 }
929 
930 /* If show_all == FALSE, show only user activatable plugins
931  * If show_all == TRUE, show all plugins
932  */
933 static void
populate_plugin_model(AnjutaPluginManager * plugin_manager,GtkListStore * store,GHashTable * plugins_to_show,GHashTable * activated_plugins,gboolean show_all)934 populate_plugin_model (AnjutaPluginManager *plugin_manager,
935 					   GtkListStore *store,
936 					   GHashTable *plugins_to_show,
937 					   GHashTable *activated_plugins,
938 					   gboolean show_all)
939 {
940 	AnjutaPluginManagerPriv *priv;
941 	GList *sorted_plugins, *l;
942 
943 	priv = plugin_manager->priv;
944 	gtk_list_store_clear (store);
945 
946 	sorted_plugins = g_list_copy (priv->available_plugins);
947 	sorted_plugins = g_list_sort (sorted_plugins, sort_plugins);
948 
949 	for (l = sorted_plugins; l != NULL; l = l->next)
950 	{
951 		AnjutaPluginHandle *plugin = l->data;
952 
953 		/* If plugins to show is NULL, show all available plugins */
954 		if (plugins_to_show == NULL ||
955 			g_hash_table_lookup (plugins_to_show, plugin))
956 		{
957 
958 			gboolean enable = FALSE;
959 			if (g_hash_table_lookup (activated_plugins, plugin))
960 				enable = TRUE;
961 
962 			if (anjuta_plugin_handle_get_name (plugin) &&
963 			    anjuta_plugin_handle_get_description (plugin) &&
964 			    (anjuta_plugin_handle_get_user_activatable (plugin) ||
965 			     show_all) &&
966 			    (g_hash_table_lookup (plugin_manager->priv->disable_plugins, plugin) == NULL))
967 			{
968 				GtkTreeIter iter;
969 				gchar *text;
970 
971 				text = g_markup_printf_escaped ("<span size=\"larger\" weight=\"bold\">%s</span>\n%s",
972 												anjuta_plugin_handle_get_name (plugin),
973 												anjuta_plugin_handle_get_about (plugin));
974 
975 				gtk_list_store_append (store, &iter);
976 				gtk_list_store_set (store, &iter,
977 									COL_ACTIVABLE,
978 									anjuta_plugin_handle_get_user_activatable (plugin),
979 									COL_ENABLED, enable,
980 									COL_NAME, text,
981 									COL_PLUGIN, plugin,
982 									-1);
983 				if (anjuta_plugin_handle_get_icon_path (plugin))
984 				{
985 					GdkPixbuf *icon;
986 					icon = gdk_pixbuf_new_from_file_at_size (anjuta_plugin_handle_get_icon_path (plugin),
987 															 32, 32, NULL);
988 					if (icon) {
989 						gtk_list_store_set (store, &iter,
990 											COL_ICON, icon, -1);
991 						g_object_unref (icon);
992 					}
993 				}
994 				g_free (text);
995 			}
996 		}
997 	}
998 
999 	g_list_free (sorted_plugins);
1000 }
1001 
1002 static GtkWidget *
create_remembered_plugins_tree(void)1003 create_remembered_plugins_tree (void)
1004 {
1005 	GtkListStore *store;
1006 	GtkWidget *tree;
1007 	GtkCellRenderer *renderer;
1008 	GtkTreeViewColumn *column;
1009 
1010 	store = gtk_list_store_new (N_REM_COLS, GDK_TYPE_PIXBUF, G_TYPE_STRING,
1011 								G_TYPE_STRING);
1012 	tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
1013 
1014 	column = gtk_tree_view_column_new ();
1015 	renderer = gtk_cell_renderer_pixbuf_new ();
1016 	gtk_tree_view_column_pack_start (column, renderer, FALSE);
1017 	gtk_tree_view_column_add_attribute (column, renderer, "pixbuf",
1018 										COL_REM_ICON);
1019 	renderer = gtk_cell_renderer_text_new ();
1020 	gtk_tree_view_column_pack_start (column, renderer, FALSE);
1021 	gtk_tree_view_column_add_attribute (column, renderer, "markup",
1022 										COL_REM_NAME);
1023 	gtk_tree_view_column_set_sizing (column,
1024 									 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1025 	gtk_tree_view_column_set_title (column, dgettext (GETTEXT_PACKAGE, "Preferred plugins"));
1026 	gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
1027 	gtk_tree_view_set_expander_column (GTK_TREE_VIEW (tree), column);
1028 
1029 	g_object_unref (store);
1030 	return tree;
1031 }
1032 
1033 static void
foreach_remembered_plugin(gpointer key,gpointer value,gpointer user_data)1034 foreach_remembered_plugin (gpointer key, gpointer value, gpointer user_data)
1035 {
1036 	AnjutaPluginHandle *handle = (AnjutaPluginHandle *) value;
1037 	GtkListStore *store = GTK_LIST_STORE (user_data);
1038 
1039 	if (anjuta_plugin_handle_get_name (handle) &&
1040 		anjuta_plugin_handle_get_description (handle))
1041 	{
1042 		GtkTreeIter iter;
1043 		gchar *text;
1044 
1045 		text = g_markup_printf_escaped ("<span size=\"larger\" weight=\"bold\">%s</span>\n%s",
1046 										anjuta_plugin_handle_get_name (handle),
1047 										anjuta_plugin_handle_get_about (handle));
1048 
1049 		gtk_list_store_append (store, &iter);
1050 		gtk_list_store_set (store, &iter,
1051 							COL_REM_NAME, text,
1052 							COL_REM_PLUGIN_KEY, key,
1053 							-1);
1054 		if (anjuta_plugin_handle_get_icon_path (handle))
1055 		{
1056 			GdkPixbuf *icon;
1057 			icon = gdk_pixbuf_new_from_file_at_size (anjuta_plugin_handle_get_icon_path (handle),
1058 													 32, 32, NULL);
1059 			if (icon) {
1060 				gtk_list_store_set (store, &iter,
1061 									COL_REM_ICON, icon, -1);
1062 				g_object_unref (icon);
1063 			}
1064 		}
1065 		g_free (text);
1066 	}
1067 }
1068 
1069 static void
populate_remembered_plugins_model(AnjutaPluginManager * plugin_manager,GtkListStore * store)1070 populate_remembered_plugins_model (AnjutaPluginManager *plugin_manager,
1071 								   GtkListStore *store)
1072 {
1073 	AnjutaPluginManagerPriv *priv = plugin_manager->priv;
1074 	gtk_list_store_clear (store);
1075 	g_hash_table_foreach (priv->remember_plugins, foreach_remembered_plugin,
1076 						  store);
1077 }
1078 
1079 static void
on_show_all_plugins_toggled(GtkToggleButton * button,GtkListStore * store)1080 on_show_all_plugins_toggled (GtkToggleButton *button, GtkListStore *store)
1081 {
1082 	AnjutaPluginManager *plugin_manager;
1083 
1084 	plugin_manager = g_object_get_data (G_OBJECT (button), "__plugin_manager");
1085 
1086 	populate_plugin_model (plugin_manager, store, NULL,
1087 						   plugin_manager->priv->activated_plugins,
1088 						   !gtk_toggle_button_get_active (button));
1089 }
1090 
1091 static void
on_forget_plugin_clicked(GtkWidget * button,GtkTreeView * view)1092 on_forget_plugin_clicked (GtkWidget *button, GtkTreeView *view)
1093 {
1094 	GtkTreeIter iter;
1095 	GtkTreeModel *model;
1096 	GtkTreeSelection *selection = gtk_tree_view_get_selection (view);
1097 	if (gtk_tree_selection_get_selected (selection, &model, &iter))
1098 	{
1099 		gchar *plugin_key;
1100 		AnjutaPluginManager *manager = g_object_get_data (G_OBJECT (model),
1101 														  "plugin-manager");
1102 		gtk_tree_model_get (model, &iter, COL_REM_PLUGIN_KEY, &plugin_key, -1);
1103 		g_hash_table_remove (manager->priv->remember_plugins, plugin_key);
1104 		gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
1105 		g_free (plugin_key);
1106 	}
1107 }
1108 
1109 static void
on_forget_plugin_sel_changed(GtkTreeSelection * selection,GtkWidget * button)1110 on_forget_plugin_sel_changed (GtkTreeSelection *selection,
1111 							  GtkWidget *button)
1112 {
1113 	GtkTreeIter iter;
1114 
1115 	if (gtk_tree_selection_get_selected (selection, NULL, &iter))
1116 		gtk_widget_set_sensitive (button, TRUE);
1117 	else
1118 		gtk_widget_set_sensitive (button, FALSE);
1119 }
1120 
1121 GtkWidget *
anjuta_plugin_manager_get_plugins_page(AnjutaPluginManager * plugin_manager)1122 anjuta_plugin_manager_get_plugins_page (AnjutaPluginManager *plugin_manager)
1123 {
1124 	GtkWidget *vbox;
1125 	GtkWidget *checkbutton;
1126 	GtkWidget *tree;
1127 	GtkWidget *scrolled;
1128 	GtkWidget *toolbar;
1129 	GtkToolItem *toolitem;
1130 	GtkListStore *store;
1131 
1132 	/* Plugins page */
1133 	vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1134 	gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
1135 
1136 	scrolled = gtk_scrolled_window_new (NULL, NULL);
1137 	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
1138 									     GTK_SHADOW_IN);
1139 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
1140 									GTK_POLICY_NEVER,
1141 									GTK_POLICY_AUTOMATIC);
1142 	gtk_style_context_set_junction_sides (gtk_widget_get_style_context (scrolled), GTK_JUNCTION_BOTTOM);
1143 	gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 0);
1144 
1145 	toolbar = gtk_toolbar_new ();
1146 	gtk_style_context_add_class (gtk_widget_get_style_context (toolbar), GTK_STYLE_CLASS_INLINE_TOOLBAR);
1147 	gtk_style_context_set_junction_sides (gtk_widget_get_style_context (toolbar), GTK_JUNCTION_TOP);
1148 	gtk_box_pack_start (GTK_BOX (vbox), toolbar, FALSE, FALSE, 0);
1149 	gtk_widget_show (toolbar);
1150 
1151 	toolitem = gtk_tool_item_new ();
1152 	gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (toolitem), 0);
1153 	gtk_widget_show (GTK_WIDGET(toolitem));
1154 
1155 	checkbutton = gtk_check_button_new_with_label (dgettext (GETTEXT_PACKAGE, "Only show user activatable plugins"));
1156 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), TRUE);
1157 	gtk_container_add (GTK_CONTAINER (toolitem), checkbutton);
1158 
1159 	tree = create_plugin_tree ();
1160 	gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (tree), TRUE);
1161 	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree), FALSE);
1162 	store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (tree)));
1163 
1164 	populate_plugin_model (plugin_manager, store, NULL,
1165 						   plugin_manager->priv->activated_plugins, FALSE);
1166 
1167 	gtk_container_add (GTK_CONTAINER (scrolled), tree);
1168 	g_object_set_data (G_OBJECT (store), "plugin-manager", plugin_manager);
1169 
1170 
1171 	g_object_set_data (G_OBJECT (checkbutton), "__plugin_manager", plugin_manager);
1172 	g_signal_connect (G_OBJECT (checkbutton), "toggled",
1173 					  G_CALLBACK (on_show_all_plugins_toggled),
1174 					  store);
1175 	gtk_widget_show_all (vbox);
1176 	return vbox;
1177 }
1178 
1179 GtkWidget *
anjuta_plugin_manager_get_remembered_plugins_page(AnjutaPluginManager * plugin_manager)1180 anjuta_plugin_manager_get_remembered_plugins_page (AnjutaPluginManager *plugin_manager)
1181 {
1182 	GtkWidget *vbox;
1183 	GtkWidget *tree;
1184 	GtkWidget *scrolled;
1185 	GtkListStore *store;
1186 	GtkWidget *hbox;
1187 	GtkWidget *display_label;
1188 	GtkWidget *forget_button;
1189 	GtkTreeSelection *selection;
1190 
1191 	/* Remembered plugin */
1192 	vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10);
1193 	gtk_container_set_border_width (GTK_CONTAINER (vbox), 10);
1194 
1195 	display_label = gtk_label_new (dgettext (GETTEXT_PACKAGE, "These are the plugins selected by you "
1196 									 "when you have been prompted to choose one of "
1197 									 "many suitable plugins. Removing the "
1198 									 "preferred plugin will let you "
1199 									 "choose a different plugin."));
1200 	gtk_label_set_line_wrap (GTK_LABEL (display_label), TRUE);
1201 	gtk_box_pack_start (GTK_BOX (vbox), display_label, FALSE, FALSE, 0);
1202 
1203 	scrolled = gtk_scrolled_window_new (NULL, NULL);
1204 	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
1205 									     GTK_SHADOW_IN);
1206 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
1207 									GTK_POLICY_AUTOMATIC,
1208 									GTK_POLICY_AUTOMATIC);
1209 	gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 0);
1210 
1211 	tree = create_remembered_plugins_tree ();
1212 	store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (tree)));
1213 
1214 	gtk_container_add (GTK_CONTAINER (scrolled), tree);
1215 	g_object_set_data (G_OBJECT (store), "plugin-manager", plugin_manager);
1216 	populate_remembered_plugins_model (plugin_manager, store);
1217 
1218 	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1219 	gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
1220 	gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
1221 	forget_button = gtk_button_new_with_label (dgettext (GETTEXT_PACKAGE, "Forget selected plugin"));
1222 	gtk_widget_set_sensitive (forget_button, FALSE);
1223 	gtk_box_pack_end (GTK_BOX (hbox), forget_button, FALSE, FALSE, 0);
1224 
1225 	g_signal_connect (forget_button, "clicked",
1226 					  G_CALLBACK (on_forget_plugin_clicked),
1227 					  tree);
1228 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree));
1229 	g_signal_connect (selection, "changed",
1230 					  G_CALLBACK (on_forget_plugin_sel_changed),
1231 					  forget_button);
1232 	gtk_widget_show_all (vbox);
1233 	return vbox;
1234 }
1235 
1236 static GList *
property_to_list(const char * value)1237 property_to_list (const char *value)
1238 {
1239 	GList *l = NULL;
1240 	char **split_str;
1241 	char **p;
1242 
1243 	split_str = g_strsplit (value, ",", -1);
1244 	for (p = split_str; *p != NULL; p++) {
1245 		l = g_list_prepend (l, g_strdup (g_strstrip (*p)));
1246 	}
1247 	g_strfreev (split_str);
1248 	return l;
1249 }
1250 
1251 static IAnjutaPluginFactory*
get_plugin_factory(AnjutaPluginManager * plugin_manager,const gchar * language,GError ** error)1252 get_plugin_factory (AnjutaPluginManager *plugin_manager,
1253 								 	const gchar *language,
1254 									GError **error)
1255 {
1256 	AnjutaPluginManagerPriv *priv;
1257 	AnjutaPluginHandle *plugin;
1258 	GList *loader_plugins, *node;
1259 	GList *valid_plugins;
1260 	GObject *obj = NULL;
1261 
1262 	g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), G_TYPE_INVALID);
1263 
1264 
1265 	if ((language == NULL) || (g_ascii_strcasecmp (language, "C") == 0))
1266 	{
1267 		/* Support of C plugin is built-in */
1268 		return IANJUTA_PLUGIN_FACTORY (anjuta_plugin_factory);
1269 	}
1270 
1271 	priv = plugin_manager->priv;
1272 	plugin = NULL;
1273 
1274 	/* Find all plugins implementing the IAnjutaPluginLoader interface. */
1275 	loader_plugins = g_hash_table_lookup (priv->plugins_by_interfaces, "IAnjutaPluginLoader");
1276 
1277 	/* Create a list of loader supporting this language */
1278 	node = loader_plugins;
1279 	valid_plugins = NULL;
1280 	while (node)
1281 	{
1282 		AnjutaPluginDescription *desc;
1283 		gchar *val;
1284 		GList *vals = NULL;
1285 		GList *l_node;
1286 		gboolean found;
1287 
1288 		plugin = node->data;
1289 
1290 		desc = anjuta_plugin_handle_get_description (plugin);
1291 		if (anjuta_plugin_description_get_string (desc, "Plugin Loader", "SupportedLanguage", &val))
1292 		{
1293 			if (val != NULL)
1294 			{
1295 				vals = property_to_list (val);
1296 				g_free (val);
1297 			}
1298 		}
1299 
1300 		found = FALSE;
1301 		l_node = vals;
1302 		while (l_node)
1303 		{
1304 			if (!found && (g_ascii_strcasecmp (l_node->data, language) == 0))
1305 			{
1306 				found = TRUE;
1307 			}
1308 			g_free (l_node->data);
1309 			l_node = g_list_next (l_node);
1310 		}
1311 		g_list_free (vals);
1312 
1313 		if (found)
1314 		{
1315 			valid_plugins = g_list_prepend (valid_plugins, plugin);
1316 		}
1317 
1318 		node = g_list_next (node);
1319 	}
1320 
1321 	/* Find the first installed plugin from the valid plugins */
1322 	node = valid_plugins;
1323 	while (node)
1324 	{
1325 		plugin = node->data;
1326 		obj = g_hash_table_lookup (priv->activated_plugins, plugin);
1327 		if (obj) break;
1328 		node = g_list_next (node);
1329 	}
1330 
1331 	/* If no plugin is installed yet, do something */
1332 	if ((obj == NULL) && valid_plugins && g_list_length (valid_plugins) == 1)
1333 	{
1334 		/* If there is just one plugin, consider it selected */
1335 		plugin = valid_plugins->data;
1336 
1337 		/* Install and return it */
1338 		plugin_set_update (plugin_manager, plugin, TRUE);
1339 		obj = g_hash_table_lookup (priv->activated_plugins, plugin);
1340 	}
1341 	else if ((obj == NULL) && valid_plugins)
1342 	{
1343 		/* Prompt the user to select one of these plugins */
1344 
1345 		GList *handles = NULL;
1346 		node = valid_plugins;
1347 		while (node)
1348 		{
1349 			plugin = node->data;
1350 			handles = g_list_prepend (handles, plugin);
1351 			node = g_list_next (node);
1352 		}
1353 		handles = g_list_reverse (handles);
1354 		obj = anjuta_plugin_manager_select_and_activate (plugin_manager,
1355 								  dgettext (GETTEXT_PACKAGE, "Select a plugin"),
1356 								  dgettext (GETTEXT_PACKAGE, "Please select a plugin to activate"),
1357 								  handles);
1358 		g_list_free (handles);
1359 	}
1360 	g_list_free (valid_plugins);
1361 
1362 	if (obj != NULL)
1363 	{
1364 		return IANJUTA_PLUGIN_FACTORY (obj);
1365 	}
1366 
1367 	/* No plugin implementing this interface found */
1368 	g_set_error (error, ANJUTA_PLUGIN_MANAGER_ERROR,
1369 					 ANJUTA_PLUGIN_MANAGER_MISSING_FACTORY,
1370 					 dgettext (GETTEXT_PACKAGE, "No plugin is able to load other plugins in %s"), language);
1371 
1372 	return NULL;
1373 }
1374 
1375 static void
on_is_active_plugins_foreach(gpointer key,gpointer data,gpointer user_data)1376 on_is_active_plugins_foreach (gpointer key, gpointer data, gpointer user_data)
1377 {
1378 	AnjutaPluginHandle *handle = ANJUTA_PLUGIN_HANDLE (key);
1379 	gchar const **search_iface = (gchar const **)user_data;
1380 
1381 	if (*search_iface != NULL)
1382 	{
1383 		GList *interfaces;
1384 		GList *found;
1385 
1386  		interfaces = anjuta_plugin_handle_get_interfaces (handle);
1387 
1388 		for (found = g_list_first (interfaces); found != NULL; found = g_list_next (found))
1389 		{
1390 		}
1391 
1392 		found = g_list_find_custom (interfaces, *search_iface, (GCompareFunc)strcmp);
1393 
1394 		if (found != NULL) *search_iface = NULL;
1395 	}
1396 }
1397 
1398 /**
1399  * anjuta_plugin_manager_is_active_plugin:
1400  * @plugin_manager: A #AnjutaPluginManager object
1401  * @iface_name: The interface implemented by the object to be found
1402  *
1403  * Searches if a currently loaded plugins implements
1404  * the given interface.
1405  *
1406  * Return value: %TRUE is the plugin is currently loaded.
1407  */
1408 
1409 gboolean
anjuta_plugin_manager_is_active_plugin(AnjutaPluginManager * plugin_manager,const gchar * iface_name)1410 anjuta_plugin_manager_is_active_plugin (AnjutaPluginManager *plugin_manager,
1411 								  const gchar *iface_name)
1412 {
1413 	const gchar *search_iface = iface_name;
1414 
1415 	g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), FALSE);
1416 
1417 	g_hash_table_foreach (plugin_manager->priv->activated_plugins,
1418 						  on_is_active_plugins_foreach,
1419 						  &search_iface);
1420 
1421 	return search_iface == NULL;
1422 }
1423 
1424 /**
1425  * anjuta_plugin_manager_get_plugin:
1426  * @plugin_manager: A #AnjutaPluginManager object
1427  * @iface_name: The interface implemented by the object to be found
1428  *
1429  * Searches the currently available plugins to find the one which
1430  * implements the given interface as primary interface and returns it. If
1431  * the plugin is not yet loaded, it will be loaded and activated.
1432  * It only searches
1433  * from the pool of plugin objects loaded in this shell and can only search
1434  * by primary interface. If there are more objects implementing this primary
1435  * interface, user might be prompted to select one from them (and might give
1436  * the option to use it as default for future queries). A typical usage of this
1437  * function is:
1438  * <programlisting>
1439  * GObject *docman =
1440  *     anjuta_plugin_manager_get_plugin (plugin_manager, "IAnjutaDocumentManager", error);
1441  * </programlisting>
1442  * Notice that this function takes the interface name string as string, unlike
1443  * anjuta_plugins_get_interface() which takes the type directly.
1444  * If no plugin implementing this interface can be found, returns %NULL.
1445  *
1446  * Return value: The plugin object (subclass of #AnjutaPlugin) which implements
1447  * the given interface or %NULL. See #AnjutaPlugin for more detail on interfaces
1448  * implemented by plugins.
1449  */
1450 GObject *
anjuta_plugin_manager_get_plugin(AnjutaPluginManager * plugin_manager,const gchar * iface_name)1451 anjuta_plugin_manager_get_plugin (AnjutaPluginManager *plugin_manager,
1452 								  const gchar *iface_name)
1453 {
1454 	AnjutaPluginManagerPriv *priv;
1455 	AnjutaPluginHandle *plugin;
1456 	GList *valid_plugins, *node;
1457 
1458 	g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
1459 	g_return_val_if_fail (iface_name != NULL, NULL);
1460 
1461 	priv = plugin_manager->priv;
1462 	plugin = NULL;
1463 
1464 	/* Find all plugins implementing this (primary) interface. */
1465 	valid_plugins = g_hash_table_lookup (priv->plugins_by_interfaces, iface_name);
1466 
1467 	/* Find the first installed plugin from the valid plugins */
1468 	node = valid_plugins;
1469 	while (node)
1470 	{
1471 		GObject *obj;
1472 		plugin = node->data;
1473 		obj = g_hash_table_lookup (priv->activated_plugins, plugin);
1474 		if (obj)
1475 			return obj;
1476 		node = g_list_next (node);
1477 	}
1478 
1479 	/* Filter disable plugins */
1480 	valid_plugins = g_list_copy (valid_plugins);
1481 	node = valid_plugins;
1482 	while (node)
1483 	{
1484 		GList *next = g_list_next (node);
1485 
1486 		if ((g_hash_table_lookup (priv->disable_plugins, node->data) != NULL) &&
1487 		    (g_hash_table_lookup (priv->activated_plugins, node->data) == NULL))
1488 
1489 		{
1490 			valid_plugins = g_list_delete_link (valid_plugins, node);
1491 		}
1492 		node = next;
1493 	}
1494 
1495 	/* If no plugin is installed yet, do something */
1496 	if (valid_plugins &&
1497 	    (g_list_length (valid_plugins) == 1))
1498 	{
1499 		/* If there is just one plugin, consider it selected */
1500 		GObject *obj;
1501 		plugin = valid_plugins->data;
1502 		g_list_free (valid_plugins);
1503 
1504 		/* Install and return it */
1505 		plugin_set_update (plugin_manager, plugin, TRUE);
1506 		obj = g_hash_table_lookup (priv->activated_plugins, plugin);
1507 
1508 		return obj;
1509 	}
1510 	else if (valid_plugins)
1511 	{
1512 		/* Prompt the user to select one of these plugins */
1513 		GObject *obj;
1514 		obj = anjuta_plugin_manager_select_and_activate (plugin_manager,
1515 									  dgettext (GETTEXT_PACKAGE, "Select a plugin"),
1516 									  dgettext (GETTEXT_PACKAGE, "<b>Please select a plugin to activate</b>"),
1517 									  valid_plugins);
1518 		g_list_free (valid_plugins);
1519 		return obj;
1520 	}
1521 
1522 	/* No plugin implementing this interface found */
1523 	return NULL;
1524 }
1525 
1526 /**
1527  * anjuta_plugin_manager_get_plugin_by_handle:
1528  * @plugin_manager: A #AnjutaPluginManager object
1529  * @handle: A #AnjutaPluginHandle
1530  *
1531  * Searches the currently available plugins to find the one with the
1532  * specified handle. If the plugin is not yet loaded, it will be loaded
1533  * and activated.
1534  *
1535  * Return value: The plugin object (subclass of #AnjutaPlugin)
1536  */
1537 GObject *
anjuta_plugin_manager_get_plugin_by_handle(AnjutaPluginManager * plugin_manager,AnjutaPluginHandle * handle)1538 anjuta_plugin_manager_get_plugin_by_handle (AnjutaPluginManager *plugin_manager,
1539                                             AnjutaPluginHandle *handle)
1540 {
1541 	AnjutaPluginManagerPriv *priv;
1542 	GObject *obj;
1543 
1544 	g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
1545 	g_return_val_if_fail (handle != NULL, NULL);
1546 
1547 	priv = plugin_manager->priv;
1548 	obj = g_hash_table_lookup (priv->activated_plugins, handle);
1549 	if (obj == NULL)
1550 	{
1551 		plugin_set_update (plugin_manager, handle, TRUE);
1552 		obj = g_hash_table_lookup (priv->activated_plugins, handle);
1553 	}
1554 
1555 	return obj;
1556 }
1557 
1558 static void
on_activated_plugins_foreach(gpointer key,gpointer data,gpointer user_data)1559 on_activated_plugins_foreach (gpointer key, gpointer data, gpointer user_data)
1560 {
1561 	AnjutaPluginHandle *plugin = ANJUTA_PLUGIN_HANDLE (key);
1562 	GList **active_plugins = (GList **)user_data;
1563 	*active_plugins = g_list_prepend (*active_plugins,
1564 						plugin);
1565 }
1566 
1567 static void
on_activated_plugin_objects_foreach(gpointer key,gpointer data,gpointer user_data)1568 on_activated_plugin_objects_foreach (gpointer key, gpointer data, gpointer user_data)
1569 {
1570 	GList **active_plugins = (GList **)user_data;
1571 	*active_plugins = g_list_prepend (*active_plugins,
1572 						data);
1573 }
1574 
1575 GList*
anjuta_plugin_manager_get_active_plugins(AnjutaPluginManager * plugin_manager)1576 anjuta_plugin_manager_get_active_plugins (AnjutaPluginManager *plugin_manager)
1577 {
1578 	GList *active_plugins = NULL;
1579 
1580 	g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
1581 	g_hash_table_foreach (plugin_manager->priv->activated_plugins,
1582 						  on_activated_plugins_foreach,
1583 						  &active_plugins);
1584 	return g_list_reverse (active_plugins);
1585 }
1586 
1587 GList*
anjuta_plugin_manager_get_active_plugin_objects(AnjutaPluginManager * plugin_manager)1588 anjuta_plugin_manager_get_active_plugin_objects (AnjutaPluginManager *plugin_manager)
1589 {
1590 	GList *active_plugins = NULL;
1591 
1592 	g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
1593 	g_hash_table_foreach (plugin_manager->priv->activated_plugins,
1594 						  on_activated_plugin_objects_foreach,
1595 						  &active_plugins);
1596 	return g_list_reverse (active_plugins);
1597 }
1598 
1599 /**
1600  * anjuta_plugin_manager_unload_plugin_by_handle:
1601  * @plugin_manager: A #AnjutaPluginManager object
1602  * @handle: A #AnjutaPluginHandle
1603  *
1604  * Unload the plugin corresponding to the given handle. If the plugin is
1605  * already unloaded, nothing will be done.
1606  *
1607  * Return value: %TRUE is the plugin is unloaded. %FALSE if a corresponding
1608  * plugin does not exist or if the plugin cannot be unloaded.
1609  */
1610 gboolean
anjuta_plugin_manager_unload_plugin_by_handle(AnjutaPluginManager * plugin_manager,AnjutaPluginHandle * handle)1611 anjuta_plugin_manager_unload_plugin_by_handle (AnjutaPluginManager *plugin_manager,
1612                                                AnjutaPluginHandle *handle)
1613 {
1614 	AnjutaPluginManagerPriv *priv;
1615 
1616 	g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), FALSE);
1617 	g_return_val_if_fail (handle != NULL, FALSE);
1618 
1619 	priv = plugin_manager->priv;
1620 	plugin_set_update (plugin_manager, handle, FALSE);
1621 
1622 	/* Check if the plugin has been indeed unloaded */
1623 	return g_hash_table_lookup (priv->activated_plugins, handle) == NULL;
1624 }
1625 
1626 static gboolean
find_plugin_for_object(gpointer key,gpointer value,gpointer data)1627 find_plugin_for_object (gpointer key, gpointer value, gpointer data)
1628 {
1629 	if (value == data)
1630 	{
1631 		g_object_set_data (G_OBJECT (data), "__plugin_plugin", key);
1632 		return TRUE;
1633 	}
1634 	return FALSE;
1635 }
1636 
1637 /**
1638  * anjuta_plugin_manager_unload_plugin:
1639  * @plugin_manager: A #AnjutaPluginManager object
1640  * @plugin_object: A #AnjutaPlugin object
1641  *
1642  * Unload the corresponding plugin. The plugin has to be loaded.
1643  *
1644  * Return value: %TRUE if the plugin has been unloaded. %FALSE if the plugin is
1645  * already or cannot be unloaded.
1646  */
1647 gboolean
anjuta_plugin_manager_unload_plugin(AnjutaPluginManager * plugin_manager,GObject * plugin_object)1648 anjuta_plugin_manager_unload_plugin (AnjutaPluginManager *plugin_manager,
1649 									 GObject *plugin_object)
1650 {
1651 	AnjutaPluginManagerPriv *priv;
1652 	AnjutaPluginHandle *plugin;
1653 
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);
1656 
1657 	priv = plugin_manager->priv;
1658 
1659 	plugin = NULL;
1660 
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");
1665 
1666 	if (plugin)
1667 	{
1668 		plugin_set_update (plugin_manager, plugin, FALSE);
1669 
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;
1675 	}
1676 	g_warning ("No plugin found with object \"%p\".", plugin_object);
1677 	return FALSE;
1678 }
1679 
1680 GList*
anjuta_plugin_manager_list_query(AnjutaPluginManager * plugin_manager,GList * secs,GList * anames,GList * avalues)1681 anjuta_plugin_manager_list_query (AnjutaPluginManager *plugin_manager,
1682 							 GList *secs,
1683 							 GList *anames,
1684 							 GList *avalues)
1685 {
1686 	AnjutaPluginManagerPriv *priv;
1687 	GList *selected_plugins = NULL;
1688 	const gchar *sec;
1689 	const gchar *aname;
1690 	const gchar *avalue;
1691 	GList *available;
1692 
1693 	g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
1694 
1695 	priv = plugin_manager->priv;
1696 	available = priv->available_plugins;
1697 
1698 	if (secs == NULL)
1699 	{
1700 		/* If no query is given, select all plugins */
1701 		while (available)
1702 		{
1703 			AnjutaPluginHandle *plugin = available->data;
1704 			if ((g_hash_table_lookup (plugin_manager->priv->disable_plugins, plugin) == NULL) ||
1705 			    (g_hash_table_lookup (plugin_manager->priv->activated_plugins, plugin) != NULL))
1706 				selected_plugins = g_list_prepend (selected_plugins, plugin);
1707 			available = g_list_next (available);
1708 		}
1709 		return g_list_reverse (selected_plugins);
1710 	}
1711 
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);
1715 
1716 	for (;available; available = g_list_next (available))
1717 	{
1718 		GList* s_node = secs;
1719 		GList* n_node = anames;
1720 		GList* v_node = avalues;
1721 
1722 		gboolean satisfied = FALSE;
1723 
1724 		AnjutaPluginHandle *plugin = available->data;
1725 		AnjutaPluginDescription *desc =
1726 			anjuta_plugin_handle_get_description (plugin);
1727 
1728 		if ((g_hash_table_lookup (plugin_manager->priv->disable_plugins, plugin) != NULL) &&
1729 		    (g_hash_table_lookup (plugin_manager->priv->activated_plugins, plugin) == NULL))
1730 			continue;
1731 
1732 		while (s_node)
1733 		{
1734 			gchar *val;
1735 			GList *vals;
1736 			GList *node;
1737 			gboolean found = FALSE;
1738 
1739 			satisfied = TRUE;
1740 
1741 			sec = s_node->data;
1742 			aname = n_node->data;
1743 			avalue = v_node->data;
1744 
1745 			if (!anjuta_plugin_description_get_string (desc, sec, aname, &val))
1746 			{
1747 				satisfied = FALSE;
1748 				break;
1749 			}
1750 
1751 			vals = property_to_list (val);
1752 			g_free (val);
1753 
1754 			node = vals;
1755 			while (node)
1756 			{
1757 				if (strchr(node->data, '*') != NULL)
1758 				{
1759 					// Star match.
1760 					gchar **segments;
1761 					gchar **seg_ptr;
1762 					const gchar *cursor;
1763 
1764 					segments = g_strsplit (node->data, "*", -1);
1765 
1766 					seg_ptr = segments;
1767 					cursor = avalue;
1768 					while (*seg_ptr != NULL)
1769 					{
1770 						if (strlen (*seg_ptr) > 0) {
1771 							cursor = strstr (cursor, *seg_ptr);
1772 							if (cursor == NULL)
1773 								break;
1774 						}
1775 						cursor += strlen (*seg_ptr);
1776 						seg_ptr++;
1777 					}
1778 					if (*seg_ptr == NULL)
1779 						found = TRUE;
1780 					g_strfreev (segments);
1781 				}
1782 				else if (g_ascii_strcasecmp (node->data, avalue) == 0)
1783 				{
1784 					// String match.
1785 					found = TRUE;
1786 				}
1787 				g_free (node->data);
1788 				node = g_list_next (node);
1789 			}
1790 			g_list_free (vals);
1791 			if (!found)
1792 			{
1793 				satisfied = FALSE;
1794 				break;
1795 			}
1796 			s_node = g_list_next (s_node);
1797 			n_node = g_list_next (n_node);
1798 			v_node = g_list_next (v_node);
1799 		}
1800 		if (satisfied)
1801 		{
1802 			selected_plugins = g_list_prepend (selected_plugins, plugin);
1803 			/* DEBUG_PRINT ("Satisfied, Adding %s",
1804 						 anjuta_plugin_handle_get_name (plugin));*/
1805 		}
1806 	}
1807 
1808 	return g_list_reverse (selected_plugins);
1809 }
1810 
1811 GList*
anjuta_plugin_manager_query(AnjutaPluginManager * plugin_manager,const gchar * section_name,const gchar * attribute_name,const gchar * attribute_value,...)1812 anjuta_plugin_manager_query (AnjutaPluginManager *plugin_manager,
1813 							 const gchar *section_name,
1814 							 const gchar *attribute_name,
1815 							 const gchar *attribute_value,
1816 							 ...)
1817 {
1818 	va_list var_args;
1819 	GList *secs = NULL;
1820 	GList *anames = NULL;
1821 	GList *avalues = NULL;
1822 	const gchar *sec;
1823 	const gchar *aname;
1824 	const gchar *avalue;
1825 	GList *selected_plugins;
1826 
1827 
1828 	if (section_name == NULL)
1829 	{
1830 		/* If no query is given, select all plugins */
1831 		return anjuta_plugin_manager_list_query (plugin_manager, NULL, NULL, NULL);
1832 	}
1833 
1834 	g_return_val_if_fail (section_name != NULL, NULL);
1835 	g_return_val_if_fail (attribute_name != NULL, NULL);
1836 	g_return_val_if_fail (attribute_value != NULL, NULL);
1837 
1838 	secs = g_list_prepend (secs, g_strdup (section_name));
1839 	anames = g_list_prepend (anames, g_strdup (attribute_name));
1840 	avalues = g_list_prepend (avalues, g_strdup (attribute_value));
1841 
1842 	va_start (var_args, attribute_value);
1843 	do
1844 	{
1845 		sec = va_arg (var_args, const gchar *);
1846 		if (sec)
1847 		{
1848 			aname = va_arg (var_args, const gchar *);
1849 			if (aname)
1850 			{
1851 				avalue = va_arg (var_args, const gchar *);
1852 				if (avalue)
1853 				{
1854 					secs = g_list_prepend (secs, g_strdup (sec));
1855 					anames = g_list_prepend (anames, g_strdup (aname));
1856 					avalues = g_list_prepend (avalues, g_strdup (avalue));
1857 				}
1858 			}
1859 		}
1860 	}
1861 	while (sec);
1862 	va_end (var_args);
1863 
1864 	secs = g_list_reverse (secs);
1865 	anames = g_list_reverse (anames);
1866 	avalues = g_list_reverse (avalues);
1867 
1868 	selected_plugins = anjuta_plugin_manager_list_query (plugin_manager,
1869 														 secs,
1870 														 anames,
1871 														 avalues);
1872 
1873 	anjuta_util_glist_strings_free (secs);
1874 	anjuta_util_glist_strings_free (anames);
1875 	anjuta_util_glist_strings_free (avalues);
1876 
1877 	return selected_plugins;
1878 }
1879 
1880 enum {
1881 	PIXBUF_COLUMN,
1882 	PLUGIN_COLUMN,
1883 	PLUGIN_HANDLE_COLUMN,
1884 	N_COLUMNS
1885 };
1886 
1887 static void
on_plugin_list_row_activated(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * column,GtkDialog * dialog)1888 on_plugin_list_row_activated (GtkTreeView *tree_view,
1889 							  GtkTreePath *path,
1890                               GtkTreeViewColumn *column,
1891                               GtkDialog *dialog)
1892 {
1893 	gtk_dialog_response (dialog, GTK_RESPONSE_OK);
1894 }
1895 
1896 
1897 static void
on_plugin_list_show(GtkTreeView * view,GtkDirectionType direction,GtkDialog * dialog)1898 on_plugin_list_show (GtkTreeView *view,
1899                      GtkDirectionType direction,
1900                      GtkDialog *dialog)
1901 {
1902 	GtkTreeSelection *selection;
1903 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
1904 
1905 	g_signal_emit_by_name (G_OBJECT (selection), "changed", GTK_DIALOG(dialog), NULL);
1906 }
1907 
1908 
1909 static void
on_plugin_list_selection_changed(GtkTreeSelection * tree_selection,GtkDialog * dialog)1910 on_plugin_list_selection_changed (GtkTreeSelection *tree_selection,
1911 								  GtkDialog *dialog)
1912 {
1913 	GtkContainer *action_area;
1914 	GList *list;
1915 	GtkButton *bt = NULL;
1916 
1917 	action_area = GTK_CONTAINER (gtk_dialog_get_action_area (dialog));
1918 	list = gtk_container_get_children (action_area);
1919 	for (; list; list = list->next) {
1920 		bt = list->data;
1921 		if (!strcmp("gtk-ok", gtk_button_get_label (bt)))
1922 		   break;
1923 	}
1924 	if (bt && gtk_tree_selection_get_selected (tree_selection, NULL, NULL))
1925 		gtk_widget_set_sensitive ((GtkWidget *) bt, TRUE);
1926 	else
1927 		gtk_widget_set_sensitive ((GtkWidget *) bt, FALSE);
1928 	g_list_free(list);
1929 }
1930 
1931 /*
1932  * anjuta_plugin_manager_select:
1933  * @plugin_manager: #AnjutaPluginManager object
1934  * @title: Title of the dialog
1935  * @description: label shown on the dialog
1936  * @plugin_handles: List of #AnjutaPluginHandle
1937  *
1938  * Show a dialog where the user can choose between the given plugins
1939  *
1940  * Returns: The chosen plugin handle
1941  */
1942 AnjutaPluginHandle *
anjuta_plugin_manager_select(AnjutaPluginManager * plugin_manager,gchar * title,gchar * description,GList * plugin_handles)1943 anjuta_plugin_manager_select (AnjutaPluginManager *plugin_manager,
1944 							  gchar *title, gchar *description,
1945 							  GList *plugin_handles)
1946 {
1947 	AnjutaPluginManagerPriv *priv;
1948 	gint plugin_count;
1949 	AnjutaPluginHandle *handle;
1950 	GtkWidget *dlg;
1951 	GtkTreeModel *model;
1952 	GtkWidget *view;
1953 	GtkTreeViewColumn *column;
1954 	GtkCellRenderer *renderer;
1955 	GList *node;
1956 	GtkWidget *label;
1957 	GtkWidget *content_area;
1958 	GtkWidget *sc;
1959 	GtkWidget *remember_checkbox;
1960 	gint response;
1961 	GtkTreeIter selected;
1962 	GtkTreeSelection *selection;
1963 	GtkTreeModel *store;
1964 	GList *selection_ids = NULL;
1965 	GString *remember_key = g_string_new ("");
1966 
1967 	g_return_val_if_fail (title != NULL, NULL);
1968 	g_return_val_if_fail (description != NULL, NULL);
1969 	g_return_val_if_fail (plugin_handles != NULL, NULL);
1970 
1971 	priv = plugin_manager->priv;
1972 
1973 	plugin_count = g_list_length (plugin_handles);
1974 	if (plugin_count <= 0)
1975 		return NULL;
1976 
1977 	dlg = gtk_dialog_new_with_buttons (title, GTK_WINDOW (priv->shell),
1978 									   GTK_DIALOG_DESTROY_WITH_PARENT,
1979 									   GTK_STOCK_CANCEL,
1980 									   GTK_RESPONSE_CANCEL,
1981 									   GTK_STOCK_OK, GTK_RESPONSE_OK,
1982 									   GTK_STOCK_HELP, GTK_RESPONSE_HELP,
1983 									   NULL);
1984 	gtk_window_set_default_size (GTK_WINDOW (dlg), 520, 200 + (plugin_count > 6 ? 300 : plugin_count * 60));
1985 
1986 	label = gtk_label_new (description);
1987 	gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
1988 	gtk_widget_show (label);
1989 	content_area = gtk_dialog_get_content_area (GTK_DIALOG (dlg));
1990 	gtk_box_pack_start (GTK_BOX (content_area), label,
1991 			    FALSE, FALSE, 5);
1992 
1993 	sc = gtk_scrolled_window_new (NULL, NULL);
1994 	gtk_widget_show (sc);
1995 	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sc),
1996 										 GTK_SHADOW_IN);
1997 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sc),
1998 									GTK_POLICY_AUTOMATIC,
1999 									GTK_POLICY_AUTOMATIC);
2000 
2001 	gtk_box_pack_start (GTK_BOX (content_area), sc,
2002 			    TRUE, TRUE, 5);
2003 
2004 	model = GTK_TREE_MODEL (gtk_list_store_new (N_COLUMNS, GDK_TYPE_PIXBUF,
2005 										   G_TYPE_STRING, G_TYPE_POINTER));
2006 	view = gtk_tree_view_new_with_model (model);
2007 	gtk_widget_show (view);
2008 	gtk_container_add (GTK_CONTAINER (sc), view);
2009 
2010 	column = gtk_tree_view_column_new ();
2011 	gtk_tree_view_column_set_sizing (column,
2012 									 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
2013 	gtk_tree_view_column_set_title (column, dgettext (GETTEXT_PACKAGE, "Available Plugins"));
2014 
2015 	renderer = gtk_cell_renderer_pixbuf_new ();
2016 	gtk_tree_view_column_pack_start (column, renderer, FALSE);
2017 	gtk_tree_view_column_add_attribute (column, renderer, "pixbuf",
2018 										PIXBUF_COLUMN);
2019 
2020 	renderer = gtk_cell_renderer_text_new ();
2021 	g_object_set (G_OBJECT (renderer), "wrap-mode", PANGO_WRAP_WORD_CHAR,
2022 	                                   "wrap-width", 450, NULL);
2023 	gtk_tree_view_column_pack_start (column, renderer, TRUE);
2024 	gtk_tree_view_column_add_attribute (column, renderer, "markup",
2025 										PLUGIN_COLUMN);
2026 
2027 	gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
2028 	gtk_tree_view_set_expander_column (GTK_TREE_VIEW (view), column);
2029 
2030 	g_signal_connect (view, "row-activated",
2031 					  G_CALLBACK (on_plugin_list_row_activated),
2032 					  GTK_DIALOG(dlg));
2033 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
2034 	g_signal_connect(selection, "changed",
2035 					 G_CALLBACK(on_plugin_list_selection_changed),
2036 					 GTK_DIALOG(dlg));
2037 	g_signal_connect(view, "focus",
2038 					 G_CALLBACK(on_plugin_list_show),
2039 					 GTK_DIALOG(dlg));
2040 
2041 	remember_checkbox =
2042 		gtk_check_button_new_with_label (dgettext (GETTEXT_PACKAGE, "Remember this selection"));
2043 	gtk_container_set_border_width (GTK_CONTAINER (remember_checkbox), 10);
2044 	gtk_widget_show (remember_checkbox);
2045 	gtk_box_pack_start (GTK_BOX (content_area), remember_checkbox,
2046 						FALSE, FALSE, 0);
2047 
2048 	node = plugin_handles;
2049 	while (node)
2050 	{
2051 		const gchar *filename;
2052 		GdkPixbuf *icon_pixbuf = NULL;
2053 		const gchar *name = NULL;
2054 		AnjutaPluginDescription *desc;
2055 
2056 		handle = (AnjutaPluginHandle*)node->data;
2057 
2058 		filename = anjuta_plugin_handle_get_icon_path (handle);
2059 		if (filename != NULL)
2060 		{
2061 			icon_pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
2062 			if (!icon_pixbuf)
2063 				g_warning ("Plugin does not define Icon: No such file %s",
2064 				           filename);
2065 		}
2066 		else
2067 		{
2068 			g_warning ("Plugin does not define Icon attribute");
2069 		}
2070 
2071 		name = anjuta_plugin_handle_get_name (handle);
2072 		desc = anjuta_plugin_handle_get_description (handle);
2073 		if ((name != NULL) && (desc != NULL))
2074 		{
2075 			gchar *plugin_desc;
2076 			GtkTreeIter iter;
2077 			gchar *text;
2078 
2079 			if (!anjuta_plugin_description_get_locale_string (desc,
2080 			                                                  "Anjuta Plugin",
2081 			                                                  "Description",
2082 			                                                  &plugin_desc))
2083 			{
2084 				g_warning ("Plugin does not define Description attribute");
2085 			}
2086 			text = g_markup_printf_escaped ("<span size=\"larger\" weight=\"bold\">%s</span>\n%s", name, plugin_desc);
2087 			g_free (plugin_desc);
2088 
2089 			gtk_list_store_append (GTK_LIST_STORE (model), &iter);
2090 			gtk_list_store_set (GTK_LIST_STORE (model), &iter,
2091 								PLUGIN_COLUMN, text,
2092 								PLUGIN_HANDLE_COLUMN, handle, -1);
2093 			if (icon_pixbuf) {
2094 				gtk_list_store_set (GTK_LIST_STORE (model), &iter,
2095 									PIXBUF_COLUMN, icon_pixbuf, -1);
2096 			}
2097 			g_free (text);
2098 
2099 			selection_ids = g_list_prepend (selection_ids, (gpointer)anjuta_plugin_handle_get_id (handle));
2100 		}
2101 		else
2102 		{
2103 			g_warning ("Plugin does not define Name attribute");
2104 		}
2105 
2106 		if (icon_pixbuf)
2107 			g_object_unref (icon_pixbuf);
2108 
2109 		node = g_list_next (node);
2110 	}
2111 
2112 	/* Prepare remembering key */
2113 	selection_ids = g_list_sort (selection_ids,
2114 	                             (GCompareFunc)strcmp);
2115 	node = selection_ids;
2116 	while (node)
2117 	{
2118 		g_string_append (remember_key, (gchar*)node->data);
2119 		g_string_append (remember_key, ",");
2120 		node = g_list_next (node);
2121 	}
2122 	g_list_free (selection_ids);
2123 
2124 	/* Find if the selection is remembered */
2125 	handle = g_hash_table_lookup (priv->remember_plugins, remember_key->str);
2126 	if (handle)
2127 	{
2128 		g_string_free (remember_key, TRUE);
2129 		gtk_widget_destroy (dlg);
2130 		return handle;
2131 	}
2132 
2133 	/* Prompt dialog */
2134 	response = gtk_dialog_run (GTK_DIALOG (dlg));
2135 	switch (response)
2136 	{
2137 	case GTK_RESPONSE_OK:
2138 		selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
2139 		if (gtk_tree_selection_get_selected (selection, &store,
2140 											 &selected))
2141 		{
2142 			gtk_tree_model_get (model, &selected,
2143 								PLUGIN_HANDLE_COLUMN, &handle, -1);
2144 			if (handle)
2145 			{
2146 				/* Remember selection */
2147 				if (gtk_toggle_button_get_active
2148 					(GTK_TOGGLE_BUTTON (remember_checkbox)))
2149 				{
2150 					/* DEBUG_PRINT ("Remembering selection '%s'",
2151 								 remember_key->str);*/
2152 					g_hash_table_insert (priv->remember_plugins,
2153 										 g_strdup (remember_key->str), handle);
2154 				}
2155 				g_string_free (remember_key, TRUE);
2156 				gtk_widget_destroy (dlg);
2157 				return handle;
2158 			}
2159 		}
2160 		break;
2161 	}
2162 	g_string_free (remember_key, TRUE);
2163 	gtk_widget_destroy (dlg);
2164 	return NULL;
2165 }
2166 
2167 GObject*
anjuta_plugin_manager_select_and_activate(AnjutaPluginManager * plugin_manager,gchar * title,gchar * description,GList * plugin_handles)2168 anjuta_plugin_manager_select_and_activate (AnjutaPluginManager *plugin_manager,
2169 										   gchar *title,
2170 										   gchar *description,
2171 										   GList *plugin_handles)
2172 {
2173 	AnjutaPluginHandle *handle;
2174 	GObject *plugin = NULL;
2175 
2176 	g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), NULL);
2177 
2178 	handle = anjuta_plugin_manager_select (plugin_manager, title, description,
2179 	                                       plugin_handles);
2180 	plugin = anjuta_plugin_manager_get_plugin_by_handle (plugin_manager, handle);
2181 
2182 	return plugin;
2183 }
2184 
2185 /*
2186  * anjuta_plugin_manager_get_plugin_handle:
2187  * @plugin_manager: #AnjutaPluginManager object
2188  * @plugin: #AnjutaPlugin object
2189  *
2190  * Get the handle corresponding to the plugin or %NULL if the plugin is not
2191  * activated.
2192  *
2193  * Returns: (transfer none) (allow-none):  A #AnjutaPluginHandle or %NULL.
2194  */
2195 AnjutaPluginHandle*
anjuta_plugin_manager_get_plugin_handle(AnjutaPluginManager * plugin_manager,GObject * plugin)2196 anjuta_plugin_manager_get_plugin_handle (AnjutaPluginManager *plugin_manager,
2197                                          GObject *plugin)
2198 {
2199 	GHashTableIter iter;
2200 	gpointer key, value;
2201 
2202 	g_hash_table_iter_init (&iter, plugin_manager->priv->activated_plugins);
2203 	while (g_hash_table_iter_next (&iter, &key, &value))
2204 	{
2205 		if (G_OBJECT(value) == plugin)
2206 		{
2207 			return ANJUTA_PLUGIN_HANDLE (key);
2208 		}
2209 	}
2210 
2211 	return NULL;
2212 }
2213 
2214 
2215 /* Plugin manager */
2216 
2217 static void
anjuta_plugin_manager_init(AnjutaPluginManager * object)2218 anjuta_plugin_manager_init (AnjutaPluginManager *object)
2219 {
2220 	object->priv = g_new0 (AnjutaPluginManagerPriv, 1);
2221 	object->priv->plugins_by_name = g_hash_table_new (g_str_hash, g_str_equal);
2222 	object->priv->plugins_by_interfaces = g_hash_table_new_full (g_str_hash,
2223 											   g_str_equal,
2224 											   NULL,
2225 											   (GDestroyNotify) g_list_free);
2226 	object->priv->plugins_by_description = g_hash_table_new (g_direct_hash,
2227 														   g_direct_equal);
2228 	object->priv->activated_plugins = g_hash_table_new_full (g_direct_hash, g_direct_equal,
2229 	                                                         NULL, g_object_unref);
2230 	object->priv->plugins_cache = g_hash_table_new_full (g_direct_hash, g_direct_equal,
2231 	                                                     NULL, g_object_unref);
2232 	object->priv->remember_plugins = g_hash_table_new_full (g_str_hash,
2233 															g_str_equal,
2234 															NULL, NULL);
2235 	object->priv->disable_plugins = g_hash_table_new (g_direct_hash,
2236 	                                                  g_direct_equal);
2237 }
2238 
2239 static void
anjuta_plugin_manager_dispose(GObject * object)2240 anjuta_plugin_manager_dispose (GObject *object)
2241 {
2242 	AnjutaPluginManagerPriv *priv;
2243 	priv = ANJUTA_PLUGIN_MANAGER (object)->priv;
2244 
2245 	if (priv->available_plugins)
2246 	{
2247 		g_list_foreach (priv->available_plugins, (GFunc)g_object_unref, NULL);
2248 		g_list_free (priv->available_plugins);
2249 		priv->available_plugins = NULL;
2250 	}
2251 	if (priv->activated_plugins)
2252 	{
2253 		g_hash_table_destroy (priv->activated_plugins);
2254 		priv->activated_plugins = NULL;
2255 	}
2256 	if (priv->plugins_cache)
2257 	{
2258 		g_hash_table_destroy (priv->plugins_cache);
2259 		priv->plugins_cache = NULL;
2260 	}
2261 	if (priv->disable_plugins)
2262 	{
2263 		g_hash_table_destroy (priv->disable_plugins);
2264 		priv->disable_plugins = NULL;
2265 	}
2266 	if (priv->plugins_by_name)
2267 	{
2268 		g_hash_table_destroy (priv->plugins_by_name);
2269 		priv->plugins_by_name = NULL;
2270 	}
2271 	if (priv->plugins_by_description)
2272 	{
2273 		g_hash_table_destroy (priv->plugins_by_description);
2274 		priv->plugins_by_description = NULL;
2275 	}
2276 	if (priv->plugins_by_interfaces)
2277 	{
2278 		g_hash_table_destroy (priv->plugins_by_interfaces);
2279 		priv->plugins_by_interfaces = NULL;
2280 	}
2281 	if (priv->plugin_dirs)
2282 	{
2283 		g_list_foreach (priv->plugin_dirs, (GFunc)g_free, NULL);
2284 		g_list_free (priv->plugin_dirs);
2285 		priv->plugin_dirs = NULL;
2286 	}
2287 #if 0
2288 	if (anjuta_c_plugin_factory)
2289 	{
2290 		g_object_unref (anjuta_c_plugin_factory);
2291 		anjuta_c_plugin_factory = NULL;
2292 	}
2293 #endif
2294 	G_OBJECT_CLASS (parent_class)->dispose (object);
2295 }
2296 
2297 static void
anjuta_plugin_manager_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)2298 anjuta_plugin_manager_set_property (GObject *object, guint prop_id,
2299 									const GValue *value, GParamSpec *pspec)
2300 {
2301 	AnjutaPluginManagerPriv *priv;
2302 
2303 	g_return_if_fail (ANJUTA_IS_PLUGIN_MANAGER (object));
2304 	priv = ANJUTA_PLUGIN_MANAGER (object)->priv;
2305 
2306 	switch (prop_id)
2307 	{
2308 	case PROP_STATUS:
2309 		priv->status = g_value_get_object (value);
2310 		break;
2311 	case PROP_SHELL:
2312 		priv->shell = g_value_get_object (value);
2313 		break;
2314 	default:
2315 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2316 		break;
2317 	}
2318 }
2319 
2320 static void
anjuta_plugin_manager_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)2321 anjuta_plugin_manager_get_property (GObject *object, guint prop_id,
2322 									GValue *value, GParamSpec *pspec)
2323 {
2324 	AnjutaPluginManagerPriv *priv;
2325 
2326 	g_return_if_fail (ANJUTA_IS_PLUGIN_MANAGER (object));
2327 	priv = ANJUTA_PLUGIN_MANAGER (object)->priv;
2328 
2329 	switch (prop_id)
2330 	{
2331 	case PROP_SHELL:
2332 		g_value_set_object (value, priv->shell);
2333 		break;
2334 	case PROP_STATUS:
2335 		g_value_set_object (value, priv->status);
2336 		break;
2337 	case PROP_AVAILABLE_PLUGINS:
2338 		g_value_set_pointer (value, priv->available_plugins);
2339 		break;
2340 	case PROP_ACTIVATED_PLUGINS:
2341 		g_value_set_pointer (value, priv->activated_plugins);
2342 		break;
2343 	default:
2344 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2345 		break;
2346 	}
2347 }
2348 static void
anjuta_plugin_manager_plugin_activated(AnjutaPluginManager * self,AnjutaPluginHandle * handle,GObject * plugin)2349 anjuta_plugin_manager_plugin_activated (AnjutaPluginManager *self,
2350 										AnjutaPluginHandle* handle,
2351 										GObject *plugin)
2352 {
2353 	/* TODO: Add default signal handler implementation here */
2354 }
2355 
2356 static void
anjuta_plugin_manager_plugin_deactivated(AnjutaPluginManager * self,AnjutaPluginHandle * handle,GObject * plugin)2357 anjuta_plugin_manager_plugin_deactivated (AnjutaPluginManager *self,
2358 										  AnjutaPluginHandle* handle,
2359 										  GObject *plugin)
2360 {
2361 	/* TODO: Add default signal handler implementation here */
2362 }
2363 
2364 static void
anjuta_plugin_manager_class_init(AnjutaPluginManagerClass * klass)2365 anjuta_plugin_manager_class_init (AnjutaPluginManagerClass *klass)
2366 {
2367 	GObjectClass* object_class = G_OBJECT_CLASS (klass);
2368 	parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
2369 
2370 	object_class->dispose = anjuta_plugin_manager_dispose;
2371 	object_class->set_property = anjuta_plugin_manager_set_property;
2372 	object_class->get_property = anjuta_plugin_manager_get_property;
2373 
2374 	klass->plugin_activated = anjuta_plugin_manager_plugin_activated;
2375 	klass->plugin_deactivated = anjuta_plugin_manager_plugin_deactivated;
2376 
2377 	g_object_class_install_property (object_class,
2378 	                                 PROP_PROFILES,
2379 	                                 g_param_spec_pointer ("profiles",
2380 	                                                       dgettext (GETTEXT_PACKAGE, "Profiles"),
2381 	                                                       dgettext (GETTEXT_PACKAGE, "Current stack of profiles"),
2382 	                                                       G_PARAM_READABLE));
2383 	g_object_class_install_property (object_class,
2384 	                                 PROP_AVAILABLE_PLUGINS,
2385 	                                 g_param_spec_pointer ("available-plugins",
2386 	                                                       dgettext (GETTEXT_PACKAGE, "Available plugins"),
2387 	                                                       dgettext (GETTEXT_PACKAGE, "Currently available plugins found in plugin paths"),
2388 	                                                       G_PARAM_READABLE));
2389 
2390 	g_object_class_install_property (object_class,
2391 	                                 PROP_ACTIVATED_PLUGINS,
2392 	                                 g_param_spec_pointer ("activated-plugins",
2393 	                                                       dgettext (GETTEXT_PACKAGE, "Activated plugins"),
2394 	                                                       dgettext (GETTEXT_PACKAGE, "Currently activated plugins"),
2395 	                                                       G_PARAM_READABLE));
2396 	g_object_class_install_property (object_class,
2397 	                                 PROP_SHELL,
2398 	                                 g_param_spec_object ("shell",
2399 														  dgettext (GETTEXT_PACKAGE, "Anjuta Shell"),
2400 														  dgettext (GETTEXT_PACKAGE, "Anjuta shell for which the plugins are made"),
2401 														  G_TYPE_OBJECT,
2402 														  G_PARAM_READABLE |
2403 														  G_PARAM_WRITABLE |
2404 														  G_PARAM_CONSTRUCT));
2405 	g_object_class_install_property (object_class,
2406 	                                 PROP_STATUS,
2407 	                                 g_param_spec_object ("status",
2408 														  dgettext (GETTEXT_PACKAGE, "Anjuta Status"),
2409 														  dgettext (GETTEXT_PACKAGE, "Anjuta status to use in loading and unloading of plugins"),
2410 														  ANJUTA_TYPE_STATUS,
2411 														  G_PARAM_READABLE |
2412 														  G_PARAM_WRITABLE |
2413 														  G_PARAM_CONSTRUCT));
2414 
2415 	plugin_manager_signals[PLUGIN_ACTIVATED] =
2416 		g_signal_new ("plugin-activated",
2417 		              G_OBJECT_CLASS_TYPE (klass),
2418 		              G_SIGNAL_RUN_FIRST,
2419 		              G_STRUCT_OFFSET (AnjutaPluginManagerClass,
2420 									   plugin_activated),
2421 		              NULL, NULL,
2422 					  anjuta_cclosure_marshal_VOID__POINTER_OBJECT,
2423 		              G_TYPE_NONE, 2,
2424 		              G_TYPE_POINTER, ANJUTA_TYPE_PLUGIN);
2425 
2426 	plugin_manager_signals[PLUGIN_DEACTIVATED] =
2427 		g_signal_new ("plugin-deactivated",
2428 		              G_OBJECT_CLASS_TYPE (klass),
2429 		              G_SIGNAL_RUN_FIRST,
2430 		              G_STRUCT_OFFSET (AnjutaPluginManagerClass,
2431 									   plugin_deactivated),
2432 		              NULL, NULL,
2433 					  anjuta_cclosure_marshal_VOID__POINTER_OBJECT,
2434 		              G_TYPE_NONE, 2,
2435 		              G_TYPE_POINTER, ANJUTA_TYPE_PLUGIN);
2436 }
2437 
2438 GType
anjuta_plugin_manager_get_type(void)2439 anjuta_plugin_manager_get_type (void)
2440 {
2441 	static GType our_type = 0;
2442 
2443 	if(our_type == 0)
2444 	{
2445 		static const GTypeInfo our_info =
2446 		{
2447 			sizeof (AnjutaPluginManagerClass), /* class_size */
2448 			(GBaseInitFunc) NULL, /* base_init */
2449 			(GBaseFinalizeFunc) NULL, /* base_finalize */
2450 			(GClassInitFunc) anjuta_plugin_manager_class_init, /* class_init */
2451 			(GClassFinalizeFunc) NULL, /* class_finalize */
2452 			NULL /* class_data */,
2453 			sizeof (AnjutaPluginManager), /* instance_size */
2454 			0, /* n_preallocs */
2455 			(GInstanceInitFunc) anjuta_plugin_manager_init, /* instance_init */
2456 			NULL /* value_table */
2457 		};
2458 		our_type = g_type_register_static (G_TYPE_OBJECT,
2459 										   "AnjutaPluginManager",
2460 		                                   &our_info, 0);
2461 	}
2462 
2463 	return our_type;
2464 }
2465 
2466 AnjutaPluginManager*
anjuta_plugin_manager_new(GObject * shell,AnjutaStatus * status,GList * plugins_directories)2467 anjuta_plugin_manager_new (GObject *shell, AnjutaStatus *status,
2468 						   GList* plugins_directories)
2469 {
2470 	GObject *manager_object;
2471 	AnjutaPluginManager *plugin_manager;
2472 	GList *cycles = NULL;
2473 	const char *gnome2_path;
2474 	char **pathv;
2475 	char **p;
2476 	GList *node;
2477 	GList *plugin_dirs = NULL;
2478 
2479 	/* Initialize the anjuta plugin system */
2480 	manager_object = g_object_new (ANJUTA_TYPE_PLUGIN_MANAGER,
2481 								   "shell", shell, "status", status, NULL);
2482 	plugin_manager = ANJUTA_PLUGIN_MANAGER (manager_object);
2483 
2484 	if (anjuta_plugin_factory == NULL)
2485 	{
2486 		anjuta_plugin_factory = anjuta_c_plugin_factory_new ();
2487 	}
2488 
2489 	gnome2_path = g_getenv ("GNOME2_PATH");
2490 	if (gnome2_path) {
2491 		pathv = g_strsplit (gnome2_path, ":", 1);
2492 
2493 		for (p = pathv; *p != NULL; p++) {
2494 			char *path = g_strdup (*p);
2495 			plugin_dirs = g_list_prepend (plugin_dirs, path);
2496 		}
2497 		g_strfreev (pathv);
2498 	}
2499 
2500 	node = plugins_directories;
2501 	while (node) {
2502 		if (!node->data)
2503 			continue;
2504 		char *path = g_strdup (node->data);
2505 		plugin_dirs = g_list_prepend (plugin_dirs, path);
2506 		node = g_list_next (node);
2507 	}
2508 	plugin_dirs = g_list_reverse (plugin_dirs);
2509 	/* load_plugins (); */
2510 
2511 	node = plugin_dirs;
2512 	while (node)
2513 	{
2514 		load_plugins_from_directory (plugin_manager, (char*)node->data);
2515 		node = g_list_next (node);
2516 	}
2517 	resolve_dependencies (plugin_manager, &cycles);
2518 	g_list_foreach(plugin_dirs, (GFunc) g_free, NULL);
2519 	g_list_free(plugin_dirs);
2520 	return plugin_manager;
2521 }
2522 
2523 void
anjuta_plugin_manager_activate_plugins(AnjutaPluginManager * plugin_manager,GList * plugins_to_activate)2524 anjuta_plugin_manager_activate_plugins (AnjutaPluginManager *plugin_manager,
2525 										GList *plugins_to_activate)
2526 {
2527 	AnjutaPluginManagerPriv *priv;
2528 	GList *node;
2529 
2530 	priv = plugin_manager->priv;
2531 
2532 	/* Freeze shell operations */
2533 	anjuta_shell_freeze (ANJUTA_SHELL (priv->shell), NULL);
2534 	if (plugins_to_activate)
2535 	{
2536 		anjuta_status_progress_add_ticks (ANJUTA_STATUS (priv->status),
2537 										  g_list_length (plugins_to_activate));
2538 	}
2539 	node = plugins_to_activate;
2540 	while (node)
2541 	{
2542 		AnjutaPluginHandle *handle;
2543 		const gchar *filename;
2544 		GdkPixbuf *icon_pixbuf = NULL;
2545 		const gchar *name;
2546 		gchar*label= NULL;
2547 
2548 		handle = node->data;
2549 
2550 		filename = anjuta_plugin_handle_get_icon_path (handle);
2551 		if (filename != NULL)
2552 		{
2553 			icon_pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
2554 			if (!icon_pixbuf)
2555 				g_warning ("Plugin does not define Icon: No such file %s",
2556 				           filename);
2557 		}
2558 
2559 		name = anjuta_plugin_handle_get_name (handle);
2560 		if (name != NULL)
2561 		{
2562 			label = g_strconcat (dgettext (GETTEXT_PACKAGE, "Loading:"), " ", name, "...", NULL);
2563 		}
2564 
2565 		anjuta_status_progress_tick (ANJUTA_STATUS (priv->status),
2566 									 icon_pixbuf, label);
2567 		g_free (label);
2568 		if (icon_pixbuf)
2569 			g_object_unref (icon_pixbuf);
2570 
2571 		/* Activate the plugin */
2572 		anjuta_plugin_manager_get_plugin_by_handle (plugin_manager, handle);
2573 
2574 		node = g_list_next (node);
2575 	}
2576 
2577 	/* Thaw shell operations */
2578 	anjuta_shell_thaw (ANJUTA_SHELL (priv->shell), NULL);
2579 }
2580 
2581 static void
on_collect(gpointer key,gpointer value,gpointer user_data)2582 on_collect (gpointer key, gpointer value, gpointer user_data)
2583 {
2584 	const gchar *id;
2585 	gchar *query = (gchar*) key;
2586 	AnjutaPluginHandle *handle = (AnjutaPluginHandle *) value;
2587 	GString *write_buffer = (GString *) user_data;
2588 
2589 	id = anjuta_plugin_handle_get_id (handle);
2590 	g_string_append_printf (write_buffer, "%s=%s;", query, id);
2591 }
2592 
2593 /**
2594  * anjuta_plugin_manager_get_remembered_plugins:
2595  * @plugin_manager: A #AnjutaPluginManager object
2596  *
2597  * Get the list of plugins loaded when there is a choice between several
2598  * ones without asking the user.
2599  *
2600  * The list format is returned as a string with the format detailed in
2601  * anjuta_plugin_manager_set_remembered_plugins().
2602  *
2603  * Return value: (transfer full): a newly-allocated string that must be freed
2604  * with g_free().
2605  */
2606 
2607 gchar*
anjuta_plugin_manager_get_remembered_plugins(AnjutaPluginManager * plugin_manager)2608 anjuta_plugin_manager_get_remembered_plugins (AnjutaPluginManager *plugin_manager)
2609 {
2610 	AnjutaPluginManagerPriv *priv;
2611 	GString *write_buffer = g_string_new ("");
2612 
2613 	g_return_val_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager), FALSE);
2614 
2615 	priv = plugin_manager->priv;
2616 	g_hash_table_foreach (priv->remember_plugins, on_collect,
2617 						  write_buffer);
2618 	return g_string_free (write_buffer, FALSE);
2619 }
2620 
2621 /**
2622  * anjuta_plugin_manager_set_remembered_plugins:
2623  * @plugin_manager: A #AnjutaPluginManager object
2624  * @remembered_plugins: A list of prefered plugins
2625  *
2626  * Set the list of plugins loaded when there is a choice between several
2627  * ones without asking the user.
2628  * The list is a string composed of elements separated by ';'. Each element
2629  * is defined with "key=value", where key is the list of possible plugins and
2630  * the value is the choosen plugin.
2631  *
2632  * By the example the following element
2633  * <programlisting>
2634  *   anjuta-symbol-browser:SymbolBrowserPlugin,anjuta-symbol-db:SymbolDBPlugin,=anjuta-symbol-db:SymbolDBPlugin;
2635  * </programlisting>
2636  * means if Anjuta has to choose between SymbolBrowserPlugin and
2637  * SymbolDBPlugin, it will choose SymbolDBPlugin.
2638  */
2639 void
anjuta_plugin_manager_set_remembered_plugins(AnjutaPluginManager * plugin_manager,const gchar * remembered_plugins)2640 anjuta_plugin_manager_set_remembered_plugins (AnjutaPluginManager *plugin_manager,
2641 											  const gchar *remembered_plugins)
2642 {
2643 	AnjutaPluginManagerPriv *priv;
2644 	gchar **strv_lines, **line_idx;
2645 
2646 	g_return_if_fail (ANJUTA_IS_PLUGIN_MANAGER (plugin_manager));
2647 	g_return_if_fail (remembered_plugins != NULL);
2648 
2649 	priv = plugin_manager->priv;
2650 
2651 	g_hash_table_remove_all (priv->remember_plugins);
2652 
2653 	strv_lines = g_strsplit (remembered_plugins, ";", -1);
2654 	line_idx = strv_lines;
2655 	while (*line_idx)
2656 	{
2657 		gchar **strv_keyvals;
2658 		strv_keyvals = g_strsplit (*line_idx, "=", -1);
2659 		if (strv_keyvals && strv_keyvals[0] && strv_keyvals[1])
2660 		{
2661 			AnjutaPluginHandle *handle;
2662 			handle = g_hash_table_lookup (priv->plugins_by_name,
2663 										  strv_keyvals[1]);
2664 			if (handle)
2665 			{
2666 				g_hash_table_insert (priv->remember_plugins,
2667 									 g_strdup (strv_keyvals[0]), handle);
2668 			}
2669 			g_strfreev (strv_keyvals);
2670 		}
2671 		line_idx++;
2672 	}
2673 	g_strfreev (strv_lines);
2674 }
2675 
2676 /**
2677  * anjuta_plugin_manager_set_disable_plugins:
2678  * @plugin_manager: A #AnjutaPluginManager object
2679  * @plugin_handles: A list of plugins to disable or reenable
2680  * @disable: %TRUE to disable, %FALSE to re-enable plugins in the list
2681  *
2682  * Disable or re-enable plugins. By default, all plugins are enabled but they
2683  * can be disabled and they will not be proposed when a plugin is requested.
2684  */
2685 void
anjuta_plugin_manager_set_disable_plugins(AnjutaPluginManager * plugin_manager,GList * plugin_handles,gboolean disable)2686 anjuta_plugin_manager_set_disable_plugins (AnjutaPluginManager *plugin_manager,
2687                                            GList *plugin_handles,
2688                                            gboolean disable)
2689 {
2690 	GList *item;
2691 
2692 	if (disable)
2693 	{
2694 		for (item = g_list_first (plugin_handles); item != NULL; item = g_list_next (item))
2695 		{
2696 			g_hash_table_add (plugin_manager->priv->disable_plugins, item->data);
2697 		}
2698 	}
2699 	else
2700 	{
2701 		for (item = g_list_first (plugin_handles); item != NULL; item = g_list_next (item))
2702 		{
2703 			g_hash_table_remove (plugin_manager->priv->disable_plugins, item->data);
2704 		}
2705 	}
2706 }
2707