/* Eye Of Mate - EOM Plugin Manager * * Copyright (C) 2007 The Free Software Foundation * * Author: Lucas Rocha * * Based on gedit code (gedit/gedit-module.c) by: * - Paolo Maggi * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H #include #endif #include #include "eom-plugin-engine.h" #include "eom-plugin.h" #include "eom-module.h" #include "eom-debug.h" #include "eom-application.h" #include "eom-config-keys.h" #include "eom-util.h" #include #include #include #define USER_EOM_PLUGINS_LOCATION "plugins/" #define PLUGIN_EXT ".eom-plugin" typedef enum { EOM_PLUGIN_LOADER_C, } EomPluginLoader; struct _EomPluginInfo { gchar *file; gchar *location; EomPluginLoader loader; GTypeModule *module; gchar *name; gchar *desc; gchar *icon_name; gchar **authors; gchar *copyright; gchar *website; EomPlugin *plugin; gint active : 1; /* A plugin is unavailable if it is not possible to activate it due to an error loading the plugin module */ gint available : 1; }; static void eom_plugin_engine_active_plugins_changed (GSettings *settings, gchar *key, gpointer user_data); static GList *eom_plugins_list = NULL; static GSettings *eom_plugin_engine_settings = NULL; static GSList *active_plugins = NULL; static void eom_plugin_info_free (EomPluginInfo *info) { if (info->plugin != NULL) { eom_debug_message (DEBUG_PLUGINS, "Unref plugin %s", info->name); g_object_unref (info->plugin); } g_free (info->file); g_free (info->location); g_free (info->name); g_free (info->desc); g_free (info->icon_name); g_free (info->website); g_free (info->copyright); g_strfreev (info->authors); g_free (info); } static EomPluginInfo * eom_plugin_engine_load (const gchar *file) { EomPluginInfo *info; GKeyFile *plugin_file = NULL; gchar *str; g_return_val_if_fail (file != NULL, NULL); eom_debug_message (DEBUG_PLUGINS, "Loading plugin: %s", file); info = g_new0 (EomPluginInfo, 1); info->file = g_strdup (file); plugin_file = g_key_file_new (); if (!g_key_file_load_from_file (plugin_file, file, G_KEY_FILE_NONE, NULL)) { g_warning ("Bad plugin file: %s", file); goto error; } if (!g_key_file_has_key (plugin_file, "Eom Plugin", "IAge", NULL)) { eom_debug_message (DEBUG_PLUGINS, "IAge key does not exist in file: %s", file); goto error; } /* Check IAge=2 */ if (g_key_file_get_integer (plugin_file, "Eom Plugin", "IAge", NULL) != 2) { eom_debug_message (DEBUG_PLUGINS, "Wrong IAge in file: %s", file); goto error; } /* Get Location */ str = g_key_file_get_string (plugin_file, "Eom Plugin", "Module", NULL); if ((str != NULL) && (*str != '\0')) { info->location = str; } else { g_warning ("Could not find 'Module' in %s", file); goto error; } /* Get the loader for this plugin */ str = g_key_file_get_string (plugin_file, "Eom Plugin", "Loader", NULL); info->loader = EOM_PLUGIN_LOADER_C; g_free (str); /* Get Name */ str = g_key_file_get_locale_string (plugin_file, "Eom Plugin", "Name", NULL, NULL); if (str) { info->name = str; } else { g_warning ("Could not find 'Name' in %s", file); goto error; } /* Get Description */ str = g_key_file_get_locale_string (plugin_file, "Eom Plugin", "Description", NULL, NULL); if (str) { info->desc = str; } else { eom_debug_message (DEBUG_PLUGINS, "Could not find 'Description' in %s", file); } /* Get Icon */ str = g_key_file_get_locale_string (plugin_file, "Eom Plugin", "Icon", NULL, NULL); if (str) { info->icon_name = str; } else { eom_debug_message (DEBUG_PLUGINS, "Could not find 'Icon' in %s, " "using 'eom-plugin'", file); } /* Get Authors */ info->authors = g_key_file_get_string_list (plugin_file, "Eom Plugin", "Authors", NULL, NULL); if (info->authors == NULL) eom_debug_message (DEBUG_PLUGINS, "Could not find 'Authors' in %s", file); /* Get Copyright */ str = g_key_file_get_string (plugin_file, "Eom Plugin", "Copyright", NULL); if (str) { info->copyright = str; } else { eom_debug_message (DEBUG_PLUGINS, "Could not find 'Copyright' in %s", file); } /* Get Website */ str = g_key_file_get_string (plugin_file, "Eom Plugin", "Website", NULL); if (str) { info->website = str; } else { eom_debug_message (DEBUG_PLUGINS, "Could not find 'Website' in %s", file); } g_key_file_free (plugin_file); /* If we know nothing about the availability of the plugin, set it as available */ info->available = TRUE; return info; error: g_free (info->file); g_free (info->location); g_free (info->name); g_free (info); g_key_file_free (plugin_file); return NULL; } static gint compare_plugin_info (EomPluginInfo *info1, EomPluginInfo *info2) { return strcmp (info1->location, info2->location); } static void eom_plugin_engine_load_dir (const gchar *dir) { GError *error = NULL; GDir *d; const gchar *dirent; if (!g_file_test (dir, G_FILE_TEST_IS_DIR)) { return; } g_return_if_fail (eom_plugin_engine_settings != NULL); eom_debug_message (DEBUG_PLUGINS, "DIR: %s", dir); d = g_dir_open (dir, 0, &error); if (!d) { g_warning ("%s", error->message); g_error_free (error); return; } while ((dirent = g_dir_read_name (d))) { if (g_str_has_suffix (dirent, PLUGIN_EXT)) { gchar *plugin_file; EomPluginInfo *info; plugin_file = g_build_filename (dir, dirent, NULL); info = eom_plugin_engine_load (plugin_file); g_free (plugin_file); if (info == NULL) continue; /* If a plugin with this name has already been loaded * drop this one (user plugins override system plugins) */ if (g_list_find_custom (eom_plugins_list, info, (GCompareFunc)compare_plugin_info) != NULL) { g_warning ("Two or more plugins named '%s'. " "Only the first will be considered.\n", info->location); eom_plugin_info_free (info); continue; } /* Actually, the plugin will be activated when reactivate_all * will be called for the first time. */ info->active = (g_slist_find_custom (active_plugins, info->location, (GCompareFunc)strcmp) != NULL); eom_plugins_list = g_list_prepend (eom_plugins_list, info); eom_debug_message (DEBUG_PLUGINS, "Plugin %s loaded", info->name); } } eom_plugins_list = g_list_reverse (eom_plugins_list); g_dir_close (d); } static void eom_plugin_engine_load_all (void) { gchar *pdir; pdir = g_build_filename (eom_util_dot_dir (), USER_EOM_PLUGINS_LOCATION, NULL); /* Load user's plugins */ eom_plugin_engine_load_dir (pdir); g_free (pdir); /* Load system plugins */ eom_plugin_engine_load_dir (EOM_PLUGIN_DIR "/"); } static void eom_plugin_engine_get_active_plugins (void) { gchar **array; gint i; active_plugins = NULL; array = g_settings_get_strv (eom_plugin_engine_settings, EOM_CONF_PLUGINS_ACTIVE_PLUGINS); if (array != NULL) { for (i = 0; array[i]; i++) { active_plugins = g_slist_append (active_plugins, g_strdup (array[i])); } } g_strfreev (array); } gboolean eom_plugin_engine_init (void) { eom_debug (DEBUG_PLUGINS); g_return_val_if_fail (eom_plugins_list == NULL, FALSE); if (!g_module_supported ()) { g_warning ("eom is not able to initialize the plugins engine."); return FALSE; } eom_plugin_engine_settings = g_settings_new (EOM_CONF_PLUGINS); g_signal_connect (eom_plugin_engine_settings, "changed::" EOM_CONF_PLUGINS_ACTIVE_PLUGINS, G_CALLBACK (eom_plugin_engine_active_plugins_changed), NULL); eom_plugin_engine_get_active_plugins (); eom_plugin_engine_load_all (); return TRUE; } void eom_plugin_engine_shutdown (void) { GList *pl; eom_debug (DEBUG_PLUGINS); g_return_if_fail (eom_plugin_engine_settings != NULL); for (pl = eom_plugins_list; pl; pl = pl->next) { EomPluginInfo *info = (EomPluginInfo*) pl->data; eom_plugin_info_free (info); } g_slist_foreach (active_plugins, (GFunc)g_free, NULL); g_slist_free (active_plugins); active_plugins = NULL; g_list_free (eom_plugins_list); eom_plugins_list = NULL; g_object_unref (eom_plugin_engine_settings); eom_plugin_engine_settings = NULL; } const GList * eom_plugin_engine_get_plugins_list (void) { eom_debug (DEBUG_PLUGINS); return eom_plugins_list; } static gboolean load_plugin_module (EomPluginInfo *info) { gchar *path; gchar *dirname; eom_debug (DEBUG_PLUGINS); g_return_val_if_fail (info != NULL, FALSE); g_return_val_if_fail (info->file != NULL, FALSE); g_return_val_if_fail (info->location != NULL, FALSE); g_return_val_if_fail (info->plugin == NULL, FALSE); g_return_val_if_fail (info->available, FALSE); switch (info->loader) { case EOM_PLUGIN_LOADER_C: dirname = g_path_get_dirname (info->file); g_return_val_if_fail (dirname != NULL, FALSE); path = g_module_build_path (dirname, info->location); g_free (dirname); g_return_val_if_fail (path != NULL, FALSE); info->module = G_TYPE_MODULE (eom_module_new (path)); g_free (path); break; default: g_return_val_if_reached (FALSE); } if (!g_type_module_use (info->module)) { switch (info->loader) { case EOM_PLUGIN_LOADER_C: g_warning ("Cannot load plugin '%s' since file '%s' cannot be read.", info->name, eom_module_get_path (EOM_MODULE (info->module))); break; default: g_return_val_if_reached (FALSE); } g_object_unref (G_OBJECT (info->module)); info->module = NULL; /* Mark plugin as unavailable and fails */ info->available = FALSE; return FALSE; } switch (info->loader) { case EOM_PLUGIN_LOADER_C: info->plugin = EOM_PLUGIN (eom_module_new_object (EOM_MODULE (info->module))); break; default: g_return_val_if_reached (FALSE); } g_type_module_unuse (info->module); eom_debug_message (DEBUG_PLUGINS, "End"); return TRUE; } static gboolean eom_plugin_engine_activate_plugin_real (EomPluginInfo *info) { gboolean res = TRUE; /* Plugin is not available, don't try to activate/load it */ if (!info->available) { return FALSE; } if (info->plugin == NULL) res = load_plugin_module (info); if (res) { const GList *wins = eom_application_get_windows (EOM_APP); while (wins != NULL) { eom_plugin_activate (info->plugin, EOM_WINDOW (wins->data)); wins = g_list_next (wins); } } else { g_warning ("Error activating plugin '%s'", info->name); } return res; } gboolean eom_plugin_engine_activate_plugin (EomPluginInfo *info) { eom_debug (DEBUG_PLUGINS); g_return_val_if_fail (info != NULL, FALSE); if (!info->available) return FALSE; if (info->active) return TRUE; if (eom_plugin_engine_activate_plugin_real (info)) { GSList *list; /* Update plugin state */ info->active = TRUE; list = active_plugins; while (list != NULL) { if (strcmp (info->location, (gchar *)list->data) == 0) { g_warning ("Plugin '%s' is already active.", info->name); return TRUE; } list = g_slist_next (list); } active_plugins = g_slist_insert_sorted (active_plugins, g_strdup (info->location), (GCompareFunc)strcmp); GArray *array; GSList *l; array = g_array_new (TRUE, TRUE, sizeof (gchar *)); for (l = active_plugins; l; l = l->next) { array = g_array_append_val (array, l->data); } g_settings_set_strv (eom_plugin_engine_settings, EOM_CONF_PLUGINS_ACTIVE_PLUGINS, (const gchar **) array->data); g_array_free (array, TRUE); return TRUE; } return FALSE; } static void eom_plugin_engine_deactivate_plugin_real (EomPluginInfo *info) { const GList *wins = eom_application_get_windows (EOM_APP); while (wins != NULL) { eom_plugin_deactivate (info->plugin, EOM_WINDOW (wins->data)); wins = g_list_next (wins); } } gboolean eom_plugin_engine_deactivate_plugin (EomPluginInfo *info) { gboolean res; GSList *list; eom_debug (DEBUG_PLUGINS); g_return_val_if_fail (info != NULL, FALSE); if (!info->active || !info->available) return TRUE; eom_plugin_engine_deactivate_plugin_real (info); /* Update plugin state */ info->active = FALSE; list = active_plugins; res = (list == NULL); while (list != NULL) { if (strcmp (info->location, (gchar *)list->data) == 0) { g_free (list->data); active_plugins = g_slist_delete_link (active_plugins, list); list = NULL; res = TRUE; } else { list = g_slist_next (list); } } if (!res) { g_warning ("Plugin '%s' is already deactivated.", info->name); return TRUE; } GArray *array; GSList *l; array = g_array_new (TRUE, TRUE, sizeof (gchar *)); for (l = active_plugins; l; l = l->next) { array = g_array_append_val (array, l->data); } g_settings_set_strv (eom_plugin_engine_settings, EOM_CONF_PLUGINS_ACTIVE_PLUGINS, (const gchar **) array->data); g_array_free (array, TRUE); return TRUE; } gboolean eom_plugin_engine_plugin_is_active (EomPluginInfo *info) { g_return_val_if_fail (info != NULL, FALSE); return (info->available && info->active); } gboolean eom_plugin_engine_plugin_is_available (EomPluginInfo *info) { g_return_val_if_fail (info != NULL, FALSE); return (info->available != FALSE); } static void reactivate_all (EomWindow *window) { GList *pl; eom_debug (DEBUG_PLUGINS); for (pl = eom_plugins_list; pl; pl = pl->next) { gboolean res = TRUE; EomPluginInfo *info = (EomPluginInfo*)pl->data; /* If plugin is not available, don't try to activate/load it */ if (info->available && info->active) { if (info->plugin == NULL) res = load_plugin_module (info); if (res) eom_plugin_activate (info->plugin, window); } } eom_debug_message (DEBUG_PLUGINS, "End"); } void eom_plugin_engine_update_plugins_ui (EomWindow *window, gboolean new_window) { GList *pl; eom_debug (DEBUG_PLUGINS); g_return_if_fail (EOM_IS_WINDOW (window)); if (new_window) reactivate_all (window); /* Updated ui of all the plugins that implement update_ui */ for (pl = eom_plugins_list; pl; pl = pl->next) { EomPluginInfo *info = (EomPluginInfo*)pl->data; if (!info->available || !info->active) continue; eom_debug_message (DEBUG_PLUGINS, "Updating UI of %s", info->name); eom_plugin_update_ui (info->plugin, window); } } gboolean eom_plugin_engine_plugin_is_configurable (EomPluginInfo *info) { eom_debug (DEBUG_PLUGINS); g_return_val_if_fail (info != NULL, FALSE); if ((info->plugin == NULL) || !info->active || !info->available) return FALSE; return eom_plugin_is_configurable (info->plugin); } void eom_plugin_engine_configure_plugin (EomPluginInfo *info, GtkWindow *parent) { GtkWidget *conf_dlg; GtkWindowGroup *wg; eom_debug (DEBUG_PLUGINS); g_return_if_fail (info != NULL); conf_dlg = eom_plugin_create_configure_dialog (info->plugin); g_return_if_fail (conf_dlg != NULL); gtk_window_set_transient_for (GTK_WINDOW (conf_dlg), parent); // Will return a default group if no group is set wg = gtk_window_get_group (parent); // For now assign a dedicated window group if it is // the default one until we know if this is really needed if (wg == gtk_window_get_group (NULL)) { wg = gtk_window_group_new (); gtk_window_group_add_window (wg, parent); } gtk_window_group_add_window (wg, GTK_WINDOW (conf_dlg)); gtk_window_set_modal (GTK_WINDOW (conf_dlg), TRUE); gtk_widget_show (conf_dlg); } static void eom_plugin_engine_active_plugins_changed (GSettings *settings, gchar *key, gpointer user_data) { GList *pl; gboolean to_activate; eom_debug (DEBUG_PLUGINS); g_return_if_fail (settings != NULL); g_return_if_fail (key != NULL); eom_plugin_engine_get_active_plugins (); for (pl = eom_plugins_list; pl; pl = pl->next) { EomPluginInfo *info = (EomPluginInfo*)pl->data; if (!info->available) continue; to_activate = (g_slist_find_custom (active_plugins, info->location, (GCompareFunc)strcmp) != NULL); if (!info->active && to_activate) { /* Activate plugin */ if (eom_plugin_engine_activate_plugin_real (info)) /* Update plugin state */ info->active = TRUE; } else { if (info->active && !to_activate) { eom_plugin_engine_deactivate_plugin_real (info); /* Update plugin state */ info->active = FALSE; } } } } const gchar * eom_plugin_engine_get_plugin_name (EomPluginInfo *info) { g_return_val_if_fail (info != NULL, NULL); return info->name; } const gchar * eom_plugin_engine_get_plugin_description (EomPluginInfo *info) { g_return_val_if_fail (info != NULL, NULL); return info->desc; } const gchar * eom_plugin_engine_get_plugin_icon_name (EomPluginInfo *info) { g_return_val_if_fail (info != NULL, NULL); /* Use the eom-plugin icon as a default if the plugin does not have its own */ if (info->icon_name != NULL && gtk_icon_theme_has_icon (gtk_icon_theme_get_default (), info->icon_name)) return info->icon_name; else return "eom-plugin"; } const gchar ** eom_plugin_engine_get_plugin_authors (EomPluginInfo *info) { g_return_val_if_fail (info != NULL, (const gchar **)NULL); return (const gchar **) info->authors; } const gchar * eom_plugin_engine_get_plugin_website (EomPluginInfo *info) { g_return_val_if_fail (info != NULL, NULL); return info->website; } const gchar * eom_plugin_engine_get_plugin_copyright (EomPluginInfo *info) { g_return_val_if_fail (info != NULL, NULL); return info->copyright; }