]> Dogcows Code - chaz/openbox/blob - openbox/menu.c
3e38c0f99ddfb3ae5b8b653924b697750b045c97
[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(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 static void parse_menu(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
68 gpointer data)
69 {
70 ObMenuParseState *state = data;
71 gchar *name = NULL, *title = NULL;
72
73 if (!parse_attr_string("id", node, &name))
74 goto parse_menu_fail;
75
76 if (!g_hash_table_lookup(menu_hash, name)) {
77 if (!parse_attr_string("label", node, &title))
78 goto parse_menu_fail;
79
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);
84 }
85 }
86
87 if (state->menus)
88 menu_add_submenu(state->menus->data, 0, name);
89
90 parse_menu_fail:
91 g_free(name);
92 g_free(title);
93 }
94
95
96 void menu_destroy_hash_value(ObMenu *self)
97 {
98 /* XXX make sure its not visible */
99 menu_clear_entries_internal(self);
100 g_free(self->name);
101 g_free(self->title);
102 }
103
104 void menu_startup(ObParseInst *i)
105 {
106 menu_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
107 (GDestroyNotify)menu_destroy_hash_value);
108 }
109
110 void menu_shutdown()
111 {
112 menu_frame_hide_all();
113 g_hash_table_destroy(menu_hash);
114 menu_hash = NULL;
115 }
116
117 void menu_parse()
118 {
119 ObParseInst *i;
120 ObMenuParseState parse_state;
121 xmlDocPtr doc;
122 xmlNodePtr node;
123 gchar *p;
124 gboolean loaded = FALSE;
125
126 i = parse_startup();
127
128 if (config_menu_path)
129 if (!(loaded =
130 parse_load(config_menu_path, "openbox_menu", &doc, &node)))
131 g_warning("Failed to load menu from '%s'", config_menu_path);
132 if (!loaded) {
133 p = g_build_filename(g_get_home_dir(), ".openbox", "menu", NULL);
134 if (!(loaded =
135 parse_load(p, "openbox_menu", &doc, &node)))
136 g_warning("Failed to load menu from '%s'", p);
137 g_free(p);
138 }
139 if (!loaded) {
140 p = g_build_filename(RCDIR, "menu", NULL);
141 if (!(loaded =
142 parse_load(p, "openbox_menu", &doc, &node)))
143 g_warning("Failed to load menu from '%s'", p);
144 g_free(p);
145 }
146
147 if (loaded) {
148 parse_state.menus = NULL;
149
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);
154 }
155
156 parse_shutdown(i);
157 }
158
159 gboolean menu_new(gchar *name, gchar *title, gpointer data)
160 {
161 ObMenu *self;
162
163 if (g_hash_table_lookup(menu_hash, name)) return FALSE;
164
165 self = g_new0(ObMenu, 1);
166 self->name = g_strdup(name);
167 self->title = g_strdup(title);
168 self->data = data;
169
170 g_hash_table_insert(menu_hash, self->name, self);
171
172 return TRUE;
173 }
174
175 void menu_free(gchar *name)
176 {
177 ObMenu *self;
178
179 if (!(self = menu_from_name(name))) return;
180 g_hash_table_remove(menu_hash, self->name);
181 }
182
183 void menu_show(gchar *name, gint x, gint y, ObClient *client)
184 {
185 ObMenu *self;
186 ObMenuFrame *frame;
187
188 if (!(self = menu_from_name(name))) return;
189
190 frame = menu_frame_new(self, client);
191 menu_frame_move(frame, x, y);
192 menu_frame_show(frame, NULL);
193 }
194
195 static ObMenuEntry* menu_entry_new(ObMenu *menu, ObMenuEntryType type, gint id)
196 {
197 ObMenuEntry *self;
198
199 g_assert(menu);
200
201 self = g_new0(ObMenuEntry, 1);
202 self->type = type;
203 self->menu = menu;
204 self->id = id;
205 self->enabled = TRUE;
206 return self;
207 }
208
209 static void menu_entry_free(ObMenuEntry *self)
210 {
211 if (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);
220 }
221 break;
222 case OB_MENU_ENTRY_TYPE_SUBMENU:
223 case OB_MENU_ENTRY_TYPE_SEPARATOR:
224 break;
225 }
226
227 g_free(self);
228 }
229 }
230
231 void menu_clear_entries(gchar *name)
232 {
233 ObMenu *self;
234
235 if (!(self = menu_from_name(name))) return;
236
237 menu_clear_entries_internal(self);
238 }
239
240 static void menu_clear_entries_internal(ObMenu *self)
241 {
242 /* XXX assert that the menu isn't visible */
243
244 while (self->entries) {
245 menu_entry_free(self->entries->data);
246 self->entries = g_list_delete_link(self->entries, self->entries);
247 }
248 }
249
250 void menu_add_normal(gchar *name, gint id, gchar *label, GSList *actions)
251 {
252 ObMenu *self;
253 ObMenuEntry *e;
254
255 if (!(self = menu_from_name(name))) return;
256
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;
260
261 self->entries = g_list_append(self->entries, e);
262 }
263
264 void menu_add_submenu(gchar *name, gint id, gchar *submenu)
265 {
266 ObMenu *self, *sub;
267 ObMenuEntry *e;
268
269 if (!(self = menu_from_name(name))) return;
270 if (!(sub = menu_from_name(submenu))) return;
271
272 e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_SUBMENU, id);
273 e->data.submenu.submenu = sub;
274
275 self->entries = g_list_append(self->entries, e);
276 }
277
278 void menu_add_separator(gchar *name, gint id)
279 {
280 ObMenu *self;
281 ObMenuEntry *e;
282
283 if (!(self = menu_from_name(name))) return;
284
285 e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_SEPARATOR, id);
286
287 self->entries = g_list_append(self->entries, e);
288 }
289
290 void menu_set_update_func(gchar *name, ObMenuUpdateFunc func)
291 {
292 ObMenu *self;
293
294 if (!(self = menu_from_name(name))) return;
295 self->update_func = func;
296 }
297
298 ObMenuEntry* menu_find_entry_id(ObMenu *self, gint id)
299 {
300 ObMenuEntry *ret = NULL;
301 GList *it;
302
303 for (it = self->entries; it; it = g_list_next(it)) {
304 ObMenuEntry *e = it->data;
305
306 if (e->id == id) {
307 ret = e;
308 break;
309 }
310 }
311 return ret;
312 }
This page took 0.050504 seconds and 3 git commands to generate.