]> Dogcows Code - chaz/openbox/blob - openbox/menu.c
84c5ded54154a7096051af01143b979e55171604
[chaz/openbox] / openbox / menu.c
1 #include "debug.h"
2 #include "menu.h"
3 #include "openbox.h"
4 #include "stacking.h"
5 #include "client.h"
6 #include "grab.h"
7 #include "config.h"
8 #include "screen.h"
9 #include "menuframe.h"
10 #include "geom.h"
11 #include "plugin.h"
12 #include "misc.h"
13 #include "parser/parse.h"
14
15 static GHashTable *menu_hash = NULL;
16
17 ObParseInst *menu_parse_inst;
18
19 typedef struct _ObMenuParseState ObMenuParseState;
20
21 struct _ObMenuParseState
22 {
23 GSList *menus;
24 };
25
26 static void menu_clear_entries_internal(ObMenu *self);
27
28 static ObMenu* menu_from_name(gchar *name)
29 {
30 ObMenu *self = NULL;
31
32 g_assert(name != NULL);
33
34 if (!(self = g_hash_table_lookup(menu_hash, name)))
35 g_warning("Attempted to access menu '%s' but it does not exist.",
36 name);
37 return self;
38 }
39
40 static void parse_menu_item(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
41 gpointer data)
42 {
43 ObMenuParseState *state = data;
44 gchar *label;
45
46 if (state->menus) {
47 if (parse_attr_string("label", node, &label)) {
48 GSList *acts = NULL;
49
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);
54 g_free(label);
55 }
56 }
57 }
58
59 static void parse_menu_separator(ObParseInst *i,
60 xmlDocPtr doc, xmlNodePtr node,
61 gpointer data)
62 {
63 ObMenuParseState *state = data;
64
65 if (state->menus)
66 menu_add_separator(state->menus->data, 0);
67 }
68
69 gboolean menu_open_plugin(ObParseInst *i, gchar *name, gchar *plugin)
70 {
71 gboolean ret = FALSE;
72
73 if (plugin_open(plugin, i)) {
74 plugin_start(plugin);
75 if (g_hash_table_lookup(menu_hash, name))
76 ret = TRUE;
77 else
78 g_warning("Specified plugin '%s' did not provide the "
79 "menu '%s'", plugin, name);
80 }
81 return ret;
82 }
83
84 static void parse_menu(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
85 gpointer data)
86 {
87 ObMenuParseState *state = data;
88 gchar *name = NULL, *title = NULL, *plugin = NULL;
89
90 if (!parse_attr_string("id", node, &name))
91 goto parse_menu_fail;
92
93 if (!g_hash_table_lookup(menu_hash, name)) {
94 if (parse_attr_string("plugin", node, &plugin)) {
95 menu_open_plugin(i, name, plugin);
96 } else {
97 if (!parse_attr_string("label", node, &title))
98 goto parse_menu_fail;
99
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);
104 }
105 }
106 }
107
108 if (state->menus)
109 menu_add_submenu(state->menus->data, 0, name);
110
111 parse_menu_fail:
112 g_free(name);
113 g_free(title);
114 g_free(plugin);
115 }
116
117
118 void menu_destroy_hash_value(ObMenu *self)
119 {
120 /* XXX make sure its not visible */
121
122 if (self->destroy_func)
123 self->destroy_func(self, self->data);
124
125 menu_clear_entries_internal(self);
126 g_free(self->name);
127 g_free(self->title);
128 }
129
130 void menu_startup(ObParseInst *i)
131 {
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();
135 }
136
137 void menu_shutdown()
138 {
139 parse_shutdown(menu_parse_inst);
140 menu_parse_inst = NULL;
141
142 menu_frame_hide_all();
143 g_hash_table_destroy(menu_hash);
144 menu_hash = NULL;
145 }
146
147 gboolean menu_open(gchar *file, xmlDocPtr *doc, xmlNodePtr *node)
148 {
149 gboolean loaded = TRUE;
150 gchar *p;
151
152 p = g_build_filename(g_get_home_dir(), ".openbox", file, NULL);
153 if (!parse_load(p, "openbox_menu", doc, node)) {
154 g_free(p);
155 p = g_build_filename(RCDIR, file, NULL);
156 if (!parse_load(p, "openbox_menu", doc, node)) {
157 g_free(p);
158 p = g_strdup(file);
159 if (!parse_load(p, "openbox_menu", doc, node)) {
160 g_warning("Failed to load menu from '%s'", file);
161 loaded = FALSE;
162 }
163 }
164 }
165 g_free(p);
166 return loaded;
167 }
168
169 void menu_parse()
170 {
171 ObMenuParseState parse_state;
172 xmlDocPtr doc;
173 xmlNodePtr node;
174 gboolean loaded = FALSE;
175 GSList *it;
176
177 for (it = config_menu_files; it; it = g_slist_next(it)) {
178 if (menu_open(it->data, &doc, &node))
179 loaded = TRUE;
180
181 }
182 if (!loaded)
183 loaded = menu_open("menu", &doc, &node);
184
185 if (loaded) {
186 parse_state.menus = NULL;
187
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);
193 }
194 }
195
196 gboolean menu_new(gchar *name, gchar *title, gpointer data)
197 {
198 ObMenu *self;
199
200 /*if (g_hash_table_lookup(menu_hash, name)) return FALSE;*/
201
202 self = g_new0(ObMenu, 1);
203 self->name = g_strdup(name);
204 self->title = g_strdup(title);
205 self->data = data;
206
207 g_hash_table_replace(menu_hash, self->name, self);
208
209 return TRUE;
210 }
211
212 void menu_free(gchar *name)
213 {
214 ObMenu *self;
215
216 if (!(self = menu_from_name(name))) return;
217 g_hash_table_remove(menu_hash, self->name);
218 }
219
220 void menu_show(gchar *name, gint x, gint y, ObClient *client)
221 {
222 ObMenu *self;
223 ObMenuFrame *frame;
224
225 if (!(self = menu_from_name(name))) return;
226
227 frame = menu_frame_new(self, client);
228 menu_frame_move(frame, x, y);
229 menu_frame_show(frame, NULL);
230 }
231
232 static ObMenuEntry* menu_entry_new(ObMenu *menu, ObMenuEntryType type, gint id)
233 {
234 ObMenuEntry *self;
235
236 g_assert(menu);
237
238 self = g_new0(ObMenuEntry, 1);
239 self->type = type;
240 self->menu = menu;
241 self->id = id;
242
243 switch (type) {
244 case OB_MENU_ENTRY_TYPE_NORMAL:
245 self->data.normal.enabled = TRUE;
246 break;
247 case OB_MENU_ENTRY_TYPE_SUBMENU:
248 case OB_MENU_ENTRY_TYPE_SEPARATOR:
249 break;
250 }
251
252 return self;
253 }
254
255 static void menu_entry_free(ObMenuEntry *self)
256 {
257 if (self) {
258 switch (self->type) {
259 case OB_MENU_ENTRY_TYPE_NORMAL:
260 g_free(self->data.normal.label);
261 while (self->data.normal.actions) {
262 action_free(self->data.normal.actions->data);
263 self->data.normal.actions =
264 g_slist_delete_link(self->data.normal.actions,
265 self->data.normal.actions);
266 }
267 break;
268 case OB_MENU_ENTRY_TYPE_SUBMENU:
269 g_free(self->data.submenu.name);
270 break;
271 case OB_MENU_ENTRY_TYPE_SEPARATOR:
272 break;
273 }
274
275 g_free(self);
276 }
277 }
278
279 void menu_clear_entries(gchar *name)
280 {
281 ObMenu *self;
282
283 if (!(self = menu_from_name(name))) return;
284
285 menu_clear_entries_internal(self);
286 }
287
288 static void menu_clear_entries_internal(ObMenu *self)
289 {
290 /* XXX assert that the menu isn't visible */
291
292 while (self->entries) {
293 menu_entry_free(self->entries->data);
294 self->entries = g_list_delete_link(self->entries, self->entries);
295 }
296 }
297
298 void menu_add_normal(gchar *name, gint id, gchar *label, GSList *actions)
299 {
300 ObMenu *self;
301 ObMenuEntry *e;
302
303 if (!(self = menu_from_name(name))) return;
304
305 e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_NORMAL, id);
306 e->data.normal.label = g_strdup(label);
307 e->data.normal.actions = actions;
308
309 self->entries = g_list_append(self->entries, e);
310 }
311
312 void menu_add_submenu(gchar *name, gint id, gchar *submenu)
313 {
314 ObMenu *self;
315 ObMenuEntry *e;
316
317 if (!(self = menu_from_name(name))) return;
318
319 e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_SUBMENU, id);
320 e->data.submenu.name = g_strdup(submenu);
321
322 self->entries = g_list_append(self->entries, e);
323 }
324
325 void menu_add_separator(gchar *name, gint id)
326 {
327 ObMenu *self;
328 ObMenuEntry *e;
329
330 if (!(self = menu_from_name(name))) return;
331
332 e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_SEPARATOR, id);
333
334 self->entries = g_list_append(self->entries, e);
335 }
336
337 void menu_set_update_func(gchar *name, ObMenuUpdateFunc func)
338 {
339 ObMenu *self;
340
341 if (!(self = menu_from_name(name))) return;
342 self->update_func = func;
343 }
344
345 void menu_set_execute_func(gchar *name, ObMenuExecuteFunc func)
346 {
347 ObMenu *self;
348
349 if (!(self = menu_from_name(name))) return;
350 self->execute_func = func;
351 }
352
353 void menu_set_destroy_func(gchar *name, ObMenuDestroyFunc func)
354 {
355 ObMenu *self;
356
357 if (!(self = menu_from_name(name))) return;
358 self->destroy_func = func;
359 }
360
361 ObMenuEntry* menu_find_entry_id(ObMenu *self, gint id)
362 {
363 ObMenuEntry *ret = NULL;
364 GList *it;
365
366 for (it = self->entries; it; it = g_list_next(it)) {
367 ObMenuEntry *e = it->data;
368
369 if (e->id == id) {
370 ret = e;
371 break;
372 }
373 }
374 return ret;
375 }
376
377 void menu_find_submenus(ObMenu *self)
378 {
379 GList *it;
380
381 for (it = self->entries; it; it = g_list_next(it)) {
382 ObMenuEntry *e = it->data;
383
384 if (e->type == OB_MENU_ENTRY_TYPE_SUBMENU)
385 e->data.submenu.submenu = menu_from_name(e->data.submenu.name);
386 }
387 }
This page took 0.055923 seconds and 3 git commands to generate.