13 #include "parser/parse.h"
15 GHashTable *menu_hash = NULL;
17 typedef struct _ObMenuParseState ObMenuParseState;
19 struct _ObMenuParseState
24 static void menu_clear_entries_internal(ObMenu *self);
26 static ObMenu* menu_from_name(gchar *name)
30 g_assert(name != NULL);
32 if (!(self = g_hash_table_lookup(menu_hash, name)))
33 g_warning("Attempted to access menu '%s' but it does not exist.",
38 static void parse_menu_item(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
41 ObMenuParseState *state = data;
45 if (parse_attr_string("label", node, &label)) {
48 for (node = node->xmlChildrenNode; node; node = node->next)
49 if (!xmlStrcasecmp(node->name, (const xmlChar*) "action"))
50 acts = g_slist_append(acts, action_parse(i, doc, node));
51 menu_add_normal(state->menus->data, 0, label, acts);
57 static void parse_menu_separator(ObParseInst *i,
58 xmlDocPtr doc, xmlNodePtr node,
61 ObMenuParseState *state = data;
64 menu_add_separator(state->menus->data, 0);
67 gboolean menu_open_plugin(ObParseInst *i, gchar *name, gchar *plugin)
71 if (plugin_open(plugin, i)) {
73 if (g_hash_table_lookup(menu_hash, name))
76 g_warning("Specified plugin '%s' did not provide the "
77 "menu '%s'", plugin, name);
82 static void parse_menu(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
85 ObMenuParseState *state = data;
86 gchar *name = NULL, *title = NULL, *plugin = NULL;
88 if (!parse_attr_string("id", node, &name))
91 if (!g_hash_table_lookup(menu_hash, name)) {
92 if (parse_attr_string("plugin", node, &plugin)) {
93 menu_open_plugin(i, name, plugin);
95 if (!parse_attr_string("label", node, &title))
98 if (menu_new(name, title, NULL)) {
99 state->menus = g_slist_prepend(state->menus, name);
100 parse_tree(i, doc, node->xmlChildrenNode);
101 state->menus = g_slist_delete_link(state->menus, state->menus);
107 menu_add_submenu(state->menus->data, 0, name);
116 void menu_destroy_hash_value(ObMenu *self)
118 /* XXX make sure its not visible */
120 if (self->destroy_func)
121 self->destroy_func(self, self->data);
123 menu_clear_entries_internal(self);
128 void menu_startup(ObParseInst *i)
130 menu_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
131 (GDestroyNotify)menu_destroy_hash_value);
136 menu_frame_hide_all();
137 g_hash_table_destroy(menu_hash);
144 ObMenuParseState parse_state;
148 gboolean loaded = FALSE;
152 if (config_menu_path)
154 parse_load(config_menu_path, "openbox_menu", &doc, &node)))
155 g_warning("Failed to load menu from '%s'", config_menu_path);
157 p = g_build_filename(g_get_home_dir(), ".openbox", "menu", NULL);
159 parse_load(p, "openbox_menu", &doc, &node)))
160 g_warning("Failed to load menu from '%s'", p);
164 p = g_build_filename(RCDIR, "menu", NULL);
166 parse_load(p, "openbox_menu", &doc, &node)))
167 g_warning("Failed to load menu from '%s'", p);
172 parse_state.menus = NULL;
174 parse_register(i, "menu", parse_menu, &parse_state);
175 parse_register(i, "item", parse_menu_item, &parse_state);
176 parse_register(i, "separator", parse_menu_separator, &parse_state);
177 parse_tree(i, doc, node->xmlChildrenNode);
183 gboolean menu_new(gchar *name, gchar *title, gpointer data)
187 if (g_hash_table_lookup(menu_hash, name)) return FALSE;
189 self = g_new0(ObMenu, 1);
190 self->name = g_strdup(name);
191 self->title = g_strdup(title);
194 g_hash_table_insert(menu_hash, self->name, self);
199 void menu_free(gchar *name)
203 if (!(self = menu_from_name(name))) return;
204 g_hash_table_remove(menu_hash, self->name);
207 void menu_show(gchar *name, gint x, gint y, ObClient *client)
212 if (!(self = menu_from_name(name))) return;
214 frame = menu_frame_new(self, client);
215 menu_frame_move(frame, x, y);
216 menu_frame_show(frame, NULL);
219 static ObMenuEntry* menu_entry_new(ObMenu *menu, ObMenuEntryType type, gint id)
225 self = g_new0(ObMenuEntry, 1);
231 case OB_MENU_ENTRY_TYPE_NORMAL:
232 self->data.normal.enabled = TRUE;
234 case OB_MENU_ENTRY_TYPE_SUBMENU:
235 case OB_MENU_ENTRY_TYPE_SEPARATOR:
242 static void menu_entry_free(ObMenuEntry *self)
245 switch (self->type) {
246 case OB_MENU_ENTRY_TYPE_NORMAL:
247 g_free(self->data.normal.label);
248 while (self->data.normal.actions) {
249 action_free(self->data.normal.actions->data);
250 self->data.normal.actions =
251 g_slist_delete_link(self->data.normal.actions,
252 self->data.normal.actions);
255 case OB_MENU_ENTRY_TYPE_SUBMENU:
256 g_free(self->data.submenu.name);
258 case OB_MENU_ENTRY_TYPE_SEPARATOR:
266 void menu_clear_entries(gchar *name)
270 if (!(self = menu_from_name(name))) return;
272 menu_clear_entries_internal(self);
275 static void menu_clear_entries_internal(ObMenu *self)
277 /* XXX assert that the menu isn't visible */
279 while (self->entries) {
280 menu_entry_free(self->entries->data);
281 self->entries = g_list_delete_link(self->entries, self->entries);
285 void menu_add_normal(gchar *name, gint id, gchar *label, GSList *actions)
290 if (!(self = menu_from_name(name))) return;
292 e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_NORMAL, id);
293 e->data.normal.label = g_strdup(label);
294 e->data.normal.actions = actions;
296 self->entries = g_list_append(self->entries, e);
299 void menu_add_submenu(gchar *name, gint id, gchar *submenu)
304 if (!(self = menu_from_name(name))) return;
306 e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_SUBMENU, id);
307 e->data.submenu.name = g_strdup(submenu);
309 self->entries = g_list_append(self->entries, e);
312 void menu_add_separator(gchar *name, gint id)
317 if (!(self = menu_from_name(name))) return;
319 e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_SEPARATOR, id);
321 self->entries = g_list_append(self->entries, e);
324 void menu_set_update_func(gchar *name, ObMenuUpdateFunc func)
328 if (!(self = menu_from_name(name))) return;
329 self->update_func = func;
332 void menu_set_execute_func(gchar *name, ObMenuExecuteFunc func)
336 if (!(self = menu_from_name(name))) return;
337 self->execute_func = func;
340 void menu_set_destroy_func(gchar *name, ObMenuDestroyFunc func)
344 if (!(self = menu_from_name(name))) return;
345 self->destroy_func = func;
348 ObMenuEntry* menu_find_entry_id(ObMenu *self, gint id)
350 ObMenuEntry *ret = NULL;
353 for (it = self->entries; it; it = g_list_next(it)) {
354 ObMenuEntry *e = it->data;
364 void menu_find_submenus(ObMenu *self)
368 for (it = self->entries; it; it = g_list_next(it)) {
369 ObMenuEntry *e = it->data;
371 if (e->type == OB_MENU_ENTRY_TYPE_SUBMENU)
372 e->data.submenu.submenu = menu_from_name(e->data.submenu.name);