]> Dogcows Code - chaz/openbox/blob - openbox/menu.c
raise menus above clients.
[chaz/openbox] / openbox / menu.c
1 #include "menu.h"
2 #include "openbox.h"
3 #include "stacking.h"
4 #include "render/theme.h"
5
6 static GHashTable *menu_hash = NULL;
7 GHashTable *menu_map = NULL;
8
9 #define TITLE_EVENTMASK (ButtonPressMask | ButtonMotionMask)
10 #define ENTRY_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
11 ButtonPressMask | ButtonReleaseMask)
12
13 void menu_destroy_hash_key(gpointer data)
14 {
15 g_free(data);
16 }
17
18 void menu_destroy_hash_value(Menu *self)
19 {
20 GList *it;
21
22 for (it = self->entries; it; it = it->next)
23 menu_entry_free(it->data);
24 g_list_free(self->entries);
25
26 g_free(self->label);
27 g_free(self->name);
28
29 g_hash_table_remove(menu_map, &self->title);
30 g_hash_table_remove(menu_map, &self->frame);
31 g_hash_table_remove(menu_map, &self->items);
32
33 appearance_free(self->a_title);
34 XDestroyWindow(ob_display, self->title);
35 XDestroyWindow(ob_display, self->frame);
36 XDestroyWindow(ob_display, self->items);
37
38 g_free(self);
39 }
40
41 void menu_entry_free(MenuEntry *self)
42 {
43 g_free(self->label);
44 action_free(self->action);
45
46 g_hash_table_remove(menu_map, &self->item);
47
48 appearance_free(self->a_item);
49 appearance_free(self->a_disabled);
50 appearance_free(self->a_hilite);
51 XDestroyWindow(ob_display, self->item);
52
53 g_free(self);
54 }
55
56 void menu_startup()
57 {
58 Menu *m;
59
60 menu_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
61 menu_destroy_hash_key,
62 (GDestroyNotify)menu_destroy_hash_value);
63 menu_map = g_hash_table_new(g_int_hash, g_int_equal);
64
65 m = menu_new("sex menu", "root", NULL);
66 menu_add_entry(m, menu_entry_new("foo shit etc bleh",
67 action_from_string("restart")));
68 menu_add_entry(m, menu_entry_new("more shit",
69 action_from_string("restart")));
70 menu_add_entry(m, menu_entry_new("",
71 action_from_string("restart")));
72 menu_add_entry(m, menu_entry_new("and yet more",
73 action_from_string("restart")));
74 }
75
76 void menu_shutdown()
77 {
78 g_hash_table_destroy(menu_hash);
79 g_hash_table_destroy(menu_map);
80 }
81
82 static Window createWindow(Window parent, unsigned long mask,
83 XSetWindowAttributes *attrib)
84 {
85 return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
86 render_depth, InputOutput, render_visual,
87 mask, attrib);
88
89 }
90
91 Menu *menu_new(char *label, char *name, Menu *parent)
92 {
93 XSetWindowAttributes attrib;
94 Menu *self;
95
96 self = g_new0(Menu, 1);
97 self->label = g_strdup(label);
98 self->name = g_strdup(name);
99 self->parent = parent;
100
101 self->entries = NULL;
102 self->shown = FALSE;
103 self->invalid = FALSE;
104 /* default controllers? */
105
106 attrib.override_redirect = TRUE;
107 self->frame = createWindow(ob_root, CWOverrideRedirect, &attrib);
108 attrib.event_mask = TITLE_EVENTMASK;
109 self->title = createWindow(self->frame, CWEventMask, &attrib);
110 self->items = createWindow(self->frame, 0, &attrib);
111
112 XSetWindowBorderWidth(ob_display, self->frame, theme_bwidth);
113 XSetWindowBorderWidth(ob_display, self->title, theme_bwidth);
114 XSetWindowBorder(ob_display, self->frame, theme_b_color->pixel);
115 XSetWindowBorder(ob_display, self->title, theme_b_color->pixel);
116
117 XMapWindow(ob_display, self->title);
118 XMapWindow(ob_display, self->items);
119
120 self->a_title = appearance_copy(theme_a_menu_title);
121 self->a_items = appearance_copy(theme_a_menu);
122
123 g_hash_table_insert(menu_map, &self->frame, self);
124 g_hash_table_insert(menu_map, &self->title, self);
125 g_hash_table_insert(menu_map, &self->items, self);
126 g_hash_table_insert(menu_hash, g_strdup(name), self);
127 return self;
128 }
129
130 void menu_free(char *name)
131 {
132 g_hash_table_remove(menu_hash, name);
133 }
134
135 MenuEntry *menu_entry_new_full(char *label, Action *action,
136 MenuEntryRenderType render_type,
137 gpointer submenu)
138 {
139 MenuEntry *menu_entry = g_new0(MenuEntry, 1);
140
141 menu_entry->label = g_strdup(label);
142 menu_entry->render_type = render_type;
143 menu_entry->action = action;
144
145 menu_entry->hilite = FALSE;
146 menu_entry->enabled = TRUE;
147
148 menu_entry->submenu = submenu;
149
150 return menu_entry;
151 }
152
153 void menu_entry_set_submenu(MenuEntry *entry, Menu *submenu)
154 {
155 g_assert(entry != NULL);
156
157 entry->submenu = submenu;
158
159 if(entry->parent != NULL)
160 entry->parent->invalid = TRUE;
161 }
162
163 void menu_add_entry(Menu *menu, MenuEntry *entry)
164 {
165 XSetWindowAttributes attrib;
166
167 g_assert(menu != NULL && entry != NULL && entry->item == None);
168
169 menu->entries = g_list_append(menu->entries, entry);
170 entry->parent = menu;
171
172 attrib.event_mask = ENTRY_EVENTMASK;
173 entry->item = createWindow(menu->items, CWEventMask, &attrib);
174 XMapWindow(ob_display, entry->item);
175 entry->a_item = appearance_copy(theme_a_menu_item);
176 entry->a_disabled = appearance_copy(theme_a_menu_disabled);
177 entry->a_hilite = appearance_copy(theme_a_menu_hilite);
178
179 menu->invalid = TRUE;
180
181 g_hash_table_insert(menu_map, &entry->item, menu);
182 }
183
184 void menu_show(char *name, int x, int y, Client *client)
185 {
186 Menu *self;
187 GList *it;
188 int items_h;
189 int nitems = 0; /* each item, only one is used */
190 int item_y;
191
192 self = g_hash_table_lookup(menu_hash, name);
193 if (!self) {
194 g_warning("Attempted to show menu '%s' but it does not exist.",
195 name);
196 return;
197 }
198
199 self->width = 1;
200 self->item_h = 0;
201
202 /* set texture data and size them mofos out */
203 self->a_title->texture[0].data.text.string = self->label;
204 appearance_minsize(self->a_title, &self->title_min_w, &self->title_h);
205 self->title_min_w += theme_bevel * 2;
206 self->title_h += theme_bevel * 2;
207 self->width = MAX(self->width, self->title_min_w);
208
209 for (it = self->entries; it; it = it->next) {
210 MenuEntry *e = it->data;
211 int h;
212
213 e->a_item->texture[0].data.text.string = e->label;
214 appearance_minsize(e->a_item, &e->min_w, &self->item_h);
215 self->width = MAX(self->width, e->min_w);
216
217 e->a_disabled->texture[0].data.text.string = e->label;
218 appearance_minsize(e->a_disabled, &e->min_w, &h);
219 self->item_h = MAX(self->item_h, h);
220 self->width = MAX(self->width, e->min_w);
221
222 e->a_hilite->texture[0].data.text.string = e->label;
223 appearance_minsize(e->a_hilite, &e->min_w, &h);
224 self->item_h = MAX(self->item_h, h);
225 self->width = MAX(self->width, e->min_w);
226
227 e->min_w += theme_bevel * 2;
228 ++nitems;
229 }
230 self->bullet_w = self->item_h + theme_bevel;
231 self->width += 2 * self->bullet_w;
232 self->item_h += theme_bevel * 2;
233 items_h = self->item_h * nitems;
234
235 RECT_SET(self->a_title->area, 0, 0, self->width, self->title_h);
236 RECT_SET(self->a_title->texture[0].position, 0, 0, self->width,
237 self->title_h);
238 RECT_SET(self->a_items->area, 0, 0, self->width, items_h);
239
240 XMoveResizeWindow(ob_display, self->frame, x, y, self->width,
241 self->title_h + items_h);
242 XMoveResizeWindow(ob_display, self->title, -theme_bwidth, -theme_bwidth,
243 self->width, self->title_h);
244 XMoveResizeWindow(ob_display, self->items, 0, self->title_h + theme_bwidth,
245 self->width, items_h);
246
247 paint(self->title, self->a_title);
248 paint(self->items, self->a_items);
249
250 item_y = 0;
251 for (it = self->entries; it; it = it->next) {
252 ((MenuEntry*)it->data)->y = item_y;
253 menu_entry_render(it->data);
254 item_y += self->item_h;
255 }
256
257 stacking_raise_internal(self->frame);
258 XMapWindow(ob_display, self->frame);
259 }
260
261 MenuEntry *menu_find_entry(Menu *menu, Window win)
262 {
263 GList *it;
264
265 for (it = menu->entries; it; it = it->next) {
266 MenuEntry *entry = it->data;
267 if (entry->item == win)
268 return entry;
269 }
270 return NULL;
271 }
272
273 void menu_entry_render(MenuEntry *self)
274 {
275 Menu *menu = self->parent;
276 Appearance *a;
277
278 a = !self->enabled ? self->a_disabled :
279 (self->hilite ? self->a_hilite : self->a_item);
280
281 RECT_SET(a->area, 0, 0, menu->width,
282 menu->item_h);
283 RECT_SET(a->texture[0].position, menu->bullet_w,
284 0, menu->width - 2 * menu->bullet_w,
285 menu->item_h);
286
287 XMoveResizeWindow(ob_display, self->item, 0, self->y,
288 menu->width, menu->item_h);
289 a->surface.data.planar.parent = menu->a_items;
290 a->surface.data.planar.parentx = 0;
291 a->surface.data.planar.parenty = self->y;
292
293 paint(self->item, a);
294 }
This page took 0.047586 seconds and 4 git commands to generate.