5 #include "render/theme.h"
7 static GHashTable
*menu_hash
= NULL
;
8 GHashTable
*menu_map
= NULL
;
10 #define FRAME_EVENTMASK (ButtonMotionMask | EnterWindowMask | LeaveWindowMask)
11 #define TITLE_EVENTMASK (ButtonPressMask | ButtonMotionMask)
12 #define ENTRY_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
13 ButtonPressMask | ButtonReleaseMask)
15 void menu_destroy_hash_key(gpointer data
)
20 void menu_destroy_hash_value(Menu
*self
)
24 for (it
= self
->entries
; it
; it
= it
->next
)
25 menu_entry_free(it
->data
);
26 g_list_free(self
->entries
);
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
);
35 appearance_free(self
->a_title
);
36 XDestroyWindow(ob_display
, self
->title
);
37 XDestroyWindow(ob_display
, self
->frame
);
38 XDestroyWindow(ob_display
, self
->items
);
43 void menu_entry_free(MenuEntry
*self
)
46 action_free(self
->action
);
48 g_hash_table_remove(menu_map
, &self
->item
);
50 appearance_free(self
->a_item
);
51 appearance_free(self
->a_disabled
);
52 appearance_free(self
->a_hilite
);
53 XDestroyWindow(ob_display
, self
->item
);
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
);
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
));
81 g_hash_table_destroy(menu_hash
);
82 g_hash_table_destroy(menu_map
);
85 static Window
createWindow(Window parent
, unsigned long mask
,
86 XSetWindowAttributes
*attrib
)
88 return XCreateWindow(ob_display
, parent
, 0, 0, 1, 1, 0,
89 render_depth
, InputOutput
, render_visual
,
94 Menu
*menu_new(char *label
, char *name
, Menu
*parent
)
96 XSetWindowAttributes attrib
;
99 self
= g_new0(Menu
, 1);
100 self
->label
= g_strdup(label
);
101 self
->name
= g_strdup(name
);
102 self
->parent
= parent
;
104 self
->entries
= NULL
;
106 self
->invalid
= FALSE
;
107 /* default controllers? */
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
);
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
);
121 XMapWindow(ob_display
, self
->title
);
122 XMapWindow(ob_display
, self
->items
);
124 self
->a_title
= appearance_copy(theme_a_menu_title
);
125 self
->a_items
= appearance_copy(theme_a_menu
);
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
);
134 void menu_free(char *name
)
136 g_hash_table_remove(menu_hash
, name
);
139 MenuEntry
*menu_entry_new_full(char *label
, Action
*action
,
140 MenuEntryRenderType render_type
,
143 MenuEntry
*menu_entry
= g_new0(MenuEntry
, 1);
145 menu_entry
->label
= g_strdup(label
);
146 menu_entry
->render_type
= render_type
;
147 menu_entry
->action
= action
;
149 menu_entry
->hilite
= FALSE
;
150 menu_entry
->enabled
= TRUE
;
152 menu_entry
->submenu
= submenu
;
157 void menu_entry_set_submenu(MenuEntry
*entry
, Menu
*submenu
)
159 g_assert(entry
!= NULL
);
161 entry
->submenu
= submenu
;
163 if(entry
->parent
!= NULL
)
164 entry
->parent
->invalid
= TRUE
;
167 void menu_add_entry(Menu
*menu
, MenuEntry
*entry
)
169 XSetWindowAttributes attrib
;
171 g_assert(menu
!= NULL
&& entry
!= NULL
&& entry
->item
== None
);
173 menu
->entries
= g_list_append(menu
->entries
, entry
);
174 entry
->parent
= menu
;
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
);
183 menu
->invalid
= TRUE
;
185 g_hash_table_insert(menu_map
, &entry
->item
, menu
);
188 void menu_show(char *name
, int x
, int y
, Client
*client
)
193 int nitems
= 0; /* each item, only one is used */
196 self
= g_hash_table_lookup(menu_hash
, name
);
198 g_warning("Attempted to show menu '%s' but it does not exist.",
203 self
->client
= client
;
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
);
215 for (it
= self
->entries
; it
; it
= it
->next
) {
216 MenuEntry
*e
= it
->data
;
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
);
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
);
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
);
233 e
->min_w
+= theme_bevel
* 2;
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
;
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
,
244 RECT_SET(self
->a_items
->area
, 0, 0, self
->width
, items_h
);
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
);
253 paint(self
->title
, self
->a_title
);
254 paint(self
->items
, self
->a_items
);
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
;
264 stacking_raise_internal(self
->frame
);
265 XMapWindow(ob_display
, self
->frame
);
266 /* grab_pointer_window(TRUE, None, self->frame);*/
271 void menu_hide(Menu
*self
) {
273 XUnmapWindow(ob_display
, self
->frame
);
278 MenuEntry
*menu_find_entry(Menu
*menu
, Window win
)
282 for (it
= menu
->entries
; it
; it
= it
->next
) {
283 MenuEntry
*entry
= it
->data
;
284 if (entry
->item
== win
)
290 void menu_entry_render(MenuEntry
*self
)
292 Menu
*menu
= self
->parent
;
295 a
= !self
->enabled
? self
->a_disabled
:
296 (self
->hilite
&& self
->action
? self
->a_hilite
: self
->a_item
);
298 RECT_SET(a
->area
, 0, 0, menu
->width
,
300 RECT_SET(a
->texture
[0].position
, menu
->bullet_w
,
301 0, menu
->width
- 2 * menu
->bullet_w
,
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
;
310 paint(self
->item
, a
);
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
;