]> Dogcows Code - chaz/openbox/blob - openbox/menu.c
Menu uber patch
[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 #include "screen.h"
7 #include "geom.h"
8 #include "plugin.h"
9
10 static GHashTable *menu_hash = NULL;
11 GHashTable *menu_map = 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 void menu_control_show(Menu *self, int x, int y, Client *client);
20
21 void menu_destroy_hash_key(gpointer data)
22 {
23 g_free(data);
24 }
25
26 void menu_destroy_hash_value(Menu *self)
27 {
28 GList *it;
29
30 for (it = self->entries; it; it = it->next)
31 menu_entry_free(it->data);
32 g_list_free(self->entries);
33
34 g_free(self->label);
35 g_free(self->name);
36
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);
40
41 appearance_free(self->a_title);
42 XDestroyWindow(ob_display, self->title);
43 XDestroyWindow(ob_display, self->frame);
44 XDestroyWindow(ob_display, self->items);
45
46 g_free(self);
47 }
48
49 void menu_entry_free(MenuEntry *self)
50 {
51 g_free(self->label);
52 action_free(self->action);
53
54 g_hash_table_remove(menu_map, &self->item);
55
56 appearance_free(self->a_item);
57 appearance_free(self->a_disabled);
58 appearance_free(self->a_hilite);
59 XDestroyWindow(ob_display, self->item);
60
61 g_free(self);
62 }
63
64 void menu_startup()
65 {
66 Menu *m;
67 Menu *s;
68 Menu *t;
69 Action *a;
70
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);
75
76 m = menu_new(NULL, "root", NULL);
77
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));
90
91 menu_add_entry(m, menu_entry_new_submenu("subz", s));
92
93 /*
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));*/
98
99 s = menu_new("empty", "chub", m);
100 menu_add_entry(m, menu_entry_new_submenu("empty", s));
101
102 s = menu_new("", "s-club", m);
103 menu_add_entry(m, menu_entry_new_submenu("empty", s));
104
105 s = menu_new(NULL, "h-club", m);
106 menu_add_entry(m, menu_entry_new_submenu("empty", s));
107
108 s = menu_new(NULL, "g-club", m);
109
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));
118
119 menu_add_entry(m, menu_entry_new_submenu("long", s));
120
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));
130
131 }
132
133 void menu_shutdown()
134 {
135 g_hash_table_destroy(menu_hash);
136 g_hash_table_destroy(menu_map);
137 }
138
139 static Window createWindow(Window parent, unsigned long mask,
140 XSetWindowAttributes *attrib)
141 {
142 return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
143 render_depth, InputOutput, render_visual,
144 mask, attrib);
145
146 }
147
148 Menu *menu_new_full(char *label, char *name, Menu *parent,
149 menu_controller_show show, menu_controller_update update)
150 {
151 XSetWindowAttributes attrib;
152 Menu *self;
153
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;
159
160 self->entries = NULL;
161 self->shown = FALSE;
162 self->invalid = TRUE;
163
164 /* default controllers */
165 self->show = show;
166 self->hide = NULL;
167 self->update = update;
168 self->mouseover = NULL;
169 self->selected = NULL;
170
171 self->plugin = NULL;
172 self->plugin_data = NULL;
173
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);
180
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);
186
187 XMapWindow(ob_display, self->title);
188 XMapWindow(ob_display, self->items);
189
190 self->a_title = appearance_copy(theme_a_menu_title);
191 self->a_items = appearance_copy(theme_a_menu);
192
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);
197 return self;
198 }
199
200 void menu_free(char *name)
201 {
202 g_hash_table_remove(menu_hash, name);
203 }
204
205 MenuEntry *menu_entry_new_full(char *label, Action *action,
206 MenuEntryRenderType render_type,
207 gpointer submenu)
208 {
209 MenuEntry *menu_entry = g_new0(MenuEntry, 1);
210
211 menu_entry->label = g_strdup(label);
212 menu_entry->render_type = render_type;
213 menu_entry->action = action;
214
215 menu_entry->hilite = FALSE;
216 menu_entry->enabled = TRUE;
217
218 menu_entry->submenu = submenu;
219
220 return menu_entry;
221 }
222
223 void menu_entry_set_submenu(MenuEntry *entry, Menu *submenu)
224 {
225 g_assert(entry != NULL);
226
227 entry->submenu = submenu;
228
229 if(entry->parent != NULL)
230 entry->parent->invalid = TRUE;
231 }
232
233 void menu_add_entry(Menu *menu, MenuEntry *entry)
234 {
235 XSetWindowAttributes attrib;
236
237 g_assert(menu != NULL && entry != NULL && entry->item == None);
238
239 menu->entries = g_list_append(menu->entries, entry);
240 entry->parent = menu;
241
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);
248
249 menu->invalid = TRUE;
250
251 g_hash_table_insert(menu_map, &entry->item, menu);
252 }
253
254 void menu_show(char *name, int x, int y, Client *client)
255 {
256 Menu *self;
257
258 self = g_hash_table_lookup(menu_hash, name);
259 if (!self) {
260 g_warning("Attempted to show menu '%s' but it does not exist.",
261 name);
262 return;
263 }
264
265 menu_show_full(self, x, y, client);
266 }
267
268 void menu_show_full(Menu *self, int x, int y, Client *client)
269 {
270 g_assert(self != NULL);
271
272 menu_render(self);
273
274 self->client = client;
275
276 if (self->show) {
277 self->show(self, x, y, client);
278 } else {
279 menu_control_show(self, x, y, client);
280 }
281 }
282
283
284 void menu_hide(Menu *self) {
285 if (self->shown) {
286 XUnmapWindow(ob_display, self->frame);
287 self->shown = FALSE;
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;
292
293 }
294 }
295
296 MenuEntry *menu_find_entry(Menu *menu, Window win)
297 {
298 GList *it;
299
300 for (it = menu->entries; it; it = it->next) {
301 MenuEntry *entry = it->data;
302 if (entry->item == win)
303 return entry;
304 }
305 return NULL;
306 }
307
308 void menu_entry_fire(MenuEntry *self)
309 {
310 Menu *m;
311
312 if (self->action) {
313 self->action->data.any.c = self->parent->client;
314 self->action->func(&self->action->data);
315
316 /* hide the whole thing */
317 m = self->parent;
318 while (m->parent) m = m->parent;
319 menu_hide(m);
320 }
321 }
322
323 /*
324 Default menu controller action for showing.
325 */
326
327 void menu_control_show(Menu *self, int x, int y, Client *client) {
328 g_assert(!self->invalid);
329
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));
336
337 if (!self->shown) {
338 stacking_raise_internal(self->frame);
339 XMapWindow(ob_display, self->frame);
340 self->shown = TRUE;
341 } else if (self->shown && self->open_submenu) {
342 menu_hide(self->open_submenu);
343 }
344 }
345
346 void menu_control_mouseover(MenuEntry *self, gboolean enter) {
347 int x;
348 self->hilite = enter;
349
350 if (enter) {
351 if (self->parent->open_submenu && self->submenu
352 != self->parent->open_submenu)
353 menu_hide(self->parent->open_submenu);
354
355 if (self->submenu) {
356 self->parent->open_submenu = self->submenu;
357
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 +
363 theme_bevel;
364
365 /* need to get the width. is this bad?*/
366 menu_render(self->submenu);
367
368 if (self->submenu->size.width + x > screen_physical_size.width)
369 x = self->parent->location.x - self->submenu->size.width -
370 theme_bevel;
371
372 menu_show_full(self->submenu, x,
373 self->parent->location.y + self->y, NULL);
374 }
375 }
376 }
This page took 0.049164 seconds and 4 git commands to generate.