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(doc, node));
51 menu_add_normal(state->menus->data, 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);
67 static void parse_menu(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
70 ObMenuParseState *state = data;
71 gchar *name = NULL, *title = NULL;
73 if (!parse_attr_string("id", node, &name))
76 if (!g_hash_table_lookup(menu_hash, name)) {
77 if (!parse_attr_string("label", node, &title))
80 if (menu_new(name, title, NULL)) {
81 state->menus = g_slist_prepend(state->menus, name);
82 parse_tree(i, doc, node->xmlChildrenNode);
83 state->menus = g_slist_delete_link(state->menus, state->menus);
88 menu_add_submenu(state->menus->data, name);
96 void menu_destroy_hash_value(ObMenu *self)
98 menu_clear_entries_internal(self);
103 void menu_startup(ObParseInst *i)
105 menu_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
106 (GDestroyNotify)menu_destroy_hash_value);
111 menu_frame_hide_all();
112 g_hash_table_destroy(menu_hash);
118 ObMenuParseState parse_state;
122 gboolean loaded = FALSE;
126 if (config_menu_path)
128 parse_load(config_menu_path, "openbox_menu", &doc, &node)))
129 g_warning("Failed to load menu from '%s'", config_menu_path);
131 p = g_build_filename(g_get_home_dir(), ".openbox", "menu", NULL);
133 parse_load(p, "openbox_menu", &doc, &node)))
134 g_warning("Failed to load menu from '%s'", p);
138 p = g_build_filename(RCDIR, "menu", NULL);
140 parse_load(p, "openbox_menu", &doc, &node)))
141 g_warning("Failed to load menu from '%s'", p);
146 parse_state.menus = NULL;
148 parse_register(i, "menu", parse_menu, &parse_state);
149 parse_register(i, "item", parse_menu_item, &parse_state);
150 parse_register(i, "separator", parse_menu_separator, &parse_state);
151 parse_tree(i, doc, node->xmlChildrenNode);
157 gboolean menu_new(gchar *name, gchar *title, gpointer data)
161 if (g_hash_table_lookup(menu_hash, name)) return FALSE;
163 self = g_new0(ObMenu, 1);
164 self->name = g_strdup(name);
165 self->title = g_strdup(title);
168 g_hash_table_insert(menu_hash, self->name, self);
173 void menu_show(gchar *name, gint x, gint y, ObClient *client)
178 if (!(self = menu_from_name(name))) return;
180 /* XXX update entries */
182 frame = menu_frame_new(self, client);
183 menu_frame_move(frame, x, y);
184 menu_frame_show(frame, NULL);
187 static ObMenuEntry* menu_entry_new(ObMenu *menu, ObMenuEntryType type)
193 self = g_new0(ObMenuEntry, 1);
196 self->enabled = TRUE;
200 static void menu_entry_free(ObMenuEntry *self)
203 switch (self->type) {
204 case OB_MENU_ENTRY_TYPE_NORMAL:
205 g_free(self->data.normal.label);
206 while (self->data.normal.actions) {
207 action_free(self->data.normal.actions->data);
208 self->data.normal.actions =
209 g_slist_delete_link(self->data.normal.actions,
210 self->data.normal.actions);
213 case OB_MENU_ENTRY_TYPE_SUBMENU:
214 case OB_MENU_ENTRY_TYPE_SEPARATOR:
222 void menu_clear_entries(gchar *name)
226 if (!(self = menu_from_name(name))) return;
228 menu_clear_entries_internal(self);
231 static void menu_clear_entries_internal(ObMenu *self)
233 /* XXX assert that the menu isn't visible */
235 while (self->entries) {
236 menu_entry_free(self->entries->data);
237 self->entries = g_list_delete_link(self->entries, self->entries);
241 void menu_add_normal(gchar *name, gchar *label, GSList *actions)
246 if (!(self = menu_from_name(name))) return;
248 e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_NORMAL);
249 e->data.normal.label = g_strdup(label);
250 e->data.normal.actions = actions;
252 self->entries = g_list_append(self->entries, e);
255 void menu_add_submenu(gchar *name, gchar *submenu)
260 if (!(self = menu_from_name(name))) return;
261 if (!(sub = menu_from_name(submenu))) return;
263 e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_SUBMENU);
264 e->data.submenu.submenu = sub;
266 self->entries = g_list_append(self->entries, e);
269 void menu_add_separator(gchar *name)
274 if (!(self = menu_from_name(name))) return;
276 e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_SEPARATOR);
278 self->entries = g_list_append(self->entries, e);