changeset 12695:0bc110c7ab91

[gaim-migrate @ 15038] Let's display outdated plugins in the plugins dialog, but grey them out. This way, the user can find out which plugins need to be updated. They will also be able to view the website address so they know where to get a new version. Inspired by SF Feature Request #1395058 from Daniel Beardsmore (uilleann). committer: Tailor Script <tailor@pidgin.im>
author Richard Laager <rlaager@wiktel.com>
date Tue, 03 Jan 2006 12:03:02 +0000
parents ebed1bbedb04
children 18e619ed4eaf
files plugins/ChangeLog.API src/gtkplugin.c src/plugin.c src/plugin.h
diffstat 4 files changed, 130 insertions(+), 34 deletions(-) [+]
line wrap: on
line diff
--- a/plugins/ChangeLog.API	Tue Jan 03 11:42:51 2006 +0000
+++ b/plugins/ChangeLog.API	Tue Jan 03 12:03:02 2006 +0000
@@ -209,6 +209,8 @@
 	* stock buttons GAIM_STOCK_IM, GAIM_STOCK_INFO
 	* gaim_conversation_present()
 	* GaimConversationUiOps->present(GaimConversation *)
+	* GaimPlugin.unloadable
+	* gaim_plugin_is_unloadable()
 
 	Signals - Changed:  (See the Doxygen docs for details on all signals.)
 	* Signal propagation now stops after a handler returns a non-NULL value.
--- a/src/gtkplugin.c	Tue Jan 03 11:42:51 2006 +0000
+++ b/src/gtkplugin.c	Tue Jan 03 12:03:02 2006 +0000
@@ -130,7 +130,9 @@
 		gtk_list_store_set(ls, &iter,
 				   0, gaim_plugin_is_loaded(plug),
 				   1, desc,
-				   2, plug, -1);
+				   2, plug,
+				   3, gaim_plugin_is_unloadable(plug),
+				   -1);
 		g_free(desc);
 	}
 }
@@ -159,14 +161,19 @@
 	gchar buf[1024];
 	gchar *name = NULL, *description = NULL;
 	GtkWidget *dialog = NULL;
-
-	GdkCursor *wait = gdk_cursor_new (GDK_WATCH);
-	gdk_window_set_cursor(plugin_dialog->window, wait);
-	gdk_cursor_unref(wait);
+	GdkCursor *wait;
 
 	gtk_tree_model_get_iter (model, &iter, path);
 	gtk_tree_model_get (model, &iter, 2, &plug, -1);
 
+	/* Apparently, GTK+ won't honor the sensitive flag on cell renderers for booleans. */
+	if (gaim_plugin_is_unloadable(plug))
+		return;
+
+	wait = gdk_cursor_new (GDK_WATCH);
+	gdk_window_set_cursor(plugin_dialog->window, wait);
+	gdk_cursor_unref(wait);
+
 	if (!gaim_plugin_is_loaded(plug))
 		gaim_plugin_load(plug);
 	else {
@@ -273,6 +280,16 @@
 		   pdesc ? pdesc : "", pdesc ? "\n\n" : "",
 		   pauth ? pauth : "", pweb ? pweb : "", plug->path);
 
+	if (plug->error != NULL)
+	{
+		char *tmp = g_strdup_printf(
+			_("%s\n"
+			  "<span foreground=\"#ff0000\" weight=\"bold\">Error: %s</span>"),
+			buf, plug->error);
+		g_free(buf);
+		buf = tmp;
+	}
+
 	gtk_widget_set_sensitive(pref_button,
 		gaim_plugin_is_loaded(plug)
 		&& ((GAIM_IS_GTK_PLUGIN(plug) && plug->info->ui_info
@@ -399,7 +416,7 @@
 
 	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(plugin_dialog)->vbox), sw, TRUE, TRUE, 0);
 
-	ls = gtk_list_store_new (3, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_POINTER);
+	ls = gtk_list_store_new (4, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_BOOLEAN);
 	gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(ls),
 					     1, GTK_SORT_ASCENDING);
 
@@ -423,9 +440,13 @@
 			  G_CALLBACK(plugin_load), ls);
 
 	rendt = gtk_cell_renderer_text_new();
+	g_object_set(rendt,
+		     "foreground", "#c0c0c0",
+		     NULL);
 	col = gtk_tree_view_column_new_with_attributes (_("Name"),
 							rendt,
 							"markup", 1,
+							"foreground-set", 3,
 							NULL);
 #if GTK_CHECK_VERSION(2,6,0)
 	gtk_tree_view_column_set_expand (col, TRUE);
--- a/src/plugin.c	Tue Jan 03 11:42:51 2006 +0000
+++ b/src/plugin.c	Tue Jan 03 12:03:02 2006 +0000
@@ -28,6 +28,7 @@
 #include "prpl.h"
 #include "request.h"
 #include "signals.h"
+#include "util.h"
 #include "version.h"
 
 typedef struct
@@ -215,11 +216,24 @@
 	g_free(basename);
 	if (plugin != NULL)
 	{
-		if (strcmp(filename, plugin->path))
-			gaim_debug_info("plugins", "Not loading %s."
+		if (!strcmp(filename, plugin->path))
+			return plugin;
+		else if (!gaim_plugin_is_unloadable(plugin))
+		{
+			gaim_debug_info("plugins", "Not loading %s. "
 							"Another plugin with the same name (%s) has already been loaded.\n",
 							filename, plugin->path);
-		return plugin;
+			return plugin;
+		}
+		else
+		{
+			/* The old plugin was a different file and it was unloadable.
+			 * There's no guarantee that this new file with the same name
+			 * will be loadable, but unless it fails in one of the silent
+			 * ways and the first one didn't, it's not any worse.  The user
+			 * will still see a greyed-out plugin, which is what we want. */
+			gaim_plugin_destroy(plugin);
+		}
 	}
 
 	plugin = gaim_plugin_new(has_file_extension(filename, G_MODULE_SUFFIX), filename);
@@ -243,20 +257,50 @@
 		plugin->handle = g_module_open(filename, 0);
 #endif
 
-#ifdef _WIN32
-		/* Restore the original error mode */
-		SetErrorMode(old_error_mode);
-#endif
-
 		if (plugin->handle == NULL)
 		{
-			error = g_module_error();
-			gaim_debug_error("plugins", "%s is unloadable: %s\n",
-							 plugin->path, error ? error : "Unknown error.");
+			const char *error = g_module_error();
+			if (error == NULL)
+				error = "Unknown error";
+			else if (gaim_str_has_prefix(error, filename))
+			{
+				error = error + strlen(filename);
+
+				/* These are just so we don't crash.  If we
+				 * got this far, they should always be true. */
+				if (*error == ':')
+					error++;
+				if (*error == ' ')
+					error++;
+
+				/* This shouldn't ever be necessary. */
+				if (!*error)
+					error = "Unknown error";
+			}
+			plugin->error = g_strdup(error);
 
-			gaim_plugin_destroy(plugin);
+			gaim_debug_error("plugins", "%s is unloadable: %s\n",
+							 plugin->path, plugin->error);
+
+#if GLIB_CHECK_VERSION(2,3,3)
+			plugin->handle = g_module_open(filename, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
+#else
+			plugin->handle = g_module_open(filename, G_MODULE_BIND_LAZY);
+#endif
 
-			return NULL;
+			if (plugin->handle == NULL)
+			{
+				gaim_plugin_destroy(plugin);
+				return NULL;
+			}
+			else
+			{
+				/* We were able to load the plugin with lazy symbol binding.
+				 * This means we're missing some symbol.  Mark it as
+				 * unloadable and keep going so we get the info to display
+				 * to the user so they know to rebuild this plugin. */
+				plugin->unloadable = TRUE;
+			}
 		}
 
 		if (!g_module_symbol(plugin->handle, "gaim_init_plugin",
@@ -275,7 +319,6 @@
 			plugin->handle = NULL;
 
 			gaim_plugin_destroy(plugin);
-
 			return NULL;
 		}
 		gaim_init_plugin = unpunned;
@@ -285,18 +328,20 @@
 
 		if (loader == NULL) {
 			gaim_plugin_destroy(plugin);
-
 			return NULL;
 		}
 
 		gaim_init_plugin = GAIM_PLUGIN_LOADER_INFO(loader)->probe;
 	}
 
-	plugin->error = NULL;
+#ifdef _WIN32
+		/* Restore the original error mode */
+		SetErrorMode(old_error_mode);
+#endif
 
-	if (!gaim_init_plugin(plugin) || plugin->info == NULL) {
+	if (!gaim_init_plugin(plugin) || plugin->info == NULL)
+	{
 		gaim_plugin_destroy(plugin);
-
 		return NULL;
 	}
 
@@ -304,11 +349,12 @@
 			plugin->info->major_version != GAIM_MAJOR_VERSION ||
 			plugin->info->minor_version > GAIM_MINOR_VERSION)
 	{
-		gaim_debug_error("plugins", "%s is unloadable: API version mismatch %d.%d.x (need %d.%d.x)\n",
-						 plugin->path, plugin->info->major_version, plugin->info->minor_version,
+		plugin->error = g_strdup_printf("ABI version mismatch %d.%d.x (need %d.%d.x)",
+						 plugin->info->major_version, plugin->info->minor_version,
 						 GAIM_MAJOR_VERSION, GAIM_MINOR_VERSION);
-		gaim_plugin_destroy(plugin);
-		return NULL;
+		gaim_debug_error("plugins", "%s is unloadable: %s\n", plugin->path, plugin->error);
+		plugin->unloadable = TRUE;
+		return plugin;
 	}
 
 	/* If plugin is a PRPL, make sure it implements the required functions */
@@ -317,10 +363,10 @@
 		(GAIM_PLUGIN_PROTOCOL_INFO(plugin)->login == NULL) ||
 		(GAIM_PLUGIN_PROTOCOL_INFO(plugin)->close == NULL)))
 	{
-		gaim_debug_error("plugins", "%s is unloadable: Does not implement all required functions\n",
-						 plugin->path);
-		gaim_plugin_destroy(plugin);
-		return NULL;
+		plugin->error = g_strdup("Does not implement all required functions");
+		gaim_debug_error("plugins", "%s is unloadable: %s\n", plugin->path, plugin->error);
+		plugin->unloadable = TRUE;
+		return plugin;
 	}
 
 	return plugin;
@@ -346,11 +392,15 @@
 	GList *l;
 
 	g_return_val_if_fail(plugin != NULL, FALSE);
-	g_return_val_if_fail(plugin->error == NULL, FALSE);
 
 	if (gaim_plugin_is_loaded(plugin))
 		return TRUE;
 
+	if (gaim_plugin_is_unloadable(plugin))
+		return FALSE;
+
+	g_return_val_if_fail(plugin->error == NULL, FALSE);
+
 	/*
 	 * Go through the list of the plugin's dependencies.
 	 *
@@ -657,6 +707,14 @@
 	return plugin->loaded;
 }
 
+gboolean
+gaim_plugin_is_unloadable(const GaimPlugin *plugin)
+{
+	g_return_val_if_fail(plugin != NULL, FALSE);
+
+	return plugin->unloadable;
+}
+
 const gchar *
 gaim_plugin_get_id(const GaimPlugin *plugin) {
 	g_return_val_if_fail(plugin, NULL);
--- a/src/plugin.h	Tue Jan 03 11:42:51 2006 +0000
+++ b/src/plugin.h	Tue Jan 03 12:03:02 2006 +0000
@@ -125,6 +125,7 @@
 	char *error;
 	void *ipc_data;                        /**< IPC data.                 */
 	void *extra;                           /**< Plugin-specific data.     */
+	gboolean unloadable;                   /**< Unloadable                */
 };
 
 #define GAIM_PLUGIN_LOADER_INFO(plugin) \
@@ -285,11 +286,25 @@
  *
  * @param plugin The plugin.
  *
- * @return TRUE if loaded, or FALSE otherwise.
+ * @return @c TRUE if loaded, or @c FALSE otherwise.
  */
 gboolean gaim_plugin_is_loaded(const GaimPlugin *plugin);
 
 /**
+ * Returns whether or not a plugin is unloadable.
+ *
+ * If this returns @c TRUE, the plugin is guaranteed to not
+ * be loadable. However, a return value of @c FALSE does not
+ * guarantee the plugin is loadable.
+ *
+ * @param plugin The plugin.
+ *
+ * @return @c TRUE if the plugin is known to be unloadable,\
+ *         @c FALSE otherwise
+ */
+gboolean gaim_plugin_is_unloadable(const GaimPlugin *plugin);
+
+/**
  * Returns a plugin's id.
  *
  * @param plugin The plugin.