+ g_assert(menu);
+
+ self = g_new0(ObMenuEntry, 1);
+ self->ref = 1;
+ self->type = type;
+ self->menu = menu;
+ self->id = id;
+
+ switch (type) {
+ case OB_MENU_ENTRY_TYPE_NORMAL:
+ self->data.normal.enabled = TRUE;
+ break;
+ case OB_MENU_ENTRY_TYPE_SUBMENU:
+ case OB_MENU_ENTRY_TYPE_SEPARATOR:
+ break;
+ }
+
+ return self;
+}
+
+void menu_entry_ref(ObMenuEntry *self)
+{
+ ++self->ref;
+}
+
+void menu_entry_unref(ObMenuEntry *self)
+{
+ if (self && --self->ref == 0) {
+ switch (self->type) {
+ case OB_MENU_ENTRY_TYPE_NORMAL:
+ g_free(self->data.normal.label);
+ while (self->data.normal.actions) {
+ action_unref(self->data.normal.actions->data);
+ self->data.normal.actions =
+ g_slist_delete_link(self->data.normal.actions,
+ self->data.normal.actions);
+ }
+ break;
+ case OB_MENU_ENTRY_TYPE_SUBMENU:
+ g_free(self->data.submenu.name);
+ break;
+ case OB_MENU_ENTRY_TYPE_SEPARATOR:
+ break;
+ }
+
+ g_free(self);
+ }
+}
+
+void menu_clear_entries(ObMenu *self)
+{
+#ifdef DEBUG
+ /* assert that the menu isn't visible */
+ {
+ GList *it;
+ ObMenuFrame *f;
+
+ for (it = menu_frame_visible; it; it = g_list_next(it)) {
+ f = it->data;
+ g_assert(f->menu != self);
+ }
+ }
+#endif
+
+ while (self->entries) {
+ menu_entry_unref(self->entries->data);
+ self->entries = g_list_delete_link(self->entries, self->entries);
+ }
+ self->more_menu->entries = self->entries; /* keep it in sync */
+}
+
+void menu_entry_remove(ObMenuEntry *self)
+{
+ self->menu->entries = g_list_remove(self->menu->entries, self);
+ menu_entry_unref(self);
+}
+
+ObMenuEntry* menu_add_normal(ObMenu *self, gint id, const gchar *label,
+ GSList *actions, gboolean allow_shortcut)
+{
+ ObMenuEntry *e;
+
+ e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_NORMAL, id);
+ e->data.normal.actions = actions;
+
+ menu_entry_set_label(e, label, allow_shortcut);
+
+ self->entries = g_list_append(self->entries, e);
+ self->more_menu->entries = self->entries; /* keep it in sync */
+ return e;
+}
+
+ObMenuEntry* menu_get_more(ObMenu *self, guint show_from)
+{
+ ObMenuEntry *e;
+ e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_SUBMENU, -1);
+ /* points to itself */
+ e->data.submenu.name = g_strdup(self->name);
+ e->data.submenu.submenu = self;
+ e->data.submenu.show_from = show_from;
+ return e;
+}
+
+ObMenuEntry* menu_add_submenu(ObMenu *self, gint id, const gchar *submenu)
+{
+ ObMenuEntry *e;
+
+ e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_SUBMENU, id);
+ e->data.submenu.name = g_strdup(submenu);
+
+ self->entries = g_list_append(self->entries, e);
+ self->more_menu->entries = self->entries; /* keep it in sync */
+ return e;
+}
+
+ObMenuEntry* menu_add_separator(ObMenu *self, gint id, const gchar *label)
+{
+ ObMenuEntry *e;
+
+ e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_SEPARATOR, id);
+
+ menu_entry_set_label(e, label, FALSE);
+
+ self->entries = g_list_append(self->entries, e);
+ self->more_menu->entries = self->entries; /* keep it in sync */
+ return e;
+}
+
+void menu_set_show_func(ObMenu *self, ObMenuShowFunc func)
+{
+ self->show_func = func;
+ self->more_menu->show_func = func; /* keep it in sync */
+}
+
+void menu_set_hide_func(ObMenu *self, ObMenuHideFunc func)
+{
+ self->hide_func = func;
+ self->more_menu->hide_func = func; /* keep it in sync */
+}
+
+void menu_set_update_func(ObMenu *self, ObMenuUpdateFunc func)
+{
+ self->update_func = func;
+ self->more_menu->update_func = func; /* keep it in sync */
+}
+
+void menu_set_execute_func(ObMenu *self, ObMenuExecuteFunc func)
+{
+ self->execute_func = func;
+ self->more_menu->execute_func = func; /* keep it in sync */
+}
+
+void menu_set_destroy_func(ObMenu *self, ObMenuDestroyFunc func)
+{
+ self->destroy_func = func;
+ self->more_menu->destroy_func = func; /* keep it in sync */
+}
+
+void menu_set_place_func(ObMenu *self, ObMenuPlaceFunc func)
+{
+ self->place_func = func;
+ self->more_menu->place_func = func; /* keep it in sync */
+}
+
+ObMenuEntry* menu_find_entry_id(ObMenu *self, gint id)
+{
+ ObMenuEntry *ret = NULL;
+ GList *it;
+
+ for (it = self->entries; it; it = g_list_next(it)) {
+ ObMenuEntry *e = it->data;
+
+ if (e->id == id) {
+ ret = e;
+ break;
+ }
+ }
+ return ret;
+}
+
+void menu_find_submenus(ObMenu *self)
+{
+ GList *it;
+
+ for (it = self->entries; it; it = g_list_next(it)) {
+ ObMenuEntry *e = it->data;
+
+ if (e->type == OB_MENU_ENTRY_TYPE_SUBMENU)
+ e->data.submenu.submenu = menu_from_name(e->data.submenu.name);
+ }
+}
+
+void menu_entry_set_label(ObMenuEntry *self, const gchar *label,
+ gboolean allow_shortcut)
+{
+ switch (self->type) {
+ case OB_MENU_ENTRY_TYPE_SEPARATOR:
+ g_free(self->data.separator.label);
+ self->data.separator.label = g_strdup(label);
+ break;
+ case OB_MENU_ENTRY_TYPE_NORMAL:
+ g_free(self->data.normal.label);
+ self->data.normal.shortcut =
+ parse_shortcut(label, allow_shortcut, &self->data.normal.label,
+ &self->data.normal.shortcut_position);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+void menu_show_all_shortcuts(ObMenu *self, gboolean show)
+{
+ self->show_all_shortcuts = show;