5 #include "render/theme.h"
10 static GHashTable *menu_hash = NULL;
11 GHashTable *menu_map = NULL;
13 #define FRAME_EVENTMASK (ButtonPressMask |ButtonMotionMask | EnterWindowMask | \
15 #define TITLE_EVENTMASK (ButtonPressMask | ButtonMotionMask)
16 #define ENTRY_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
17 ButtonPressMask | ButtonReleaseMask)
19 void menu_control_show(Menu *self, int x, int y, Client *client);
21 void menu_destroy_hash_key(gpointer data)
26 void menu_destroy_hash_value(Menu *self)
30 for (it = self->entries; it; it = it->next)
31 menu_entry_free(it->data);
32 g_list_free(self->entries);
37 g_hash_table_remove(menu_map, &self->title);
38 g_hash_table_remove(menu_map, &self->frame);
39 g_hash_table_remove(menu_map, &self->items);
41 appearance_free(self->a_title);
42 XDestroyWindow(ob_display, self->title);
43 XDestroyWindow(ob_display, self->frame);
44 XDestroyWindow(ob_display, self->items);
49 void menu_entry_free(MenuEntry *self)
52 action_free(self->action);
54 g_hash_table_remove(menu_map, &self->item);
56 appearance_free(self->a_item);
57 appearance_free(self->a_disabled);
58 appearance_free(self->a_hilite);
59 XDestroyWindow(ob_display, self->item);
71 menu_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
72 menu_destroy_hash_key,
73 (GDestroyNotify)menu_destroy_hash_value);
74 menu_map = g_hash_table_new(g_int_hash, g_int_equal);
76 m = menu_new(NULL, "root", NULL);
78 a = action_from_string("execute");
79 a->data.execute.path = g_strdup("xterm");
80 menu_add_entry(m, menu_entry_new("xterm", a));
81 a = action_from_string("restart");
82 menu_add_entry(m, menu_entry_new("restart", a));
83 menu_add_entry(m, menu_entry_new_separator("--"));
84 a = action_from_string("exit");
85 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));
94 t = (Menu *)plugin_create("timed_menu");
95 a = action_from_string("execute");
96 a->data.execute.path = g_strdup("xeyes");
97 menu_add_entry(t, menu_entry_new("xeyes", a));*/
99 s = menu_new("empty", "chub", m);
100 menu_add_entry(m, menu_entry_new_submenu("empty", s));
102 s = menu_new("", "s-club", m);
103 menu_add_entry(m, menu_entry_new_submenu("empty", s));
105 s = menu_new(NULL, "h-club", m);
106 menu_add_entry(m, menu_entry_new_submenu("empty", s));
108 s = menu_new(NULL, "g-club", m);
110 a = action_from_string("execute");
111 a->data.execute.path = g_strdup("xterm");
112 menu_add_entry(s, menu_entry_new("xterm", a));
113 a = action_from_string("restart");
114 menu_add_entry(s, menu_entry_new("restart", a));
115 menu_add_entry(s, menu_entry_new_separator("--"));
116 a = action_from_string("exit");
117 menu_add_entry(s, menu_entry_new("exit", a));
119 menu_add_entry(m, menu_entry_new_submenu("long", s));
121 m = menu_new("client menu", "client", NULL);
122 a = action_from_string("iconify");
123 menu_add_entry(m, menu_entry_new("iconify", a));
124 a = action_from_string("toggleshade");
125 menu_add_entry(m, menu_entry_new("(un)shade", a));
126 a = action_from_string("togglemaximizefull");
127 menu_add_entry(m, menu_entry_new("(un)maximize", a));
128 a = action_from_string("close");
129 menu_add_entry(m, menu_entry_new("close", a));
135 g_hash_table_destroy(menu_hash);
136 g_hash_table_destroy(menu_map);
139 static Window createWindow(Window parent, unsigned long mask,
140 XSetWindowAttributes *attrib)
142 return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
143 render_depth, InputOutput, render_visual,
148 Menu *menu_new_full(char *label, char *name, Menu *parent,
149 menu_controller_show show, menu_controller_update update)
151 XSetWindowAttributes attrib;
154 self = g_new0(Menu, 1);
155 self->label = g_strdup(label);
156 self->name = g_strdup(name);
157 self->parent = parent;
158 self->open_submenu = NULL;
160 self->entries = NULL;
162 self->invalid = TRUE;
164 /* default controllers */
167 self->update = update;
168 self->mouseover = NULL;
169 self->selected = NULL;
172 self->plugin_data = NULL;
174 attrib.override_redirect = TRUE;
175 attrib.event_mask = FRAME_EVENTMASK;
176 self->frame = createWindow(ob_root, CWOverrideRedirect|CWEventMask, &attrib);
177 attrib.event_mask = TITLE_EVENTMASK;
178 self->title = createWindow(self->frame, CWEventMask, &attrib);
179 self->items = createWindow(self->frame, 0, &attrib);
181 XSetWindowBorderWidth(ob_display, self->frame, theme_bwidth);
182 XSetWindowBackground(ob_display, self->frame, theme_b_color->pixel);
183 XSetWindowBorderWidth(ob_display, self->title, theme_bwidth);
184 XSetWindowBorder(ob_display, self->frame, theme_b_color->pixel);
185 XSetWindowBorder(ob_display, self->title, theme_b_color->pixel);
187 XMapWindow(ob_display, self->title);
188 XMapWindow(ob_display, self->items);
190 self->a_title = appearance_copy(theme_a_menu_title);
191 self->a_items = appearance_copy(theme_a_menu);
193 g_hash_table_insert(menu_map, &self->frame, self);
194 g_hash_table_insert(menu_map, &self->title, self);
195 g_hash_table_insert(menu_map, &self->items, self);
196 g_hash_table_insert(menu_hash, g_strdup(name), self);
200 void menu_free(char *name)
202 g_hash_table_remove(menu_hash, name);
205 MenuEntry *menu_entry_new_full(char *label, Action *action,
206 MenuEntryRenderType render_type,
209 MenuEntry *menu_entry = g_new0(MenuEntry, 1);
211 menu_entry->label = g_strdup(label);
212 menu_entry->render_type = render_type;
213 menu_entry->action = action;
215 menu_entry->hilite = FALSE;
216 menu_entry->enabled = TRUE;
218 menu_entry->submenu = submenu;
223 void menu_entry_set_submenu(MenuEntry *entry, Menu *submenu)
225 g_assert(entry != NULL);
227 entry->submenu = submenu;
229 if(entry->parent != NULL)
230 entry->parent->invalid = TRUE;
233 void menu_add_entry(Menu *menu, MenuEntry *entry)
235 XSetWindowAttributes attrib;
237 g_assert(menu != NULL && entry != NULL && entry->item == None);
239 menu->entries = g_list_append(menu->entries, entry);
240 entry->parent = menu;
242 attrib.event_mask = ENTRY_EVENTMASK;
243 entry->item = createWindow(menu->items, CWEventMask, &attrib);
244 XMapWindow(ob_display, entry->item);
245 entry->a_item = appearance_copy(theme_a_menu_item);
246 entry->a_disabled = appearance_copy(theme_a_menu_disabled);
247 entry->a_hilite = appearance_copy(theme_a_menu_hilite);
249 menu->invalid = TRUE;
251 g_hash_table_insert(menu_map, &entry->item, menu);
254 void menu_show(char *name, int x, int y, Client *client)
258 self = g_hash_table_lookup(menu_hash, name);
260 g_warning("Attempted to show menu '%s' but it does not exist.",
265 menu_show_full(self, x, y, client);
268 void menu_show_full(Menu *self, int x, int y, Client *client)
270 g_assert(self != NULL);
274 self->client = client;
277 self->show(self, x, y, client);
279 menu_control_show(self, x, y, client);
284 void menu_hide(Menu *self) {
286 XUnmapWindow(ob_display, self->frame);
288 if (self->open_submenu)
289 menu_hide(self->open_submenu);
290 if (self->parent && self->parent->open_submenu == self)
291 self->parent->open_submenu = NULL;
296 MenuEntry *menu_find_entry(Menu *menu, Window win)
300 for (it = menu->entries; it; it = it->next) {
301 MenuEntry *entry = it->data;
302 if (entry->item == win)
308 void menu_entry_fire(MenuEntry *self)
313 self->action->data.any.c = self->parent->client;
314 self->action->func(&self->action->data);
316 /* hide the whole thing */
318 while (m->parent) m = m->parent;
324 Default menu controller action for showing.
327 void menu_control_show(Menu *self, int x, int y, Client *client) {
328 g_assert(!self->invalid);
330 XMoveWindow(ob_display, self->frame,
331 MIN(x, screen_physical_size.width - self->size.width),
332 MIN(y, screen_physical_size.height - self->size.height));
333 POINT_SET(self->location,
334 MIN(x, screen_physical_size.width - self->size.width),
335 MIN(y, screen_physical_size.height - self->size.height));
338 stacking_raise_internal(self->frame);
339 XMapWindow(ob_display, self->frame);
341 } else if (self->shown && self->open_submenu) {
342 menu_hide(self->open_submenu);
346 void menu_control_mouseover(MenuEntry *self, gboolean enter) {
348 self->hilite = enter;
351 if (self->parent->open_submenu && self->submenu
352 != self->parent->open_submenu)
353 menu_hide(self->parent->open_submenu);
356 self->parent->open_submenu = self->submenu;
358 /* shouldn't be invalid since it must be displayed */
359 g_assert(!self->parent->invalid);
360 /* TODO: I don't understand why these bevels should be here.
361 Something must be wrong in the width calculation */
362 x = self->parent->location.x + self->parent->size.width +
365 /* need to get the width. is this bad?*/
366 menu_render(self->submenu);
368 if (self->submenu->size.width + x > screen_physical_size.width)
369 x = self->parent->location.x - self->submenu->size.width -
372 menu_show_full(self->submenu, x,
373 self->parent->location.y + self->y, NULL);