13 #include "parser/parse.h"
15 static GHashTable *menu_hash = NULL;
17 ObParseInst *menu_parse_inst;
19 typedef struct _ObMenuParseState ObMenuParseState;
21 struct _ObMenuParseState
26 static void menu_clear_entries_internal(ObMenu *self);
28 static ObMenu* menu_from_name(gchar *name)
32 g_assert(name != NULL);
34 if (!(self = g_hash_table_lookup(menu_hash, name)))
35 g_warning("Attempted to access menu '%s' but it does not exist.",
40 static void parse_menu_item(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
43 ObMenuParseState *state = data;
47 if (parse_attr_string("label", node, &label)) {
50 for (node = node->xmlChildrenNode; node; node = node->next)
51 if (!xmlStrcasecmp(node->name, (const xmlChar*) "action"))
52 acts = g_slist_append(acts, action_parse(i, doc, node));
53 menu_add_normal(state->menus->data, 0, label, acts);
59 static void parse_menu_separator(ObParseInst *i,
60 xmlDocPtr doc, xmlNodePtr node,
63 ObMenuParseState *state = data;
66 menu_add_separator(state->menus->data, 0);
69 gboolean menu_open_plugin(ObParseInst *i, gchar *name, gchar *plugin)
73 if (plugin_open(plugin, i)) {
75 if (g_hash_table_lookup(menu_hash, name))
78 g_warning("Specified plugin '%s' did not provide the "
79 "menu '%s'", plugin, name);
84 static void parse_menu(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
87 ObMenuParseState *state = data;
88 gchar *name = NULL, *title = NULL, *plugin = NULL;
90 if (!parse_attr_string("id", node, &name))
93 if (!g_hash_table_lookup(menu_hash, name)) {
94 if (parse_attr_string("plugin", node, &plugin)) {
95 menu_open_plugin(i, name, plugin);
97 if (!parse_attr_string("label", node, &title))
100 if (menu_new(name, title, NULL)) {
101 state->menus = g_slist_prepend(state->menus, name);
102 parse_tree(i, doc, node->xmlChildrenNode);
103 state->menus = g_slist_delete_link(state->menus, state->menus);
109 menu_add_submenu(state->menus->data, 0, name);
118 void menu_destroy_hash_value(ObMenu *self)
120 /* XXX make sure its not visible */
122 if (self->destroy_func)
123 self->destroy_func(self, self->data);
125 menu_clear_entries_internal(self);
130 void menu_startup(ObParseInst *i)
132 menu_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
133 (GDestroyNotify)menu_destroy_hash_value);
134 menu_parse_inst = parse_startup();
139 parse_shutdown(menu_parse_inst);
140 menu_parse_inst = NULL;
142 menu_frame_hide_all();
143 g_hash_table_destroy(menu_hash);
147 gboolean menu_open(gchar *file, xmlDocPtr *doc, xmlNodePtr *node)
149 gboolean loaded = TRUE;
152 p = g_build_filename(g_get_home_dir(), ".openbox", file, NULL);
153 if (!parse_load(p, "openbox_menu", doc, node)) {
155 p = g_build_filename(RCDIR, file, NULL);
156 if (!parse_load(p, "openbox_menu", doc, node)) {
159 if (!parse_load(p, "openbox_menu", doc, node)) {
160 g_warning("Failed to load menu from '%s'", file);
171 ObMenuParseState parse_state;
174 gboolean loaded = FALSE;
177 for (it = config_menu_files; it; it = g_slist_next(it)) {
178 if (menu_open(it->data, &doc, &node))
183 loaded = menu_open("menu", &doc, &node);
186 parse_state.menus = NULL;
188 parse_register(menu_parse_inst, "menu", parse_menu, &parse_state);
189 parse_register(menu_parse_inst, "item", parse_menu_item, &parse_state);
190 parse_register(menu_parse_inst, "separator",
191 parse_menu_separator, &parse_state);
192 parse_tree(menu_parse_inst, doc, node->xmlChildrenNode);
197 ObMenu* menu_new(gchar *name, gchar *title, gpointer data)
201 /*if (g_hash_table_lookup(menu_hash, name)) return FALSE;*/
203 self = g_new0(ObMenu, 1);
204 self->name = g_strdup(name);
205 self->title = g_strdup(title);
208 g_hash_table_replace(menu_hash, self->name, self);
213 void menu_free(gchar *name)
217 if (!(self = menu_from_name(name))) return;
218 g_hash_table_remove(menu_hash, self->name);
221 void menu_show(gchar *name, gint x, gint y, ObClient *client)
226 if (!(self = menu_from_name(name))) return;
228 frame = menu_frame_new(self, client);
229 menu_frame_move(frame, x, y);
230 menu_frame_show(frame, NULL);
233 static ObMenuEntry* menu_entry_new(ObMenu *menu, ObMenuEntryType type, gint id)
239 self = g_new0(ObMenuEntry, 1);
245 case OB_MENU_ENTRY_TYPE_NORMAL:
246 self->data.normal.enabled = TRUE;
248 case OB_MENU_ENTRY_TYPE_SUBMENU:
249 case OB_MENU_ENTRY_TYPE_SEPARATOR:
256 static void menu_entry_free(ObMenuEntry *self)
259 switch (self->type) {
260 case OB_MENU_ENTRY_TYPE_NORMAL:
261 g_free(self->data.normal.label);
262 while (self->data.normal.actions) {
263 action_free(self->data.normal.actions->data);
264 self->data.normal.actions =
265 g_slist_delete_link(self->data.normal.actions,
266 self->data.normal.actions);
269 case OB_MENU_ENTRY_TYPE_SUBMENU:
270 g_free(self->data.submenu.name);
272 case OB_MENU_ENTRY_TYPE_SEPARATOR:
280 void menu_clear_entries(gchar *name)
284 if (!(self = menu_from_name(name))) return;
286 menu_clear_entries_internal(self);
289 static void menu_clear_entries_internal(ObMenu *self)
291 /* XXX assert that the menu isn't visible */
293 while (self->entries) {
294 menu_entry_free(self->entries->data);
295 self->entries = g_list_delete_link(self->entries, self->entries);
299 ObMenuEntry* menu_add_normal(gchar *name, gint id, gchar *label,
305 if (!(self = menu_from_name(name))) return;
307 e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_NORMAL, id);
308 e->data.normal.label = g_strdup(label);
309 e->data.normal.actions = actions;
311 self->entries = g_list_append(self->entries, e);
315 ObMenuEntry* menu_add_submenu(gchar *name, gint id, gchar *submenu)
320 if (!(self = menu_from_name(name))) return;
322 e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_SUBMENU, id);
323 e->data.submenu.name = g_strdup(submenu);
325 self->entries = g_list_append(self->entries, e);
329 ObMenuEntry* menu_add_separator(gchar *name, gint id)
334 if (!(self = menu_from_name(name))) return;
336 e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_SEPARATOR, id);
338 self->entries = g_list_append(self->entries, e);
342 void menu_set_update_func(gchar *name, ObMenuUpdateFunc func)
346 if (!(self = menu_from_name(name))) return;
347 self->update_func = func;
350 void menu_set_execute_func(gchar *name, ObMenuExecuteFunc func)
354 if (!(self = menu_from_name(name))) return;
355 self->execute_func = func;
358 void menu_set_destroy_func(gchar *name, ObMenuDestroyFunc func)
362 if (!(self = menu_from_name(name))) return;
363 self->destroy_func = func;
366 ObMenuEntry* menu_find_entry_id(ObMenu *self, gint id)
368 ObMenuEntry *ret = NULL;
371 for (it = self->entries; it; it = g_list_next(it)) {
372 ObMenuEntry *e = it->data;
382 void menu_find_submenus(ObMenu *self)
386 for (it = self->entries; it; it = g_list_next(it)) {
387 ObMenuEntry *e = it->data;
389 if (e->type == OB_MENU_ENTRY_TYPE_SUBMENU)
390 e->data.submenu.submenu = menu_from_name(e->data.submenu.name);