5 #include "render/theme.h"
10 static GHashTable
*menu_hash
= NULL
;
12 #define FRAME_EVENTMASK (ButtonPressMask |ButtonMotionMask | EnterWindowMask | \
14 #define TITLE_EVENTMASK (ButtonPressMask | ButtonMotionMask)
15 #define ENTRY_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
16 ButtonPressMask | ButtonReleaseMask)
18 void menu_control_show(Menu
*self
, int x
, int y
, Client
*client
);
20 void menu_destroy_hash_key(gpointer data
)
25 void menu_destroy_hash_value(Menu
*self
)
29 for (it
= self
->entries
; it
; it
= it
->next
)
30 menu_entry_free(it
->data
);
31 g_list_free(self
->entries
);
36 g_hash_table_remove(window_map
, &self
->title
);
37 g_hash_table_remove(window_map
, &self
->frame
);
38 g_hash_table_remove(window_map
, &self
->items
);
40 stacking_remove(self
);
42 appearance_free(self
->a_title
);
43 XDestroyWindow(ob_display
, self
->title
);
44 XDestroyWindow(ob_display
, self
->frame
);
45 XDestroyWindow(ob_display
, self
->items
);
50 void menu_entry_free(MenuEntry
*self
)
53 action_free(self
->action
);
55 g_hash_table_remove(window_map
, &self
->item
);
57 appearance_free(self
->a_item
);
58 appearance_free(self
->a_disabled
);
59 appearance_free(self
->a_hilite
);
60 XDestroyWindow(ob_display
, self
->item
);
72 menu_hash
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
73 menu_destroy_hash_key
,
74 (GDestroyNotify
)menu_destroy_hash_value
);
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
));
93 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 menu_add_entry(m
, menu_entry_new_submenu("timed", t
));
101 t
= (Menu
*)plugin_create("fifo_menu");
103 menu_add_entry(m
, menu_entry_new_submenu("fifo", t
));
106 s
= menu_new("empty", "chub", m
);
107 menu_add_entry(m
, menu_entry_new_submenu("empty", s
));
109 s
= menu_new("", "s-club", m
);
110 menu_add_entry(m
, menu_entry_new_submenu("empty", s
));
112 s
= menu_new(NULL
, "h-club", m
);
113 menu_add_entry(m
, menu_entry_new_submenu("empty", s
));
115 s
= menu_new(NULL
, "g-club", m
);
117 a
= action_from_string("execute");
118 a
->data
.execute
.path
= g_strdup("xterm");
119 menu_add_entry(s
, menu_entry_new("xterm", a
));
120 a
= action_from_string("restart");
121 menu_add_entry(s
, menu_entry_new("restart", a
));
122 menu_add_entry(s
, menu_entry_new_separator("--"));
123 a
= action_from_string("exit");
124 menu_add_entry(s
, menu_entry_new("exit", a
));
126 menu_add_entry(m
, menu_entry_new_submenu("long", s
));
128 m
= menu_new(NULL
, "client", NULL
);
129 a
= action_from_string("iconify");
130 menu_add_entry(m
, menu_entry_new("iconify", a
));
131 a
= action_from_string("toggleshade");
132 menu_add_entry(m
, menu_entry_new("(un)shade", a
));
133 a
= action_from_string("togglemaximizefull");
134 menu_add_entry(m
, menu_entry_new("(un)maximize", a
));
135 a
= action_from_string("close");
136 menu_add_entry(m
, menu_entry_new("close", a
));
142 g_hash_table_destroy(menu_hash
);
145 static Window
createWindow(Window parent
, unsigned long mask
,
146 XSetWindowAttributes
*attrib
)
148 return XCreateWindow(ob_display
, parent
, 0, 0, 1, 1, 0,
149 render_depth
, InputOutput
, render_visual
,
154 Menu
*menu_new_full(char *label
, char *name
, Menu
*parent
,
155 menu_controller_show show
, menu_controller_update update
)
157 XSetWindowAttributes attrib
;
160 self
= g_new0(Menu
, 1);
161 self
->obwin
.type
= Window_Menu
;
162 self
->label
= g_strdup(label
);
163 self
->name
= g_strdup(name
);
164 self
->parent
= parent
;
165 self
->open_submenu
= NULL
;
167 self
->entries
= NULL
;
169 self
->invalid
= TRUE
;
171 /* default controllers */
174 self
->update
= update
;
175 self
->mouseover
= NULL
;
176 self
->selected
= NULL
;
179 self
->plugin_data
= NULL
;
181 attrib
.override_redirect
= TRUE
;
182 attrib
.event_mask
= FRAME_EVENTMASK
;
183 self
->frame
= createWindow(ob_root
, CWOverrideRedirect
|CWEventMask
, &attrib
);
184 attrib
.event_mask
= TITLE_EVENTMASK
;
185 self
->title
= createWindow(self
->frame
, CWEventMask
, &attrib
);
186 self
->items
= createWindow(self
->frame
, 0, &attrib
);
188 XSetWindowBorderWidth(ob_display
, self
->frame
, theme_bwidth
);
189 XSetWindowBackground(ob_display
, self
->frame
, theme_b_color
->pixel
);
190 XSetWindowBorderWidth(ob_display
, self
->title
, theme_bwidth
);
191 XSetWindowBorder(ob_display
, self
->frame
, theme_b_color
->pixel
);
192 XSetWindowBorder(ob_display
, self
->title
, theme_b_color
->pixel
);
194 XMapWindow(ob_display
, self
->title
);
195 XMapWindow(ob_display
, self
->items
);
197 self
->a_title
= appearance_copy(theme_a_menu_title
);
198 self
->a_items
= appearance_copy(theme_a_menu
);
200 g_hash_table_insert(window_map
, &self
->frame
, self
);
201 g_hash_table_insert(window_map
, &self
->title
, self
);
202 g_hash_table_insert(window_map
, &self
->items
, self
);
203 g_hash_table_insert(menu_hash
, g_strdup(name
), self
);
205 stacking_add(MENU_AS_WINDOW(self
));
206 stacking_raise(MENU_AS_WINDOW(self
));
211 void menu_free(char *name
)
213 g_hash_table_remove(menu_hash
, name
);
216 MenuEntry
*menu_entry_new_full(char *label
, Action
*action
,
217 MenuEntryRenderType render_type
,
220 MenuEntry
*menu_entry
= g_new0(MenuEntry
, 1);
222 menu_entry
->label
= g_strdup(label
);
223 menu_entry
->render_type
= render_type
;
224 menu_entry
->action
= action
;
226 menu_entry
->hilite
= FALSE
;
227 menu_entry
->enabled
= TRUE
;
229 menu_entry
->submenu
= submenu
;
234 void menu_entry_set_submenu(MenuEntry
*entry
, Menu
*submenu
)
236 g_assert(entry
!= NULL
);
238 entry
->submenu
= submenu
;
240 if(entry
->parent
!= NULL
)
241 entry
->parent
->invalid
= TRUE
;
244 void menu_add_entry(Menu
*menu
, MenuEntry
*entry
)
246 XSetWindowAttributes attrib
;
248 g_assert(menu
!= NULL
);
249 g_assert(entry
!= NULL
);
250 g_assert(entry
->item
== None
);
252 menu
->entries
= g_list_append(menu
->entries
, entry
);
253 entry
->parent
= menu
;
255 attrib
.event_mask
= ENTRY_EVENTMASK
;
256 entry
->item
= createWindow(menu
->items
, CWEventMask
, &attrib
);
257 XMapWindow(ob_display
, entry
->item
);
258 entry
->a_item
= appearance_copy(theme_a_menu_item
);
259 entry
->a_disabled
= appearance_copy(theme_a_menu_disabled
);
260 entry
->a_hilite
= appearance_copy(theme_a_menu_hilite
);
262 menu
->invalid
= TRUE
;
264 g_hash_table_insert(window_map
, &entry
->item
, menu
);
267 void menu_show(char *name
, int x
, int y
, Client
*client
)
271 self
= g_hash_table_lookup(menu_hash
, name
);
273 g_warning("Attempted to show menu '%s' but it does not exist.",
278 menu_show_full(self
, x
, y
, client
);
281 void menu_show_full(Menu
*self
, int x
, int y
, Client
*client
)
283 g_assert(self
!= NULL
);
287 self
->client
= client
;
290 self
->show(self
, x
, y
, client
);
292 menu_control_show(self
, x
, y
, client
);
297 void menu_hide(Menu
*self
) {
299 XUnmapWindow(ob_display
, self
->frame
);
301 if (self
->open_submenu
)
302 menu_hide(self
->open_submenu
);
303 if (self
->parent
&& self
->parent
->open_submenu
== self
)
304 self
->parent
->open_submenu
= NULL
;
309 void menu_clear(Menu
*self
) {
312 for (it
= self
->entries
; it
; it
= it
->next
) {
313 MenuEntry
*entry
= it
->data
;
314 menu_entry_free(entry
);
316 self
->entries
= NULL
;
317 self
->invalid
= TRUE
;
321 MenuEntry
*menu_find_entry(Menu
*menu
, Window win
)
325 for (it
= menu
->entries
; it
; it
= it
->next
) {
326 MenuEntry
*entry
= it
->data
;
327 if (entry
->item
== win
)
333 void menu_entry_fire(MenuEntry
*self
)
338 self
->action
->data
.any
.c
= self
->parent
->client
;
339 self
->action
->func(&self
->action
->data
);
341 /* hide the whole thing */
343 while (m
->parent
) m
= m
->parent
;
349 Default menu controller action for showing.
352 void menu_control_show(Menu
*self
, int x
, int y
, Client
*client
) {
353 g_assert(!self
->invalid
);
355 XMoveWindow(ob_display
, self
->frame
,
356 MIN(x
, screen_physical_size
.width
- self
->size
.width
),
357 MIN(y
, screen_physical_size
.height
- self
->size
.height
));
358 POINT_SET(self
->location
,
359 MIN(x
, screen_physical_size
.width
- self
->size
.width
),
360 MIN(y
, screen_physical_size
.height
- self
->size
.height
));
363 XMapWindow(ob_display
, self
->frame
);
364 stacking_raise(MENU_AS_WINDOW(self
));
366 } else if (self
->shown
&& self
->open_submenu
) {
367 menu_hide(self
->open_submenu
);
371 void menu_control_mouseover(MenuEntry
*self
, gboolean enter
) {
373 self
->hilite
= enter
;
376 if (self
->parent
->open_submenu
&& self
->submenu
377 != self
->parent
->open_submenu
)
378 menu_hide(self
->parent
->open_submenu
);
381 self
->parent
->open_submenu
= self
->submenu
;
383 /* shouldn't be invalid since it must be displayed */
384 g_assert(!self
->parent
->invalid
);
385 /* TODO: I don't understand why these bevels should be here.
386 Something must be wrong in the width calculation */
387 x
= self
->parent
->location
.x
+ self
->parent
->size
.width
+
390 /* need to get the width. is this bad?*/
391 menu_render(self
->submenu
);
393 if (self
->submenu
->size
.width
+ x
> screen_physical_size
.width
)
394 x
= self
->parent
->location
.x
- self
->submenu
->size
.width
-
397 menu_show_full(self
->submenu
, x
,
398 self
->parent
->location
.y
+ self
->y
, NULL
);