+static GHashTable *menu_hash = NULL;
+static ObParseInst *menu_parse_inst;
+static ObMenuParseState menu_parse_state;
+
+static void menu_destroy_hash_value(ObMenu *self);
+static void parse_menu_item(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
+ gpointer data);
+static void parse_menu_separator(ObParseInst *i,
+ xmlDocPtr doc, xmlNodePtr node,
+ gpointer data);
+static void parse_menu(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
+ gpointer data);
+
+static gboolean menu_open(gchar *file, xmlDocPtr *doc, xmlNodePtr *node)
+{
+ gboolean loaded = TRUE;
+ gchar *p;
+
+ p = g_build_filename(g_get_home_dir(), ".openbox", file, NULL);
+ if (!parse_load(p, "openbox_menu", doc, node)) {
+ g_free(p);
+ p = g_build_filename(RCDIR, file, NULL);
+ if (!parse_load(p, "openbox_menu", doc, node)) {
+ g_free(p);
+ p = g_strdup(file);
+ if (!parse_load(p, "openbox_menu", doc, node)) {
+ g_warning("Failed to load menu from '%s'", file);
+ loaded = FALSE;
+ }
+ }
+ }
+ g_free(p);
+ return loaded;
+}
+
+void menu_startup()
+{
+ xmlDocPtr doc;
+ xmlNodePtr node;
+ gboolean loaded = FALSE;
+ GSList *it;
+
+ menu_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
+ (GDestroyNotify)menu_destroy_hash_value);
+
+ client_list_menu_startup();
+ client_menu_startup();
+
+ menu_parse_inst = parse_startup();
+
+ for (it = config_menu_files; it; it = g_slist_next(it)) {
+ if (menu_open(it->data, &doc, &node))
+ loaded = TRUE;
+
+ }
+ if (!loaded)
+ loaded = menu_open("menu", &doc, &node);
+
+ if (loaded) {
+ menu_parse_state.menus = NULL;
+
+ parse_register(menu_parse_inst, "menu", parse_menu, &menu_parse_state);
+ parse_register(menu_parse_inst, "item", parse_menu_item,
+ &menu_parse_state);
+ parse_register(menu_parse_inst, "separator",
+ parse_menu_separator, &menu_parse_state);
+ parse_tree(menu_parse_inst, doc, node->xmlChildrenNode);
+ xmlFreeDoc(doc);
+
+ g_assert(menu_parse_state.menus == NULL);
+ }
+}
+
+void menu_shutdown()
+{
+ parse_shutdown(menu_parse_inst);
+ menu_parse_inst = NULL;
+
+ menu_frame_hide_all();
+ g_hash_table_destroy(menu_hash);
+ menu_hash = NULL;
+}
+
+void menu_pipe_execute(ObMenu *self)
+{
+ xmlDocPtr doc;
+ xmlNodePtr node;
+ gchar *output;
+ GError *err = NULL;
+
+ if (!self->execute)
+ return;
+
+ if (!g_spawn_command_line_sync(self->execute, &output, NULL, NULL, &err))
+ {
+ g_warning("Failed to execute command for pipe-menu: %s", err->message);
+ g_error_free(err);
+ return;
+ }
+
+ if (parse_load_mem(output, strlen(output),
+ "openbox_pipe_menu", &doc, &node))
+ {
+ menu_clear_entries(self);
+
+ menu_parse_state.menus = g_slist_prepend(NULL, self);
+ parse_tree(menu_parse_inst, doc, node->xmlChildrenNode);
+ menu_parse_state.menus = g_slist_remove(menu_parse_state.menus, self);
+ xmlFreeDoc(doc);
+
+ g_assert(menu_parse_state.menus == NULL);
+ }
+}