]> Dogcows Code - chaz/openbox/blob - openbox/menu.c
prefix and capitalize ObMenu ObMenuEntry and ObMenuEntryRenderType
[chaz/openbox] / openbox / menu.c
1 #include "menu.h"
2 #include "openbox.h"
3 #include "stacking.h"
4 #include "client.h"
5 #include "grab.h"
6 #include "screen.h"
7 #include "geom.h"
8 #include "plugin.h"
9
10 GHashTable *menu_hash = NULL;
11 GSList *menu_visible = NULL;
12
13 #define FRAME_EVENTMASK (ButtonPressMask |ButtonMotionMask | EnterWindowMask | \
14 LeaveWindowMask)
15 #define TITLE_EVENTMASK (ButtonPressMask | ButtonMotionMask)
16 #define ENTRY_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
17 ButtonPressMask | ButtonReleaseMask)
18
19 static void parse_menu(xmlDocPtr doc, xmlNodePtr node, void *data)
20 {
21 Action *act;
22 xmlNodePtr nact;
23 gchar *id = NULL, *title = NULL, *label = NULL;
24 ObMenu *menu, *parent;
25
26 if (!parse_attr_string("id", node->parent, &id))
27 goto parse_menu_fail;
28 if (!parse_attr_string("label", node->parent, &title))
29 goto parse_menu_fail;
30
31 g_message("menu label %s", title);
32
33 menu = menu_new(title, id, data ? *((ObMenu**)data) : NULL);
34 if (data)
35 *((ObMenu**)data) = menu;
36
37 while (node) {
38 if (!xmlStrcasecmp(node->name, (const xmlChar*) "menu")) {
39 parent = menu;
40 parse_menu(doc, node->xmlChildrenNode, &parent);
41 menu_add_entry(menu, menu_entry_new_submenu(parent->label,
42 parent));
43 }
44 else if (!xmlStrcasecmp(node->name, (const xmlChar*) "item")) {
45 if (parse_attr_string("label", node, &label)) {
46 if ((nact = parse_find_node("action", node->xmlChildrenNode)))
47 act = action_parse(doc, nact);
48 else
49 act = NULL;
50 if (act)
51 menu_add_entry(menu, menu_entry_new(label, act));
52 else
53 menu_add_entry(menu, menu_entry_new_separator(label));
54 g_free(label);
55 }
56 }
57 node = node->next;
58 }
59
60 parse_menu_fail:
61 g_free(id);
62 g_free(title);
63 }
64
65 void menu_control_show(ObMenu *self, int x, int y, ObClient *client);
66
67 void menu_destroy_hash_key(ObMenu *menu)
68 {
69 g_free(menu);
70 }
71
72 void menu_destroy_hash_value(ObMenu *self)
73 {
74 GList *it;
75
76 for (it = self->entries; it; it = it->next)
77 menu_entry_free(it->data);
78 g_list_free(self->entries);
79
80 g_free(self->label);
81 g_free(self->name);
82
83 g_hash_table_remove(window_map, &self->title);
84 g_hash_table_remove(window_map, &self->frame);
85 g_hash_table_remove(window_map, &self->items);
86
87 stacking_remove(self);
88
89 RrAppearanceFree(self->a_title);
90 XDestroyWindow(ob_display, self->title);
91 XDestroyWindow(ob_display, self->frame);
92 XDestroyWindow(ob_display, self->items);
93
94 g_free(self);
95 }
96
97 void menu_entry_free(ObMenuEntry *self)
98 {
99 g_free(self->label);
100 action_free(self->action);
101
102 g_hash_table_remove(window_map, &self->item);
103
104 RrAppearanceFree(self->a_item);
105 RrAppearanceFree(self->a_disabled);
106 RrAppearanceFree(self->a_hilite);
107 XDestroyWindow(ob_display, self->item);
108
109 g_free(self);
110 }
111
112 void menu_startup()
113 {
114 /*
115 ObMenu *m;
116 ObMenu *s;
117 ObMenu *t;
118 Action *a;
119 */
120
121 menu_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
122 (GDestroyNotify)menu_destroy_hash_key,
123 (GDestroyNotify)menu_destroy_hash_value);
124
125 parse_register("menu", parse_menu, NULL);
126
127 /*
128 m = menu_new("sex menu", "root", NULL);
129
130 a = action_from_string("execute");
131 a->data.execute.path = g_strdup("xterm");
132 menu_add_entry(m, menu_entry_new("xterm", a));
133 a = action_from_string("restart");
134 menu_add_entry(m, menu_entry_new("restart", a));
135 menu_add_entry(m, menu_entry_new_separator("--"));
136 a = action_from_string("exit");
137 menu_add_entry(m, menu_entry_new("exit", a));
138 */
139
140 /*
141 s = menu_new("subsex menu", "submenu", m);
142 a = action_from_string("execute");
143 a->data.execute.path = g_strdup("xclock");
144 menu_add_entry(s, menu_entry_new("xclock", a));
145
146 menu_add_entry(m, menu_entry_new_submenu("subz", s));
147
148 s = menu_new("empty", "chub", m);
149 menu_add_entry(m, menu_entry_new_submenu("empty", s));
150
151 s = menu_new("", "s-club", m);
152 menu_add_entry(m, menu_entry_new_submenu("empty", s));
153
154 s = menu_new(NULL, "h-club", m);
155 menu_add_entry(m, menu_entry_new_submenu("empty", s));
156
157 s = menu_new(NULL, "g-club", m);
158
159 a = action_from_string("execute");
160 a->data.execute.path = g_strdup("xterm");
161 menu_add_entry(s, menu_entry_new("xterm", a));
162 a = action_from_string("restart");
163 menu_add_entry(s, menu_entry_new("restart", a));
164 menu_add_entry(s, menu_entry_new_separator("--"));
165 a = action_from_string("exit");
166 menu_add_entry(s, menu_entry_new("exit", a));
167
168 menu_add_entry(m, menu_entry_new_submenu("long", s));
169 */
170 }
171
172 void menu_shutdown()
173 {
174 g_hash_table_destroy(menu_hash);
175 }
176
177 static Window createWindow(Window parent, unsigned long mask,
178 XSetWindowAttributes *attrib)
179 {
180 return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
181 RrDepth(ob_rr_inst), InputOutput,
182 RrVisual(ob_rr_inst), mask, attrib);
183
184 }
185
186 ObMenu *menu_new_full(char *label, char *name, ObMenu *parent,
187 menu_controller_show show, menu_controller_update update)
188 {
189 XSetWindowAttributes attrib;
190 ObMenu *self;
191
192 self = g_new0(ObMenu, 1);
193 self->obwin.type = Window_Menu;
194 self->label = g_strdup(label);
195 self->name = g_strdup(name);
196 self->parent = parent;
197 self->open_submenu = NULL;
198
199 self->entries = NULL;
200 self->shown = FALSE;
201 self->invalid = TRUE;
202
203 /* default controllers */
204 self->show = show;
205 self->hide = NULL;
206 self->update = update;
207 self->mouseover = NULL;
208 self->selected = NULL;
209
210 self->plugin = NULL;
211 self->plugin_data = NULL;
212
213 attrib.override_redirect = TRUE;
214 attrib.event_mask = FRAME_EVENTMASK;
215 self->frame = createWindow(RootWindow(ob_display, ob_screen),
216 CWOverrideRedirect|CWEventMask, &attrib);
217 attrib.event_mask = TITLE_EVENTMASK;
218 self->title = createWindow(self->frame, CWEventMask, &attrib);
219 self->items = createWindow(self->frame, 0, &attrib);
220
221 self->a_title = self->a_items = NULL;
222
223 XMapWindow(ob_display, self->title);
224 XMapWindow(ob_display, self->items);
225
226 g_hash_table_insert(window_map, &self->frame, self);
227 g_hash_table_insert(window_map, &self->title, self);
228 g_hash_table_insert(window_map, &self->items, self);
229 g_hash_table_insert(menu_hash, g_strdup(name), self);
230
231 stacking_add(MENU_AS_WINDOW(self));
232 stacking_raise(MENU_AS_WINDOW(self));
233
234 return self;
235 }
236
237 void menu_free(char *name)
238 {
239 g_hash_table_remove(menu_hash, name);
240 }
241
242 ObMenuEntry *menu_entry_new_full(char *label, Action *action,
243 ObMenuEntryRenderType render_type,
244 gpointer submenu)
245 {
246 ObMenuEntry *menu_entry = g_new0(ObMenuEntry, 1);
247
248 menu_entry->label = g_strdup(label);
249 menu_entry->render_type = render_type;
250 menu_entry->action = action;
251
252 menu_entry->hilite = FALSE;
253 menu_entry->enabled = TRUE;
254
255 menu_entry->submenu = submenu;
256
257 return menu_entry;
258 }
259
260 void menu_entry_set_submenu(ObMenuEntry *entry, ObMenu *submenu)
261 {
262 g_assert(entry != NULL);
263
264 entry->submenu = submenu;
265
266 if(entry->parent != NULL)
267 entry->parent->invalid = TRUE;
268 }
269
270 void menu_add_entry(ObMenu *menu, ObMenuEntry *entry)
271 {
272 XSetWindowAttributes attrib;
273
274 g_assert(menu != NULL);
275 g_assert(entry != NULL);
276 g_assert(entry->item == None);
277
278 menu->entries = g_list_append(menu->entries, entry);
279 entry->parent = menu;
280
281 attrib.event_mask = ENTRY_EVENTMASK;
282 entry->item = createWindow(menu->items, CWEventMask, &attrib);
283 XMapWindow(ob_display, entry->item);
284
285 entry->a_item = entry->a_disabled = entry->a_hilite = NULL;
286
287 menu->invalid = TRUE;
288
289 g_hash_table_insert(window_map, &entry->item, menu);
290 }
291
292 void menu_show(char *name, int x, int y, ObClient *client)
293 {
294 ObMenu *self;
295
296 self = g_hash_table_lookup(menu_hash, name);
297 if (!self) {
298 g_warning("Attempted to show menu '%s' but it does not exist.",
299 name);
300 return;
301 }
302
303 menu_show_full(self, x, y, client);
304 }
305
306 void menu_show_full(ObMenu *self, int x, int y, ObClient *client)
307 {
308 g_assert(self != NULL);
309
310 menu_render(self);
311
312 self->client = client;
313
314 if (!self->shown) {
315 if (!self->parent) {
316 grab_pointer(TRUE, None);
317 grab_keyboard(TRUE);
318 }
319 menu_visible = g_slist_append(menu_visible, self);
320 }
321
322 if (self->show) {
323 self->show(self, x, y, client);
324 } else {
325 menu_control_show(self, x, y, client);
326 }
327 }
328
329 void menu_hide(ObMenu *self) {
330 if (self->shown) {
331 XUnmapWindow(ob_display, self->frame);
332 self->shown = FALSE;
333 if (self->open_submenu)
334 menu_hide(self->open_submenu);
335 if (self->parent && self->parent->open_submenu == self)
336 self->parent->open_submenu = NULL;
337
338 if (!self->parent) {
339 grab_keyboard(FALSE);
340 grab_pointer(FALSE, None);
341 }
342 menu_visible = g_slist_remove(menu_visible, self);
343 }
344 }
345
346 void menu_clear(ObMenu *self) {
347 GList *it;
348
349 for (it = self->entries; it; it = it->next) {
350 ObMenuEntry *entry = it->data;
351 menu_entry_free(entry);
352 }
353 self->entries = NULL;
354 self->invalid = TRUE;
355 }
356
357
358 ObMenuEntry *menu_find_entry(ObMenu *menu, Window win)
359 {
360 GList *it;
361
362 for (it = menu->entries; it; it = it->next) {
363 ObMenuEntry *entry = it->data;
364 if (entry->item == win)
365 return entry;
366 }
367 return NULL;
368 }
369
370 ObMenuEntry *menu_find_entry_by_pos(ObMenu *menu, int x, int y)
371 {
372 if (x < 0 || x >= menu->size.width || y < 0 || y >= menu->size.height)
373 return NULL;
374
375 y -= menu->title_h + ob_rr_theme->bwidth;
376 if (y < 0) return NULL;
377
378 g_message ("%d %p", y/menu->item_h, g_list_nth_data(menu->entries, y / menu->item_h));
379 return g_list_nth_data(menu->entries, y / menu->item_h);
380 }
381
382 void menu_entry_fire(ObMenuEntry *self)
383 {
384 ObMenu *m;
385
386 if (self->action) {
387 self->action->data.any.c = self->parent->client;
388 self->action->func(&self->action->data);
389
390 /* hide the whole thing */
391 m = self->parent;
392 while (m->parent) m = m->parent;
393 menu_hide(m);
394 }
395 }
396
397 /*
398 Default menu controller action for showing.
399 */
400
401 void menu_control_show(ObMenu *self, int x, int y, ObClient *client) {
402 guint i;
403 Rect *a = NULL;
404
405 g_assert(!self->invalid);
406
407 for (i = 0; i < screen_num_monitors; ++i) {
408 a = screen_physical_area_monitor(i);
409 if (RECT_CONTAINS(*a, x, y))
410 break;
411 }
412 g_assert(a != NULL);
413 self->xin_area = i;
414
415 POINT_SET(self->location,
416 MIN(x, a->x + a->width - 1 - self->size.width),
417 MIN(y, a->y + a->height - 1 - self->size.height));
418 XMoveWindow(ob_display, self->frame, self->location.x, self->location.y);
419
420 if (!self->shown) {
421 XMapWindow(ob_display, self->frame);
422 stacking_raise(MENU_AS_WINDOW(self));
423 self->shown = TRUE;
424 } else if (self->shown && self->open_submenu) {
425 menu_hide(self->open_submenu);
426 }
427 }
428
429 void menu_control_mouseover(ObMenuEntry *self, gboolean enter) {
430 int x;
431 Rect *a;
432
433 self->hilite = enter;
434
435 if (enter) {
436 if (self->parent->open_submenu && self->submenu
437 != self->parent->open_submenu)
438 menu_hide(self->parent->open_submenu);
439
440 if (self->submenu && self->parent->open_submenu != self->submenu) {
441 self->parent->open_submenu = self->submenu;
442
443 /* shouldn't be invalid since it must be displayed */
444 g_assert(!self->parent->invalid);
445 /* TODO: I don't understand why these bevels should be here.
446 Something must be wrong in the width calculation */
447 x = self->parent->location.x + self->parent->size.width +
448 ob_rr_theme->bwidth;
449
450 /* need to get the width. is this bad?*/
451 menu_render(self->submenu);
452
453 a = screen_physical_area_monitor(self->parent->xin_area);
454
455 if (self->submenu->size.width + x >= a->x + a->width)
456 x = self->parent->location.x - self->submenu->size.width -
457 ob_rr_theme->bwidth;
458
459 menu_show_full(self->submenu, x,
460 self->parent->location.y + self->y,
461 self->parent->client);
462 }
463 }
464 }
This page took 0.057213 seconds and 5 git commands to generate.