diff options
author | Michael Zucci <zucchi@src.gnome.org> | 2004-09-20 13:59:55 +0800 |
---|---|---|
committer | Michael Zucci <zucchi@src.gnome.org> | 2004-09-20 13:59:55 +0800 |
commit | ef6a3af717132e0750f226fa8a0ee0f3c98e19f0 (patch) | |
tree | f4ed25f7a247a8ccb855a8d940777e7a3d21a3e3 /e-util/e-menu.c | |
parent | 96111b1f1487ca3fe454b340a73ba927cc6bfb83 (diff) | |
download | gsoc2013-evolution-ef6a3af717132e0750f226fa8a0ee0f3c98e19f0.tar.gz gsoc2013-evolution-ef6a3af717132e0750f226fa8a0ee0f3c98e19f0.tar.zst gsoc2013-evolution-ef6a3af717132e0750f226fa8a0ee0f3c98e19f0.zip |
Merged notzed-eplugin-2-branch to head.
svn path=/trunk/; revision=27300
Diffstat (limited to 'e-util/e-menu.c')
-rw-r--r-- | e-util/e-menu.c | 901 |
1 files changed, 901 insertions, 0 deletions
diff --git a/e-util/e-menu.c b/e-util/e-menu.c new file mode 100644 index 0000000000..209f0816a0 --- /dev/null +++ b/e-util/e-menu.c @@ -0,0 +1,901 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Michael Zucchi <notzed@ximian.com> + * + * Copyright 2004 Ximian, Inc. (www.ximian.com) + * + * 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <stdlib.h> + +#include <glib.h> + +#include "e-menu.h" + +#include <e-util/e-icon-factory.h> + +#include <libgnome/gnome-i18n.h> +#include <bonobo/bonobo-ui-util.h> + +struct _EMenuFactory { + struct _EMenuFactory *next, *prev; + + char *menuid; + EMenuFactoryFunc factory; + void *factory_data; +}; + +struct _item_node { + struct _item_node *next; + + EMenuItem *item; + struct _menu_node *menu; +}; + +struct _menu_node { + struct _menu_node *next, *prev; + + EMenu *parent; + + GSList *items; + GSList *uis; + GSList *pixmaps; + + EMenuItemsFunc freefunc; + void *data; + + /* a copy of items wrapped in an item_node, for bonobo + * callback mapping */ + struct _item_node *menu; +}; + +struct _EMenuPrivate { + EDList menus; +}; + +static GObjectClass *em_parent; + +static void +em_init(GObject *o) +{ + EMenu *emp = (EMenu *)o; + struct _EMenuPrivate *p; + + p = emp->priv = g_malloc0(sizeof(struct _EMenuPrivate)); + + e_dlist_init(&p->menus); +} + +static void +em_finalise(GObject *o) +{ + EMenu *em = (EMenu *)o; + struct _EMenuPrivate *p = em->priv; + struct _menu_node *mnode; + + if (em->target) + e_menu_target_free(em, em->target); + g_free(em->menuid); + + while ((mnode = (struct _menu_node *)e_dlist_remhead(&p->menus))) { + struct _item_node *inode; + + if (mnode->freefunc) + mnode->freefunc(em, mnode->items, mnode->uis, mnode->pixmaps, mnode->data); + + inode = mnode->menu; + while (inode) { + struct _item_node *nnode = inode->next; + + g_free(inode); + inode = nnode; + } + + g_free(mnode); + } + + g_free(p); + + ((GObjectClass *)em_parent)->finalize(o); +} + +static void +em_target_free(EMenu *ep, EMenuTarget *t) +{ + g_free(t); + /* look funny but t has a reference to us */ + g_object_unref(ep); +} + +static void +em_class_init(GObjectClass *klass) +{ + printf("EMenu class init %p '%s'\n", klass, g_type_name(((GObjectClass *)klass)->g_type_class.g_type)); + + printf("e menu class init\n"); + klass->finalize = em_finalise; + ((EMenuClass *)klass)->target_free = em_target_free; +} + +static void +em_base_init(GObjectClass *klass) +{ + /* each class instance must have its own list, it isn't inherited */ + printf("%p: list init\n", klass); + e_dlist_init(&((EMenuClass *)klass)->factories); +} + +/** + * e_menu_get_type: + * + * Standard GObject type function. Used to subclass this type only. + * + * Return value: The EMenu object type. + **/ +GType +e_menu_get_type(void) +{ + static GType type = 0; + + if (type == 0) { + static const GTypeInfo info = { + sizeof(EMenuClass), + (GBaseInitFunc)em_base_init, NULL, + (GClassInitFunc)em_class_init, + NULL, NULL, + sizeof(EMenu), 0, + (GInstanceInitFunc)em_init + }; + em_parent = g_type_class_ref(G_TYPE_OBJECT); + type = g_type_register_static(G_TYPE_OBJECT, "EMenu", &info, 0); + } + + return type; +} + +/** + * e_menu_construct: + * @em: An instantiated but uninitislied EPopup. + * @menuid: The unique identifier for this menu. + * + * Construct the base menu instance based on the parameters. + * + * Return value: Returns @em. + **/ +EMenu *e_menu_construct(EMenu *em, const char *menuid) +{ + struct _EMenuFactory *f; + EMenuClass *klass; + + printf("constructing menu '%s'\n", menuid); + + klass = (EMenuClass *)G_OBJECT_GET_CLASS(em); + + printf(" class is %p '%s'\n", klass, g_type_name(((GObjectClass *)klass)->g_type_class.g_type)); + + em->menuid = g_strdup(menuid); + + /* setup the menu itself based on factories */ + f = (struct _EMenuFactory *)klass->factories.head; + if (f->next == NULL) { + printf("%p no factories registered on menu\n", klass); + } + + while (f->next) { + if (f->menuid == NULL + || !strcmp(f->menuid, em->menuid)) { + printf(" calling factory\n"); + f->factory(em, f->factory_data); + } + f = f->next; + } + + return em; +} + +/** + * e_menu_add_items: + * @emp: An initialised EMenu. + * @items: A list of EMenuItems or derived structures defining a group + * of menu items for this menu. + * @uifiles: A list of EMenuUIFile objects describing all ui files + * associated with the items. + * @pixmaps: A list of EMenuPixmap objects describing all pixmaps + * associated with the menus. + * @freefunc: If supplied, called when the menu items are no longer needed. + * @data: user-data passed to @freefunc and activate callbacks. + * + * Add new EMenuItems to the menu's. This may be called any number of + * times before the menu is first activated to hook onto any of the + * menu items defined for that view. + * + * Return value: A handle that can be passed to remove_items as required. + **/ +void * +e_menu_add_items(EMenu *emp, GSList *items, GSList *uifiles, GSList *pixmaps, EMenuItemsFunc freefunc, void *data) +{ + struct _menu_node *node; + GSList *l; + + node = g_malloc(sizeof(*node)); + node->parent = emp; + node->items = items; + node->uis = uifiles; + node->pixmaps = pixmaps; + node->freefunc = freefunc; + node->data = data; + + for (l=items;l;l=g_slist_next(l)) { + struct _item_node *inode = g_malloc0(sizeof(*inode)); + EMenuItem *item = l->data; + + inode->item = item; + inode->menu = node; + inode->next = node->menu; + node->menu = inode; + } + + for (l=pixmaps;l;l=g_slist_next(l)) { + EMenuPixmap *pixmap = l->data; + + if (pixmap->pixmap == NULL) { + GdkPixbuf *pixbuf; + + pixbuf = e_icon_factory_get_icon(pixmap->name, pixmap->size); + if (pixbuf == NULL) { + g_warning("Unable to load icon '%s'", pixmap->name); + } else { + pixmap->pixmap = bonobo_ui_util_pixbuf_to_xml(pixbuf); + g_object_unref(pixbuf); + } + } + } + + e_dlist_addtail(&emp->priv->menus, (EDListNode *)node); + + /* FIXME: add the menu's to a running menu if it is there? */ + + return (void *)node; +} + +/** + * e_menu_remove_items: + * @emp: + * @handle: + * + * Remove menu items previously added. + **/ +void +e_menu_remove_items(EMenu *emp, void *handle) +{ + struct _menu_node *node = handle; + struct _item_node *inode; + GSList *l; + + e_dlist_remove((EDListNode *)node); + + if (emp->uic) { + for (l = node->items;l;l=g_slist_next(l)) { + EMenuItem *item = l->data; + + bonobo_ui_component_remove_verb(emp->uic, item->verb); + } + } + + if (node->freefunc) + node->freefunc(emp, node->items, node->uis, node->pixmaps, node->data); + + inode = node->menu; + while (inode) { + struct _item_node *nnode = inode->next; + + g_free(inode); + inode = nnode; + } + + g_free(node); +} + +static void +em_activate_toggle(BonoboUIComponent *component, const char *path, Bonobo_UIComponent_EventType type, const char *state, void *data) +{ + struct _item_node *inode = data; + + if (type != Bonobo_UIComponent_STATE_CHANGED) + return; + + ((EMenuToggleActivateFunc)inode->item->activate)(inode->menu->parent, inode->item, state[0] != '0', inode->menu->data); +} + +static void +em_activate(BonoboUIComponent *uic, void *data, const char *cname) +{ + struct _item_node *inode = data; + + ((EMenuActivateFunc)inode->item->activate)(inode->menu->parent, inode->item, inode->menu->data); +} + +/** + * e_menu_activate: + * @em: An initialised EMenu. + * @uic: The BonoboUI component for this views menu's. + * @act: If %TRUE, then the control is being activated. + * + * This is called by the owner of the component, control, or view to + * pass on the activate or deactivate control signals. If the view is + * being activated then the callbacks and menu items are setup, + * otherwise they are removed. + * + * This should always be called in the strict sequence of activate, then + * deactivate, repeated any number of times. + **/ +void e_menu_activate(EMenu *em, struct _BonoboUIComponent *uic, int act) +{ + struct _EMenuPrivate *p = em->priv; + struct _menu_node *mw; + GSList *l; + + if (act) { + GArray *verbs; + int i; + + em->uic = uic; + + verbs = g_array_new(TRUE, FALSE, sizeof(BonoboUIVerb)); + for (mw = (struct _menu_node *)p->menus.head;mw->next;mw=mw->next) { + struct _item_node *inode; + + for (l = mw->uis; l ; l = g_slist_next(l)) { + EMenuUIFile *ui = l->data; + + bonobo_ui_util_set_ui(uic, ui->appdir, ui->filename, ui->appname, NULL); + } + + for (l = mw->pixmaps; l ; l = g_slist_next(l)) { + EMenuPixmap *pm = l->data; + + if (pm->pixmap) + bonobo_ui_component_set_prop(uic, pm->command, "pixmap", pm->pixmap, NULL); + } + + for (inode = mw->menu; inode; inode=inode->next) { + EMenuItem *item = inode->item; + BonoboUIVerb *verb; + + printf("adding menu verb '%s'\n", item->verb); + + switch (item->type & E_MENU_TYPE_MASK) { + case E_MENU_ITEM: + i = verbs->len; + verbs = g_array_set_size(verbs, i+1); + verb = &((BonoboUIVerb *)verbs->data)[i]; + + verb->cname = item->verb; + verb->cb = em_activate; + verb->user_data = inode; + break; + case E_MENU_TOGGLE: + bonobo_ui_component_set_prop(uic, item->path, "state", item->type & E_MENU_ACTIVE?"1":"0", NULL); + bonobo_ui_component_add_listener(uic, item->verb, em_activate_toggle, inode); + break; + } + } + } + + if (verbs->len) + bonobo_ui_component_add_verb_list(uic, (BonoboUIVerb *)verbs->data); + + g_array_free(verbs, TRUE); + } else { + for (mw = (struct _menu_node *)p->menus.head;mw->next;mw=mw->next) { + for (l = mw->items;l;l=g_slist_next(l)) { + EMenuItem *item = l->data; + + bonobo_ui_component_remove_verb(uic, item->verb); + } + } + + em->uic = NULL; + } +} + +/** + * e_menu_update_target: + * @em: An initialised EMenu. + * @tp: Target, after this call the menu owns the target. + * + * Change the target for the menu. Once the target is changed, the + * sensitivity state of the menu items managed by @em is re-evaluated + * and the physical menu's updated to reflect it. + * + * This is used by the owner of the menu and view to update the menu + * system based on user input or changed system state. + **/ +void e_menu_update_target(EMenu *em, void *tp) +{ + struct _EMenuPrivate *p = em->priv; + EMenuTarget *t = tp; + guint32 mask = ~0; + struct _menu_node *mw; + GSList *l; + + if (em->target && em->target != t) + e_menu_target_free(em, em->target); + + /* if we unset the target, should we disable/hide all the menu items? */ + em->target = t; + if (t == NULL) + return; + + mask = t->mask; + + /* canna do any more capt'n */ + if (em->uic == NULL) + return; + + for (mw = (struct _menu_node *)p->menus.head;mw->next;mw=mw->next) { + for (l = mw->items;l;l=g_slist_next(l)) { + EMenuItem *item = l->data; + int state; + + printf("checking item '%s' mask %08x against target %08x\n", item->verb, item->enable, mask); + + state = (item->enable & mask) == 0; + bonobo_ui_component_set_prop(em->uic, item->path, "sensitive", state?"1":"0", NULL); + /* visible? */ + } + } +} + +/* ********************************************************************** */ + +/** + * e_menu_class_add_factory: + * @klass: An EMenuClass type to which this factory applies. + * @menuid: The identifier of the menu for this factory, or NULL to be + * called on all menus. + * @func: An EMenuFactoryFunc callback. + * @data: Callback data for @func. + * + * Add a menu factory which will be called when the menu @menuid is + * created. The factory is free to add new items as it wishes to the + * menu provided in the callback. + * + * TODO: Make the menuid a pattern? + * + * Return value: A handle to the factory. + **/ +EMenuFactory * +e_menu_class_add_factory(EMenuClass *klass, const char *menuid, EMenuFactoryFunc func, void *data) +{ + struct _EMenuFactory *f = g_malloc0(sizeof(*f)); + + printf("%p adding factory '%s' to class '%s'\n", klass, menuid, g_type_name(((GObjectClass *)klass)->g_type_class.g_type)); + + f->menuid = g_strdup(menuid); + f->factory = func; + f->factory_data = data; + e_dlist_addtail(&klass->factories, (EDListNode *)f); + + /* setup the menu itself based on factories */ + { + struct _EMenuFactory *j; + + j = (struct _EMenuFactory *)klass->factories.head; + if (j->next == NULL) { + printf("%p no factories registered on menu???\n", klass); + } + } + + return f; +} + +/** + * e_menu_class_remove_factory: + * @klass: Class on which the factory was originally added. + * @f: Factory handle. + * + * Remove a popup factory. This must only be called once, and must + * only be called using a valid factory handle @f. After this call, + * @f is undefined. + **/ +void +e_menu_class_remove_factory(EMenuClass *klass, EMenuFactory *f) +{ + e_dlist_remove((EDListNode *)f); + g_free(f->menuid); + g_free(f); +} + +/** + * e_menu_target_new: + * @ep: An EMenu to which this target applies. + * @type: Target type, up to implementation. + * @size: Size of memory to allocate. Must be >= sizeof(EMenuTarget). + * + * Allocate a new menu target suitable for this class. @size is used + * to specify the actual target size, which may vary depending on the + * implementing class. + **/ +void *e_menu_target_new(EMenu *ep, int type, size_t size) +{ + EMenuTarget *t; + + g_assert(size >= sizeof(EMenuTarget)); + + t = g_malloc0(size); + t->menu = ep; + g_object_ref(ep); + t->type = type; + + return t; +} + +/** + * e_menu_target_free: + * @ep: EMenu on which the target was allocated. + * @o: Tareget to free. + * + * Free a target. + **/ +void +e_menu_target_free(EMenu *ep, void *o) +{ + EMenuTarget *t = o; + + ((EMenuClass *)G_OBJECT_GET_CLASS(ep))->target_free(ep, t); +} + +/* ********************************************************************** */ + +/* Main menu plugin handler */ + +/* NB: This has significant overlap with EPopupHook */ + +/* +<e-plugin + class="com.ximian.mail.plugin.popup:1.0" + id="com.ximian.mail.plugin.popup.item:1.0" + type="shlib" + location="/opt/gnome2/lib/camel/1.0/libcamelimap.so" + name="imap" + description="Main menu plugin"> + <hook class="com.ximian.evolution.bonobomenu:1.0"> + <menu id="any" target="select" view="com.ximian.mail"> + <ui file="ui file1"/> + <ui file="ui file2"/> + <pixmap command="command" pixmap="stockname" size="menu|button|small_toolbar|large_toolbar|dnd|dialog"/> + <item + type="item|toggle" + verb="verb" + enable="select_one" + visible="select_one" + activate="doactivate"/> + </menu> + </hook> + </extension> + +*/ + +static void *emph_parent_class; +#define emph ((EMenuHook *)eph) + +/* must have 1:1 correspondence with e-menu types in order */ +static const EPluginHookTargetKey emph_item_types[] = { + { "item", E_MENU_ITEM }, + { "toggle", E_MENU_TOGGLE }, + { "radio", E_MENU_RADIO }, + { 0 } +}; + +/* 1:1 with e-icon-factory sizes */ +static const EPluginHookTargetKey emph_pixmap_sizes[] = { + { "menu", 0 }, + { "button", 1}, + { "small_toolbar", 2}, + { "large_toolbar", 3}, + { "dnd", 4}, + { "dialog", 5}, + { 0 } +}; + +static void +emph_menu_activate(EMenu *em, EMenuItem *item, void *data) +{ + EMenuHook *hook = data; + + printf("invoking plugin hook '%s' %p\n", (char *)item->user_data, em->target); + + e_plugin_invoke(hook->hook.plugin, item->user_data, em->target); +} + +static void +emph_menu_toggle_activate(EMenu *em, EMenuItem *item, int state, void *data) +{ + EMenuHook *hook = data; + + /* FIXME: where does the toggle state go? */ + printf("invoking plugin hook '%s' %p\n", (char *)item->user_data, em->target); + + e_plugin_invoke(hook->hook.plugin, item->user_data, em->target); +} + +static void +emph_menu_factory(EMenu *emp, void *data) +{ + struct _EMenuHookMenu *menu = data; + + printf("menu factory, adding %d items\n", g_slist_length(menu->items)); + + if (menu->items) + e_menu_add_items(emp, menu->items, menu->uis, menu->pixmaps, NULL, menu->hook); +} + +static void +emph_free_item(struct _EMenuItem *item) +{ + g_free(item->path); + g_free(item->verb); + g_free(item->user_data); + g_free(item); +} + +static void +emph_free_ui(struct _EMenuUIFile *ui) +{ + g_free(ui->appdir); + g_free(ui->appname); + g_free(ui->filename); +} + +static void +emph_free_pixmap(struct _EMenuPixmap *pixmap) +{ + g_free(pixmap->command); + g_free(pixmap->name); + g_free(pixmap->pixmap); + g_free(pixmap); +} + +static void +emph_free_menu(struct _EMenuHookMenu *menu) +{ + g_slist_foreach(menu->items, (GFunc)emph_free_item, NULL); + g_slist_free(menu->items); + g_slist_foreach(menu->uis, (GFunc)emph_free_ui, NULL); + g_slist_free(menu->uis); + g_slist_foreach(menu->pixmaps, (GFunc)emph_free_pixmap, NULL); + g_slist_free(menu->pixmaps); + + g_free(menu->id); + g_free(menu); +} + +static struct _EMenuItem * +emph_construct_item(EPluginHook *eph, EMenuHookMenu *menu, xmlNodePtr root, EMenuHookTargetMap *map) +{ + struct _EMenuItem *item; + + printf(" loading menu item\n"); + item = g_malloc0(sizeof(*item)); + item->type = e_plugin_hook_id(root, emph_item_types, "type"); + item->path = e_plugin_xml_prop(root, "path"); + item->verb = e_plugin_xml_prop(root, "verb"); + item->visible = e_plugin_hook_mask(root, map->mask_bits, "visible"); + item->enable = e_plugin_hook_mask(root, map->mask_bits, "enable"); + item->user_data = e_plugin_xml_prop(root, "activate"); + if ((item->type & E_MENU_TYPE_MASK) == E_MENU_TOGGLE) + item->activate = G_CALLBACK(emph_menu_toggle_activate); + else + item->activate = G_CALLBACK(emph_menu_activate); + + if (item->type == -1 || item->user_data == NULL) + goto error; + + printf(" path=%s\n", item->path); + printf(" verb=%s\n", item->verb); + + return item; +error: + printf("error!\n"); + emph_free_item(item); + return NULL; +} + +static struct _EMenuPixmap * +emph_construct_pixmap(EPluginHook *eph, EMenuHookMenu *menu, xmlNodePtr root) +{ + struct _EMenuPixmap *pixmap; + + printf(" loading menu pixmap\n"); + pixmap = g_malloc0(sizeof(*pixmap)); + pixmap->command = e_plugin_xml_prop(root, "command"); + pixmap->name = e_plugin_xml_prop(root, "pixmap"); + pixmap->size = e_plugin_hook_id(root, emph_pixmap_sizes, "size"); + + if (pixmap->command == NULL || pixmap->name == NULL || pixmap->size == -1) + goto error; + + return pixmap; +error: + printf("error!\n"); + emph_free_pixmap(pixmap); + return NULL; +} + +static struct _EMenuHookMenu * +emph_construct_menu(EPluginHook *eph, xmlNodePtr root) +{ + struct _EMenuHookMenu *menu; + xmlNodePtr node; + EMenuHookTargetMap *map; + EMenuHookClass *klass = (EMenuHookClass *)G_OBJECT_GET_CLASS(eph); + char *tmp; + + printf(" loading menu\n"); + menu = g_malloc0(sizeof(*menu)); + menu->hook = (EMenuHook *)eph; + + tmp = xmlGetProp(root, "target"); + if (tmp == NULL) + goto error; + map = g_hash_table_lookup(klass->target_map, tmp); + xmlFree(tmp); + if (map == NULL) + goto error; + + menu->target_type = map->id; + menu->id = e_plugin_xml_prop(root, "id"); + node = root->children; + while (node) { + if (0 == strcmp(node->name, "item")) { + struct _EMenuItem *item; + + item = emph_construct_item(eph, menu, node, map); + if (item) + menu->items = g_slist_append(menu->items, item); + } else if (0 == strcmp(node->name, "ui")) { + tmp = xmlGetProp(node, "file"); + if (tmp) { + EMenuUIFile *ui = g_malloc0(sizeof(*ui)); + + ui->filename = tmp; + ui->appdir = g_strdup("/tmp"); + ui->appname = g_strdup("Evolution"); + menu->uis = g_slist_append(menu->uis, ui); + } + } else if (0 == strcmp(node->name, "pixmap")) { + struct _EMenuPixmap *pixmap; + + pixmap = emph_construct_pixmap(eph, menu, node); + if (pixmap) + menu->pixmaps = g_slist_append(menu->pixmaps, pixmap); + } + node = node->next; + } + + return menu; +error: + printf("error loading menu hook\n"); + emph_free_menu(menu); + return NULL; +} + +static int +emph_construct(EPluginHook *eph, EPlugin *ep, xmlNodePtr root) +{ + xmlNodePtr node; + EMenuClass *klass; + + printf("loading menu hook\n"); + + if (((EPluginHookClass *)emph_parent_class)->construct(eph, ep, root) == -1) + return -1; + + klass = ((EMenuHookClass *)G_OBJECT_GET_CLASS(eph))->menu_class; + + node = root->children; + while (node) { + if (strcmp(node->name, "menu") == 0) { + struct _EMenuHookMenu *menu; + + menu = emph_construct_menu(eph, node); + if (menu) { + printf(" plugin adding factory %p\n", klass); + e_menu_class_add_factory(klass, menu->id, emph_menu_factory, menu); + emph->menus = g_slist_append(emph->menus, menu); + } + } + + node = node->next; + } + + eph->plugin = ep; + + return 0; +} + +static void +emph_finalise(GObject *o) +{ + EPluginHook *eph = (EPluginHook *)o; + + g_slist_foreach(emph->menus, (GFunc)emph_free_menu, NULL); + g_slist_free(emph->menus); + + ((GObjectClass *)emph_parent_class)->finalize(o); +} + +static void +emph_class_init(EPluginHookClass *klass) +{ + printf("EMenuHook class init %p '%s'\n", klass, g_type_name(((GObjectClass *)klass)->g_type_class.g_type)); + + ((GObjectClass *)klass)->finalize = emph_finalise; + klass->construct = emph_construct; + + /* this is actually an abstract implementation but list it anyway */ + klass->id = "com.ximian.evolution.bonobomenu:1.0"; + + ((EMenuHookClass *)klass)->target_map = g_hash_table_new(g_str_hash, g_str_equal); + ((EMenuHookClass *)klass)->menu_class = g_type_class_ref(e_menu_get_type()); +} + +/** + * e_menu_hook_get_type: + * + * Standard GObject function to get the object type. Used to subclass + * EMenuHook. + * + * Return value: The type of the menu hook class. + **/ +GType +e_menu_hook_get_type(void) +{ + static GType type = 0; + + if (!type) { + static const GTypeInfo info = { + sizeof(EMenuHookClass), NULL, NULL, (GClassInitFunc) emph_class_init, NULL, NULL, + sizeof(EMenuHook), 0, (GInstanceInitFunc) NULL, + }; + + emph_parent_class = g_type_class_ref(e_plugin_hook_get_type()); + type = g_type_register_static(e_plugin_hook_get_type(), "EMenuHook", &info, 0); + } + + return type; +} + +/** + * e_menu_hook_class_add_target_map: + * @klass: The derived EMenuHook class. + * @map: A map used to describe a single EMenuTarget for this class. + * + * Adds a target map to a concrete derived class of EMenu. The target + * map enumerates a single target type, and the enable mask bit names, + * so that the type can be loaded automatically by the EMenu class. + **/ +void e_menu_hook_class_add_target_map(EMenuHookClass *klass, const EMenuHookTargetMap *map) +{ + g_hash_table_insert(klass->target_map, (void *)map->type, (void *)map); +} |