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