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
, 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 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
, 0, name
);
96 void menu_destroy_hash_value(ObMenu
*self
)
98 /* XXX make sure its not visible */
99 menu_clear_entries_internal(self
);
104 void menu_startup(ObParseInst
*i
)
106 menu_hash
= g_hash_table_new_full(g_str_hash
, g_str_equal
, NULL
,
107 (GDestroyNotify
)menu_destroy_hash_value
);
112 menu_frame_hide_all();
113 g_hash_table_destroy(menu_hash
);
120 ObMenuParseState parse_state
;
124 gboolean loaded
= FALSE
;
128 if (config_menu_path
)
130 parse_load(config_menu_path
, "openbox_menu", &doc
, &node
)))
131 g_warning("Failed to load menu from '%s'", config_menu_path
);
133 p
= g_build_filename(g_get_home_dir(), ".openbox", "menu", NULL
);
135 parse_load(p
, "openbox_menu", &doc
, &node
)))
136 g_warning("Failed to load menu from '%s'", p
);
140 p
= g_build_filename(RCDIR
, "menu", NULL
);
142 parse_load(p
, "openbox_menu", &doc
, &node
)))
143 g_warning("Failed to load menu from '%s'", p
);
148 parse_state
.menus
= NULL
;
150 parse_register(i
, "menu", parse_menu
, &parse_state
);
151 parse_register(i
, "item", parse_menu_item
, &parse_state
);
152 parse_register(i
, "separator", parse_menu_separator
, &parse_state
);
153 parse_tree(i
, doc
, node
->xmlChildrenNode
);
159 gboolean
menu_new(gchar
*name
, gchar
*title
, gpointer data
)
163 if (g_hash_table_lookup(menu_hash
, name
)) return FALSE
;
165 self
= g_new0(ObMenu
, 1);
166 self
->name
= g_strdup(name
);
167 self
->title
= g_strdup(title
);
170 g_hash_table_insert(menu_hash
, self
->name
, self
);
175 void menu_free(gchar
*name
)
179 if (!(self
= menu_from_name(name
))) return;
180 g_hash_table_remove(menu_hash
, self
->name
);
183 void menu_show(gchar
*name
, gint x
, gint y
, ObClient
*client
)
188 if (!(self
= menu_from_name(name
))) return;
190 frame
= menu_frame_new(self
, client
);
191 menu_frame_move(frame
, x
, y
);
192 menu_frame_show(frame
, NULL
);
195 static ObMenuEntry
* menu_entry_new(ObMenu
*menu
, ObMenuEntryType type
, gint id
)
201 self
= g_new0(ObMenuEntry
, 1);
205 self
->enabled
= TRUE
;
209 static void menu_entry_free(ObMenuEntry
*self
)
212 switch (self
->type
) {
213 case OB_MENU_ENTRY_TYPE_NORMAL
:
214 g_free(self
->data
.normal
.label
);
215 while (self
->data
.normal
.actions
) {
216 action_free(self
->data
.normal
.actions
->data
);
217 self
->data
.normal
.actions
=
218 g_slist_delete_link(self
->data
.normal
.actions
,
219 self
->data
.normal
.actions
);
222 case OB_MENU_ENTRY_TYPE_SUBMENU
:
223 case OB_MENU_ENTRY_TYPE_SEPARATOR
:
231 void menu_clear_entries(gchar
*name
)
235 if (!(self
= menu_from_name(name
))) return;
237 menu_clear_entries_internal(self
);
240 static void menu_clear_entries_internal(ObMenu
*self
)
242 /* XXX assert that the menu isn't visible */
244 while (self
->entries
) {
245 menu_entry_free(self
->entries
->data
);
246 self
->entries
= g_list_delete_link(self
->entries
, self
->entries
);
250 void menu_add_normal(gchar
*name
, gint id
, gchar
*label
, GSList
*actions
)
255 if (!(self
= menu_from_name(name
))) return;
257 e
= menu_entry_new(self
, OB_MENU_ENTRY_TYPE_NORMAL
, id
);
258 e
->data
.normal
.label
= g_strdup(label
);
259 e
->data
.normal
.actions
= actions
;
261 self
->entries
= g_list_append(self
->entries
, e
);
264 void menu_add_submenu(gchar
*name
, gint id
, gchar
*submenu
)
269 if (!(self
= menu_from_name(name
))) return;
270 if (!(sub
= menu_from_name(submenu
))) return;
272 e
= menu_entry_new(self
, OB_MENU_ENTRY_TYPE_SUBMENU
, id
);
273 e
->data
.submenu
.submenu
= sub
;
275 self
->entries
= g_list_append(self
->entries
, e
);
278 void menu_add_separator(gchar
*name
, gint id
)
283 if (!(self
= menu_from_name(name
))) return;
285 e
= menu_entry_new(self
, OB_MENU_ENTRY_TYPE_SEPARATOR
, id
);
287 self
->entries
= g_list_append(self
->entries
, e
);
290 void menu_set_update_func(gchar
*name
, ObMenuUpdateFunc func
)
294 if (!(self
= menu_from_name(name
))) return;
295 self
->update_func
= func
;
298 ObMenuEntry
* menu_find_entry_id(ObMenu
*self
, gint id
)
300 ObMenuEntry
*ret
= NULL
;
303 for (it
= self
->entries
; it
; it
= g_list_next(it
)) {
304 ObMenuEntry
*e
= it
->data
;