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