8 #include "render2/theme.h"
10 GHashTable *menu_hash = NULL;
12 #define FRAME_EVENTMASK (ButtonPressMask |ButtonMotionMask | EnterWindowMask |\
13 LeaveWindowMask | ExposureMask)
14 #define TITLE_EVENTMASK (ButtonPressMask | ButtonMotionMask | ExposureMask)
15 #define ENTRY_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
16 ButtonPressMask | ButtonReleaseMask | ExposureMask)
18 void menu_control_show(Menu *self, int x, int y, Client *client);
20 void menu_destroy_hash_key(Menu *menu)
25 void menu_destroy_hash_value(Menu *self)
29 for (it = self->entries; it; it = it->next)
30 menu_entry_free(it->data);
31 g_list_free(self->entries);
36 g_hash_table_remove(window_map, &self->w_title);
37 g_hash_table_remove(window_map, &self->w_frame);
38 g_hash_table_remove(window_map, &self->w_items);
40 stacking_remove(self);
42 RrSurfaceFree(self->s_items);
43 RrSurfaceFree(self->s_title);
44 RrSurfaceFree(self->s_frame);
45 XDestroyWindow(ob_display, self->w_frame);
50 void menu_entry_free(MenuEntry *self)
53 action_free(self->action);
55 g_hash_table_remove(window_map, &self->w_item);
56 g_hash_table_remove(window_map, &self->w_text);
58 RrSurfaceFree(self->s_item);
70 menu_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
71 menu_destroy_hash_key,
72 (GDestroyNotify)menu_destroy_hash_value);
74 m = menu_new("sex menu", "root", NULL);
76 a = action_from_string("execute");
77 a->data.execute.path = g_strdup("xterm");
78 menu_add_entry(m, menu_entry_new("xterm", a));
79 a = action_from_string("restart");
80 menu_add_entry(m, menu_entry_new("restart", a));
81 menu_add_entry(m, menu_entry_new_separator("--"));
82 a = action_from_string("exit");
83 menu_add_entry(m, menu_entry_new("exit", a));
86 s = menu_new("subsex menu", "submenu", m);
87 a = action_from_string("execute");
88 a->data.execute.path = g_strdup("xclock");
89 menu_add_entry(s, menu_entry_new("xclock", a));
91 menu_add_entry(m, menu_entry_new_submenu("subz", s));
93 s = menu_new("empty", "chub", m);
94 menu_add_entry(m, menu_entry_new_submenu("empty", s));
96 s = menu_new("", "s-club", m);
97 menu_add_entry(m, menu_entry_new_submenu("empty", s));
99 s = menu_new(NULL, "h-club", m);
100 menu_add_entry(m, menu_entry_new_submenu("empty", s));
102 s = menu_new(NULL, "g-club", m);
104 a = action_from_string("execute");
105 a->data.execute.path = g_strdup("xterm");
106 menu_add_entry(s, menu_entry_new("xterm", a));
107 a = action_from_string("restart");
108 menu_add_entry(s, menu_entry_new("restart", a));
109 menu_add_entry(s, menu_entry_new_separator("--"));
110 a = action_from_string("exit");
111 menu_add_entry(s, menu_entry_new("exit", a));
113 menu_add_entry(m, menu_entry_new_submenu("long", s));
119 g_hash_table_destroy(menu_hash);
122 static Window createWindow(Window parent, unsigned long mask,
123 XSetWindowAttributes *attrib)
125 return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
126 RrInstanceDepth(ob_render_inst), InputOutput,
127 RrInstanceVisual(ob_render_inst), mask, attrib);
131 Menu *menu_new_full(char *label, char *name, Menu *parent,
132 menu_controller_show show, menu_controller_update update)
134 XSetWindowAttributes attrib;
137 self = g_new0(Menu, 1);
138 self->obwin.type = Window_Menu;
139 self->label = g_strdup(label);
140 self->name = g_strdup(name);
141 self->parent = parent;
142 self->open_submenu = NULL;
144 self->entries = NULL;
146 self->invalid = TRUE;
148 /* default controllers */
151 self->update = update;
152 self->mouseover = NULL;
153 self->selected = NULL;
156 self->plugin_data = NULL;
158 attrib.override_redirect = TRUE;
159 attrib.event_mask = FRAME_EVENTMASK;
160 self->w_frame = createWindow(ob_root,
161 CWOverrideRedirect|CWEventMask, &attrib);
162 self->s_frame = RrSurfaceNew(ob_render_inst, 0, self->w_frame, 0);
164 self->s_title = RrSurfaceNewChild(0, self->s_frame, 0);
165 self->w_title = RrSurfaceWindow(self->s_title);
166 XSelectInput(ob_display, self->w_title, TITLE_EVENTMASK);
168 self->s_items = RrSurfaceNewChild(0, self->s_frame, 0);
169 self->w_items = RrSurfaceWindow(self->s_items);
171 RrSurfaceShow(self->s_items);
173 g_hash_table_insert(window_map, &self->w_frame, self);
174 g_hash_table_insert(window_map, &self->w_title, self);
175 g_hash_table_insert(window_map, &self->w_items, self);
176 g_hash_table_insert(menu_hash, g_strdup(name), self);
178 stacking_add(MENU_AS_WINDOW(self));
179 stacking_raise(MENU_AS_WINDOW(self));
184 void menu_free(char *name)
186 g_hash_table_remove(menu_hash, name);
189 MenuEntry *menu_entry_new_full(char *label, Action *action,
190 MenuEntryRenderType render_type,
193 MenuEntry *menu_entry = g_new0(MenuEntry, 1);
195 menu_entry->label = g_strdup(label);
196 menu_entry->render_type = render_type;
197 menu_entry->action = action;
199 menu_entry->hilite = FALSE;
200 menu_entry->enabled = TRUE;
202 menu_entry->submenu = submenu;
207 void menu_entry_set_submenu(MenuEntry *entry, Menu *submenu)
209 g_assert(entry != NULL);
211 entry->submenu = submenu;
213 if(entry->parent != NULL)
214 entry->parent->invalid = TRUE;
217 void menu_add_entry(Menu *menu, MenuEntry *entry)
221 g_assert(menu != NULL);
222 g_assert(entry != NULL);
223 g_assert(entry->w_item == None);
225 menu->entries = g_list_append(menu->entries, entry);
226 entry->parent = menu;
228 entry->s_item = RrSurfaceNewChild(0, menu->s_items, 0);
229 entry->w_item = RrSurfaceWindow(entry->s_item);
230 XSelectInput(ob_display, entry->w_item, ENTRY_EVENTMASK);
232 entry->s_text = RrSurfaceNewChild(RR_SURFACE_PLANAR, entry->s_item, 1);
233 entry->w_text = RrSurfaceWindow(entry->s_text);
234 XSelectInput(ob_display, entry->w_text, ENTRY_EVENTMASK);
235 RrColorSet(&c, 0, 0, 0, 0); /* clear */
236 RrPlanarSet(entry->s_text, RR_PLANAR_SOLID, RR_BEVEL_NONE, &c, NULL,
239 RrSurfaceShow(entry->s_item);
240 RrSurfaceShow(entry->s_text);
242 menu->invalid = TRUE;
244 g_hash_table_insert(window_map, &entry->w_item, menu);
245 g_hash_table_insert(window_map, &entry->w_text, menu);
248 void menu_show(char *name, int x, int y, Client *client)
252 self = g_hash_table_lookup(menu_hash, name);
254 g_warning("Attempted to show menu '%s' but it does not exist.",
259 menu_show_full(self, x, y, client);
262 void menu_show_full(Menu *self, int x, int y, Client *client)
264 g_assert(self != NULL);
268 self->client = client;
271 self->show(self, x, y, client);
273 menu_control_show(self, x, y, client);
277 void menu_hide(Menu *self) {
279 RrSurfaceHide(self->s_frame);
281 if (self->open_submenu)
282 menu_hide(self->open_submenu);
283 if (self->parent && self->parent->open_submenu == self)
284 self->parent->open_submenu = NULL;
289 void menu_clear(Menu *self) {
292 for (it = self->entries; it; it = it->next) {
293 MenuEntry *entry = it->data;
294 menu_entry_free(entry);
296 self->entries = NULL;
297 self->invalid = TRUE;
301 MenuEntry *menu_find_entry(Menu *menu, Window win)
305 for (it = menu->entries; it; it = it->next) {
306 MenuEntry *entry = it->data;
307 if (entry->w_item == win || entry->w_text == win)
313 void menu_entry_fire(MenuEntry *self)
318 self->action->data.any.c = self->parent->client;
319 self->action->func(&self->action->data);
321 /* hide the whole thing */
323 while (m->parent) m = m->parent;
329 Default menu controller action for showing.
332 void menu_control_show(Menu *self, int x, int y, Client *client) {
333 g_assert(!self->invalid);
335 POINT_SET(self->location,
336 MIN(x, screen_physical_size.width - self->size.width),
337 MIN(y, screen_physical_size.height - self->size.height));
338 self->invalid = TRUE;
342 RrSurfaceShow(self->s_frame);
343 stacking_raise(MENU_AS_WINDOW(self));
345 } else if (self->shown && self->open_submenu) {
346 menu_hide(self->open_submenu);
350 void menu_control_mouseover(MenuEntry *self, gboolean enter) {
352 self->hilite = enter;
355 if (self->parent->open_submenu && self->submenu
356 != self->parent->open_submenu)
357 menu_hide(self->parent->open_submenu);
360 self->parent->open_submenu = self->submenu;
362 /* shouldn't be invalid since it must be displayed */
363 g_assert(!self->parent->invalid);
364 /* TODO: I don't understand why these bevels should be here.
365 Something must be wrong in the width calculation */
366 x = self->parent->location.x + self->parent->size.width +
369 /* need to get the width. is this bad?*/
370 menu_render(self->submenu);
372 if (self->submenu->size.width + x > screen_physical_size.width)
373 x = self->parent->location.x - self->submenu->size.width -
376 menu_show_full(self->submenu, x,
377 self->parent->location.y + self->y,
378 self->parent->client);