diff options
Diffstat (limited to 'e-util/e-plugin.c')
-rw-r--r-- | e-util/e-plugin.c | 280 |
1 files changed, 199 insertions, 81 deletions
diff --git a/e-util/e-plugin.c b/e-util/e-plugin.c index d7320ab2cf..a60aa7bafd 100644 --- a/e-util/e-plugin.c +++ b/e-util/e-plugin.c @@ -54,7 +54,7 @@ static GSList *ep_path; /* global table of plugins by plugin.id */ static GHashTable *ep_plugins; /* a table of GSLists of plugins by hook class for hooks not loadable yet */ -static GHashTable *ep_plugins_pending; +static GHashTable *ep_plugins_pending_hooks; /* list of all cached xml docs:struct _plugin_doc's */ static EDList ep_plugin_docs = E_DLIST_INITIALISER(ep_plugin_docs); /* gconf client */ @@ -71,8 +71,11 @@ struct _plugin_doc { struct _plugin_doc *next; struct _plugin_doc *prev; + char *filename; xmlDocPtr doc; - GSList *plugins; + + GSList *plugin_hooks; /* EPlugin objects with pending hooks */ + GSList *plugins; /* xmlNodePtr's of plugins with unknown type (mono,etc) */ }; static gboolean @@ -159,14 +162,14 @@ ep_construct(EPlugin *ep, xmlNodePtr root) GSList *l; char *oldclass; - if (ep_plugins_pending == NULL) - ep_plugins_pending = g_hash_table_new(g_str_hash, g_str_equal); - if (!g_hash_table_lookup_extended(ep_plugins_pending, class, (void **)&oldclass, (void **)&l)) { + if (ep_plugins_pending_hooks == NULL) + ep_plugins_pending_hooks = g_hash_table_new(g_str_hash, g_str_equal); + if (!g_hash_table_lookup_extended(ep_plugins_pending_hooks, class, (void **)&oldclass, (void **)&l)) { oldclass = class; l = NULL; } l = g_slist_prepend(l, ep); - g_hash_table_insert(ep_plugins_pending, oldclass, l); + g_hash_table_insert(ep_plugins_pending_hooks, oldclass, l); ep->hooks_pending = g_slist_prepend(ep->hooks_pending, node); } } else if (strcmp(node->name, "description") == 0) { @@ -281,6 +284,54 @@ e_plugin_get_type(void) return type; } +static EPlugin * +ep_load_plugin(xmlNodePtr root, struct _plugin_doc *pdoc) +{ + char *prop, *id; + EPluginClass *klass; + EPlugin *ep; + + id = e_plugin_xml_prop(root, "id"); + if (id == NULL) { + g_warning("Invalid e-plugin entry in '%s': no id", pdoc->filename); + return NULL; + } + + if (g_hash_table_lookup(ep_plugins, id)) { + g_warning("Plugin '%s' already defined", id); + g_free(id); + return NULL; + } + + prop = xmlGetProp(root, "type"); + if (prop == NULL) { + g_free(id); + g_warning("Invalid e-plugin entry in '%s': no type", pdoc->filename); + return NULL; + } + + /* If we can't find a plugin, add it to a pending list which is checked when a new type is registered */ + klass = g_hash_table_lookup(ep_types, prop); + if (klass == NULL) { + pd(printf("Delaying loading of plugin '%s' unknown type '%s'\n", id, prop)); + g_free(id); + xmlFree(prop); + pdoc->plugins = g_slist_prepend(pdoc->plugins, root); + return NULL; + } + xmlFree(prop); + + ep = g_object_new(G_TYPE_FROM_CLASS(klass), NULL); + ep->id = id; + ep->path = g_strdup(pdoc->filename); + ep->enabled = ep_check_enabled(id); + if (e_plugin_construct(ep, root) == -1) + e_plugin_enable(ep, FALSE); + g_hash_table_insert(ep_plugins, ep->id, ep); + + return ep; +} + static int ep_load(const char *filename) { @@ -305,60 +356,28 @@ ep_load(const char *filename) pdoc = g_malloc0(sizeof(*pdoc)); pdoc->doc = doc; - pdoc->plugins = NULL; + pdoc->filename = g_strdup(filename); for (root = root->children; root ; root = root->next) { if (strcmp(root->name, "e-plugin") == 0) { - char *prop, *id; - EPluginClass *klass; - - id = e_plugin_xml_prop(root, "id"); - if (id == NULL) { - g_warning("Invalid e-plugin entry in '%s': no id", filename); - goto fail; + ep = ep_load_plugin(root, pdoc); + if (ep) { + pdoc->plugin_hooks = g_slist_prepend(pdoc->plugin_hooks, ep); + cache |= (ep->hooks_pending != NULL); } - - if (g_hash_table_lookup(ep_plugins, id)) { - g_warning("Plugin '%s' already defined", id); - g_free(id); - continue; - } - - prop = xmlGetProp(root, "type"); - if (prop == NULL) { - g_free(id); - g_warning("Invalid e-plugin entry in '%s': no type", filename); - goto fail; - } - - klass = g_hash_table_lookup(ep_types, prop); - if (klass == NULL) { - g_warning("Can't find plugin type '%s' for plugin '%s'\n", prop, id); - g_free(id); - xmlFree(prop); - continue; - } - xmlFree(prop); - - ep = g_object_new(G_TYPE_FROM_CLASS(klass), NULL); - ep->id = id; - ep->path = g_strdup(filename); - ep->enabled = ep_check_enabled(id); - if (e_plugin_construct(ep, root) == -1) - e_plugin_enable(ep, FALSE); - g_hash_table_insert(ep_plugins, ep->id, ep); - pdoc->plugins = g_slist_prepend(pdoc->plugins, ep); - cache |= (ep->hooks_pending != NULL); + cache |= pdoc->plugins != NULL; } } res = 0; -fail: + if (cache) { pd(printf("Caching plugin description '%s' for unknown future hooks\n", filename)); e_dlist_addtail(&ep_plugin_docs, (EDListNode *)pdoc); } else { + pd(printf("freeing plugin description '%s', nothing uses it\n", filename)); xmlFreeDoc(pdoc->doc); + g_free(pdoc->filename); g_free(pdoc); } @@ -453,7 +472,6 @@ e_plugin_load_plugins(void) return 0; } - for (l = ep_path;l;l = g_slist_next(l)) { DIR *dir; struct dirent *d; @@ -495,6 +513,7 @@ void e_plugin_register_type(GType type) { EPluginClass *klass; + struct _plugin_doc *pdoc, *ndoc; if (ep_types == NULL) { ep_types = g_hash_table_new(g_str_hash, g_str_equal); @@ -509,6 +528,41 @@ e_plugin_register_type(GType type) pd(printf("register plugin type '%s'\n", klass->type)); g_hash_table_insert(ep_types, (void *)klass->type, klass); + + /* check for pending plugins */ + pdoc = (struct _plugin_doc *)ep_plugin_docs.head; + ndoc = pdoc->next; + while (ndoc) { + if (pdoc->plugins) { + GSList *l, *add = NULL; + + for (l=pdoc->plugins;l;l=g_slist_next(l)) { + xmlNodePtr root = l->data; + char *type; + + type = xmlGetProp(root, "type"); + if (!strcmp(type, klass->type)) + add = g_slist_append(add, l->data); + xmlFree(type); + } + + for (l=add;l;l=g_slist_next(l)) { + xmlNodePtr root = l->data; + EPlugin *ep; + + pdoc->plugins = g_slist_remove(pdoc->plugins, root); + ep = ep_load_plugin(root, pdoc); + if (ep) + pdoc->plugin_hooks = g_slist_prepend(pdoc->plugin_hooks, ep); + /* TODO: garbage collect plugin doc? */ + } + + g_slist_free(add); + } + + pdoc = ndoc; + ndoc = ndoc->next; + } } static void @@ -798,8 +852,10 @@ epl_construct(EPlugin *ep, xmlNodePtr root) epl->location = e_plugin_xml_prop(root, "location"); - if (epl->location == NULL) + if (epl->location == NULL) { + g_warning("Library plugin '%s' has no location", ep->id); return -1; + } /* If we're enabled, check for the load-on-startup property */ if (ep->enabled) { @@ -999,11 +1055,11 @@ e_plugin_hook_register_type(GType type) /* if we've already loaded a plugin that needed this hook but it didn't exist, re-load it now */ - if (ep_plugins_pending - && g_hash_table_lookup_extended(ep_plugins_pending, klass->id, (void **)&class, (void **)&plugins)) { + if (ep_plugins_pending_hooks + && g_hash_table_lookup_extended(ep_plugins_pending_hooks, klass->id, (void **)&class, (void **)&plugins)) { struct _plugin_doc *pdoc, *ndoc; - g_hash_table_remove(ep_plugins_pending, class); + g_hash_table_remove(ep_plugins_pending_hooks, class); g_free(class); for (l = plugins; l; l = g_slist_next(l)) { EPlugin *ep = l->data; @@ -1020,15 +1076,16 @@ e_plugin_hook_register_type(GType type) ndoc = pdoc->next; while (ndoc) { if (pdoc->doc) { - int cache = FALSE; + int cache = pdoc->plugins != NULL; - for (l=pdoc->plugins;l;l=g_slist_next(l)) + for (l=pdoc->plugin_hooks;!cache && l;l=g_slist_next(l)) cache |= (((EPlugin *)l->data)->hooks_pending != NULL); if (!cache) { - pd(printf("Gargabe collecting plugin description\n")); + pd(printf("Gargabe collecting plugin description '%s'\n", pdoc->filename)); e_dlist_remove((EDListNode *)pdoc); xmlFreeDoc(pdoc->doc); + g_free(pdoc->filename); g_free(pdoc); } } @@ -1127,39 +1184,100 @@ e_plugin_hook_id(xmlNodePtr root, const struct _EPluginHookTargetKey *map, const return ~0; } -#if 0 -/* - e-mail-format-handler - mime_type - target -*/ -struct _EMFormatPlugin { - EPlugin plugin; +/* ********************************************************************** */ +/* Plugin plugin */ - char *target; - char *mime_type; - struct _EMFormatHandler *(*get_handler)(void); -}; +static void *epth_parent_class; +#define epth ((EPluginTypeHook *)eph) -struct _EMFormatPluginClass { - EPluginClass plugin_class; -}; +static int +epth_load_plugin(void *d) +{ + EPluginHook *eph = d; + GType type; -#endif + epth->idle = 0; -#if 0 -void em_setup_plugins(void); + type = GPOINTER_TO_UINT(e_plugin_invoke(eph->plugin, epth->get_type, eph->plugin)); + if (type != 0) + e_plugin_register_type(type); -void -em_setup_plugins(void) + return FALSE; +} + +static int +epth_construct(EPluginHook *eph, EPlugin *ep, xmlNodePtr root) { - GType *e_plugin_mono_get_type(void); + xmlNodePtr node; - e_plugin_register_type(e_plugin_lib_get_type()); - e_plugin_register_type(e_plugin_mono_get_type()); + phd(printf("loading plugin hook\n")); - e_plugin_hook_register_type(em_popup_hook_get_type()); + if (((EPluginHookClass *)epth_parent_class)->construct(eph, ep, root) == -1) + return -1; - e_plugin_load_plugins("."); + node = root->children; + while (node) { + if (strcmp(node->name, "plugin-type") == 0) { + epth->get_type = e_plugin_xml_prop(node, "get-type"); + /* We need to run this in an idle handler, + * since at this point the parent EPlugin wont + * be fully initialised ... darn */ + if (epth->get_type) + epth->idle = g_idle_add(epth_load_plugin, epth); + else + g_warning("Plugin type plugin missing get-type callback"); + } + node = node->next; + } + + eph->plugin = ep; + + return 0; +} + +static void +epth_finalise(GObject *o) +{ + EPluginHook *eph = (EPluginHook *)o; + + if (epth->idle != 0) + g_source_remove(epth->idle); + + g_free(epth->get_type); + + ((GObjectClass *)eph_parent_class)->finalize((GObject *)o); +} + +static void +epth_class_init(EPluginHookClass *klass) +{ + ((GObjectClass *)klass)->finalize = epth_finalise; + klass->construct = epth_construct; + + klass->id = "org.gnome.evolution.plugin.type:1.0"; +} + +/** + * e_plugin_type_hook_get_type: + * + * Get the type for the plugin plugin hook. + * + * Return value: The type of the plugin type hook. + **/ +GType +e_plugin_type_hook_get_type(void) +{ + static GType type = 0; + + if (!type) { + static const GTypeInfo info = { + sizeof(EPluginTypeHookClass), NULL, NULL, (GClassInitFunc) epth_class_init, NULL, NULL, + sizeof(EPluginTypeHook), 0, (GInstanceInitFunc) NULL, + }; + + epth_parent_class = g_type_class_ref(e_plugin_hook_get_type()); + type = g_type_register_static(e_plugin_hook_get_type(), "EPluginTypeHook", &info, 0); + } + + return type; } -#endif |