#include "client.h"
#include "menu.h"
#include "screen.h"
-#include "prop.h"
#include "actions.h"
#include "event.h"
#include "grab.h"
#include "openbox.h"
-#include "mainloop.h"
#include "config.h"
-#include "render/theme.h"
+#include "obt/prop.h"
+#include "obt/keyboard.h"
+#include "obrender/theme.h"
#define PADDING 2
#define MAX_MENU_WIDTH 400
#define FRAME_EVENTMASK (ButtonPressMask |ButtonMotionMask | EnterWindowMask |\
LeaveWindowMask)
#define ENTRY_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
- ButtonPressMask | ButtonReleaseMask)
+ ButtonPressMask | ButtonReleaseMask | \
+ PointerMotionMask)
GList *menu_frame_visible;
GHashTable *menu_frame_map;
static RrAppearance *a_sep;
+static guint submenu_show_timer = 0;
+static guint submenu_hide_timer = 0;
static ObMenuEntryFrame* menu_entry_frame_new(ObMenuEntry *entry,
ObMenuFrame *frame);
static Window createWindow(Window parent, gulong mask,
XSetWindowAttributes *attrib)
{
- return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
+ return XCreateWindow(obt_display, parent, 0, 0, 1, 1, 0,
RrDepth(ob_rr_inst), InputOutput,
RrVisual(ob_rr_inst), mask, attrib);
}
+static void client_dest(ObClient *client, gpointer data)
+{
+ GList *it;
+
+ /* menus can be associated with a client, so null those refs since
+ we are disappearing now */
+ for (it = menu_frame_visible; it; it = g_list_next(it)) {
+ ObMenuFrame *f = it->data;
+ if (f->client == client)
+ f->client = NULL;
+ }
+}
+
void menu_frame_startup(gboolean reconfig)
{
gint i;
if (reconfig) return;
+ client_add_destroy_notify(client_dest, NULL);
menu_frame_map = g_hash_table_new(g_int_hash, g_int_equal);
}
if (reconfig) return;
+ client_remove_destroy_notify(client_dest);
g_hash_table_destroy(menu_frame_map);
}
ObMenuFrame *self;
XSetWindowAttributes attr;
- self = g_new0(ObMenuFrame, 1);
- self->type = Window_Menu;
+ self = g_slice_new0(ObMenuFrame);
+ self->obwin.type = OB_WINDOW_CLASS_MENUFRAME;
self->menu = menu;
self->selected = NULL;
self->client = client;
self->show_from = show_from;
attr.event_mask = FRAME_EVENTMASK;
- self->window = createWindow(RootWindow(ob_display, ob_screen),
+ self->window = createWindow(obt_root(ob_screen),
CWEventMask, &attr);
/* make it a popup menu type window */
- PROP_SET32(self->window, net_wm_window_type, atom,
- prop_atoms.net_wm_window_type_popup_menu);
+ OBT_PROP_SET32(self->window, NET_WM_WINDOW_TYPE, ATOM,
+ OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_POPUP_MENU));
- XSetWindowBorderWidth(ob_display, self->window, ob_rr_theme->mbwidth);
- XSetWindowBorder(ob_display, self->window,
+ XSetWindowBorderWidth(obt_display, self->window, ob_rr_theme->mbwidth);
+ XSetWindowBorder(obt_display, self->window,
RrColorPixel(ob_rr_theme->menu_border_color));
self->a_items = RrAppearanceCopy(ob_rr_theme->a_menu);
- stacking_add(MENU_AS_WINDOW(self));
+ window_add(&self->window, MENUFRAME_AS_WINDOW(self));
+ stacking_add(MENUFRAME_AS_WINDOW(self));
return self;
}
self->entries = g_list_delete_link(self->entries, self->entries);
}
- stacking_remove(MENU_AS_WINDOW(self));
+ stacking_remove(MENUFRAME_AS_WINDOW(self));
+ window_remove(self->window);
RrAppearanceFree(self->a_items);
- XDestroyWindow(ob_display, self->window);
+ XDestroyWindow(obt_display, self->window);
- g_free(self);
+ g_slice_free(ObMenuFrame, self);
}
}
+ObtIC* menu_frame_ic(ObMenuFrame *self)
+{
+ /* menus are always used through a grab right now, so they can always use
+ the grab input context */
+ return grab_input_context();
+}
+
static ObMenuEntryFrame* menu_entry_frame_new(ObMenuEntry *entry,
ObMenuFrame *frame)
{
ObMenuEntryFrame *self;
XSetWindowAttributes attr;
- self = g_new0(ObMenuEntryFrame, 1);
+ self = g_slice_new0(ObMenuEntryFrame);
self->entry = entry;
self->frame = frame;
self->text = createWindow(self->window, 0, NULL);
g_hash_table_insert(menu_frame_map, &self->window, self);
g_hash_table_insert(menu_frame_map, &self->text, self);
- if (entry->type == OB_MENU_ENTRY_TYPE_NORMAL) {
+ if ((entry->type == OB_MENU_ENTRY_TYPE_NORMAL) ||
+ (entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)) {
self->icon = createWindow(self->window, 0, NULL);
g_hash_table_insert(menu_frame_map, &self->icon, self);
}
g_hash_table_insert(menu_frame_map, &self->bullet, self);
}
- XMapWindow(ob_display, self->window);
- XMapWindow(ob_display, self->text);
+ XMapWindow(obt_display, self->window);
+ XMapWindow(obt_display, self->text);
+
+ window_add(&self->window, MENUFRAME_AS_WINDOW(self->frame));
return self;
}
if (self) {
menu_entry_unref(self->entry);
- XDestroyWindow(ob_display, self->text);
- XDestroyWindow(ob_display, self->window);
+ window_remove(self->window);
+
+ XDestroyWindow(obt_display, self->text);
+ XDestroyWindow(obt_display, self->window);
g_hash_table_remove(menu_frame_map, &self->text);
g_hash_table_remove(menu_frame_map, &self->window);
- if (self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL) {
- XDestroyWindow(ob_display, self->icon);
+ if ((self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL) ||
+ (self->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)) {
+ XDestroyWindow(obt_display, self->icon);
g_hash_table_remove(menu_frame_map, &self->icon);
}
if (self->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) {
- XDestroyWindow(ob_display, self->bullet);
+ XDestroyWindow(obt_display, self->bullet);
g_hash_table_remove(menu_frame_map, &self->bullet);
}
- g_free(self);
+ g_slice_free(ObMenuEntryFrame, self);
}
}
{
RECT_SET_POINT(self->area, x, y);
self->monitor = screen_find_monitor_point(x, y);
- XMoveWindow(ob_display, self->window, self->area.x, self->area.y);
+ XMoveWindow(obt_display, self->window, self->area.x, self->area.y);
}
static void menu_frame_place_topmenu(ObMenuFrame *self, gint *x, gint *y)
void menu_frame_move_on_screen(ObMenuFrame *self, gint x, gint y,
gint *dx, gint *dy)
{
- Rect *a = NULL;
- gint pos, half;
+ const Rect *a = NULL;
+ Rect search = self->area;
+ gint pos, half, monitor;
*dx = *dy = 0;
+ RECT_SET_POINT(search, x, y);
+
+ if (self->parent)
+ monitor = self->parent->monitor;
+ else
+ monitor = screen_find_monitor(&search);
- a = screen_physical_area_monitor(screen_find_monitor_point(x, y));
+ a = screen_physical_area_monitor(monitor);
half = g_list_length(self->entries) / 2;
pos = g_list_index(self->entries, self->selected);
*dx = MAX(*dx, a->x - x);
*dy = MAX(*dy, a->y - y);
}
-
- g_free(a);
}
static void menu_entry_frame_render(ObMenuEntryFrame *self)
}
RECT_SET_SIZE(self->area, self->frame->inner_w, th);
- XResizeWindow(ob_display, self->window,
+ XResizeWindow(obt_display, self->window,
self->area.width, self->area.height);
item_a->surface.parent = self->frame->a_items;
item_a->surface.parentx = self->area.x;
ob_rr_theme->a_menu_text_normal);
sub = self->entry->data.submenu.submenu;
text_a->texture[0].data.text.string = sub ? sub->title : "";
- if (sub->shortcut && (self->frame->menu->show_all_shortcuts ||
+ if (sub && sub->shortcut && (self->frame->menu->show_all_shortcuts ||
sub->shortcut_always_show ||
sub->shortcut_position > 0))
{
else
text_a = ob_rr_theme->a_menu_text_normal;
break;
+ default:
+ g_assert_not_reached();
}
switch (self->entry->type) {
case OB_MENU_ENTRY_TYPE_NORMAL:
- XMoveResizeWindow(ob_display, self->text,
+ XMoveResizeWindow(obt_display, self->text,
self->frame->text_x, PADDING,
self->frame->text_w,
ITEM_HEIGHT - 2*PADDING);
ITEM_HEIGHT - 2*PADDING);
break;
case OB_MENU_ENTRY_TYPE_SUBMENU:
- XMoveResizeWindow(ob_display, self->text,
+ XMoveResizeWindow(obt_display, self->text,
self->frame->text_x, PADDING,
self->frame->text_w - ITEM_HEIGHT,
ITEM_HEIGHT - 2*PADDING);
case OB_MENU_ENTRY_TYPE_SEPARATOR:
if (self->entry->data.separator.label != NULL) {
/* labeled separator */
- XMoveResizeWindow(ob_display, self->text,
+ XMoveResizeWindow(obt_display, self->text,
ob_rr_theme->paddingx, ob_rr_theme->paddingy,
self->area.width - 2*ob_rr_theme->paddingx,
ob_rr_theme->menu_title_height -
gint i;
/* unlabeled separator */
- XMoveResizeWindow(ob_display, self->text, 0, 0,
+ XMoveResizeWindow(obt_display, self->text, 0, 0,
self->area.width,
ob_rr_theme->menu_sep_width +
2*ob_rr_theme->menu_sep_paddingy);
2*ob_rr_theme->menu_sep_paddingy);
}
break;
+ default:
+ g_assert_not_reached();
}
- if (self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
+ if (((self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL) ||
+ (self->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)) &&
self->entry->data.normal.icon)
{
RrAppearance *clear;
- XMoveResizeWindow(ob_display, self->icon,
+ XMoveResizeWindow(obt_display, self->icon,
PADDING, frame->item_margin.top,
ITEM_HEIGHT - frame->item_margin.top
- frame->item_margin.bottom,
- frame->item_margin.bottom,
ITEM_HEIGHT - frame->item_margin.top
- frame->item_margin.bottom);
- XMapWindow(ob_display, self->icon);
+ XMapWindow(obt_display, self->icon);
} else if (self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
self->entry->data.normal.mask)
{
RrColor *c;
RrAppearance *clear;
- XMoveResizeWindow(ob_display, self->icon,
+ XMoveResizeWindow(obt_display, self->icon,
PADDING, frame->item_margin.top,
ITEM_HEIGHT - frame->item_margin.top
- frame->item_margin.bottom,
- frame->item_margin.bottom,
ITEM_HEIGHT - frame->item_margin.top
- frame->item_margin.bottom);
- XMapWindow(ob_display, self->icon);
+ XMapWindow(obt_display, self->icon);
} else
- XUnmapWindow(ob_display, self->icon);
+ XUnmapWindow(obt_display, self->icon);
if (self->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) {
RrAppearance *bullet_a;
- XMoveResizeWindow(ob_display, self->bullet,
+ XMoveResizeWindow(obt_display, self->bullet,
self->frame->text_x + self->frame->text_w -
ITEM_HEIGHT + PADDING, PADDING,
ITEM_HEIGHT - 2*PADDING,
RrPaint(bullet_a, self->bullet,
ITEM_HEIGHT - 2*PADDING,
ITEM_HEIGHT - 2*PADDING);
- XMapWindow(ob_display, self->bullet);
+ XMapWindow(obt_display, self->bullet);
} else
- XUnmapWindow(ob_display, self->bullet);
+ XUnmapWindow(obt_display, self->bullet);
- XFlush(ob_display);
+ XFlush(obt_display);
}
/*! this code is taken from the menu_frame_render. if that changes, this won't
}
RECT_SET_POINT(e->area, 0, h+e->border);
- XMoveWindow(ob_display, e->window,
+ XMoveWindow(obt_display, e->window,
e->area.x-e->border, e->area.y-e->border);
- XSetWindowBorderWidth(ob_display, e->window, e->border);
- XSetWindowBorder(ob_display, e->window,
+ XSetWindowBorderWidth(obt_display, e->window, e->border);
+ XSetWindowBorder(obt_display, e->window,
RrColorPixel(ob_rr_theme->menu_border_color));
text_a = (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
2*ob_rr_theme->menu_sep_paddingy - 2*PADDING;
}
break;
+ default:
+ g_assert_not_reached();
}
tw += 2*PADDING;
th += 2*PADDING;
if (!w) w = 10;
if (!h) h = 3;
- XResizeWindow(ob_display, self->window, w, h);
+ XResizeWindow(obt_display, self->window, w, h);
self->inner_w = w;
RECT_SET_SIZE(self->area, w, h);
- XFlush(ob_display);
+ XFlush(obt_display);
}
static void menu_frame_update(ObMenuFrame *self)
{
GList *mit, *fit;
- Rect *a;
+ const Rect *a;
gint h;
menu_pipe_execute(self->menu);
}
}
- g_free(a);
-
menu_frame_render(self);
}
menu_frame_move(self, x, y);
- XMapWindow(ob_display, self->window);
+ XMapWindow(obt_display, self->window);
if (screen_pointer_pos(&px, &py)) {
ObMenuEntryFrame *e = menu_entry_frame_under(px, py);
*/
static void remove_submenu_hide_timeout(ObMenuFrame *child)
{
- ob_main_loop_timeout_remove_data(ob_main_loop, submenu_hide_timeout,
- child, FALSE);
+ if (submenu_hide_timer) g_source_remove(submenu_hide_timer);
+ submenu_hide_timer = 0;
}
gboolean menu_frame_show_submenu(ObMenuFrame *self, ObMenuFrame *parent,
parent->child_entry = parent_entry;
}
- if (!menu_frame_show(self))
+ if (!menu_frame_show(self)) {
+ parent->child = NULL;
+ parent->child_entry = NULL;
return FALSE;
+ }
menu_frame_place_submenu(self, &x, &y);
menu_frame_move_on_screen(self, x, y, &dx, &dy);
}
menu_frame_move(self, x + dx, y + dy);
- XMapWindow(ob_display, self->window);
+ XMapWindow(obt_display, self->window);
if (screen_pointer_pos(&px, &py)) {
ObMenuEntryFrame *e = menu_entry_frame_under(px, py);
static void menu_frame_hide(ObMenuFrame *self)
{
+ ObMenu *const menu = self->menu;
GList *it = g_list_find(menu_frame_visible, self);
gulong ignore_start;
if (!it)
return;
- if (self->menu->hide_func)
- self->menu->hide_func(self, self->menu->data);
+ if (menu->hide_func)
+ menu->hide_func(self, menu->data);
if (self->child)
menu_frame_hide(self->child);
}
ignore_start = event_start_ignore_all_enters();
- XUnmapWindow(ob_display, self->window);
+ XUnmapWindow(obt_display, self->window);
event_end_ignore_all_enters(ignore_start);
menu_frame_free(self);
+
+ if (menu->cleanup_func)
+ menu->cleanup_func(menu, menu->data);
}
void menu_frame_hide_all(void)
if (config_submenu_show_delay) {
/* remove any submenu open requests */
- ob_main_loop_timeout_remove(ob_main_loop, submenu_show_timeout);
+ if (submenu_show_timer) g_source_remove(submenu_show_timer);
+ submenu_show_timer = 0;
}
if ((it = g_list_last(menu_frame_visible)))
menu_frame_hide(it->data);
}
-void menu_frame_hide_all_client(ObClient *client)
-{
- GList *it = g_list_last(menu_frame_visible);
- if (it) {
- ObMenuFrame *f = it->data;
- if (f->client == client) {
- if (config_submenu_show_delay) {
- /* remove any submenu open requests */
- ob_main_loop_timeout_remove(ob_main_loop,
- submenu_show_timeout);
- }
- menu_frame_hide(f);
- }
- }
-}
-
ObMenuFrame* menu_frame_under(gint x, gint y)
{
ObMenuFrame *ret = NULL;
if (config_submenu_show_delay) {
/* remove any submenu open requests */
- ob_main_loop_timeout_remove(ob_main_loop, submenu_show_timeout);
+ if (submenu_show_timer) g_source_remove(submenu_show_timer);
+ submenu_show_timer = 0;
}
self->selected = entry;
submenu */
if (immediate || config_submenu_hide_delay == 0)
menu_frame_hide(oldchild);
- else if (config_submenu_hide_delay > 0)
- ob_main_loop_timeout_add(ob_main_loop,
- config_submenu_hide_delay * 1000,
- submenu_hide_timeout,
- oldchild, g_direct_equal,
- NULL);
+ else if (config_submenu_hide_delay > 0) {
+ if (submenu_hide_timer) g_source_remove(submenu_hide_timer);
+ submenu_hide_timer =
+ g_timeout_add_full(G_PRIORITY_DEFAULT,
+ config_submenu_hide_delay,
+ submenu_hide_timeout, oldchild, NULL);
+ }
}
}
if (oldchild_entry != self->selected) {
if (immediate || config_submenu_hide_delay == 0)
menu_entry_frame_show_submenu(self->selected);
- else if (config_submenu_hide_delay > 0)
- ob_main_loop_timeout_add(ob_main_loop,
- config_submenu_show_delay * 1000,
- submenu_show_timeout,
- self->selected, g_direct_equal,
- NULL);
+ else if (config_submenu_hide_delay > 0) {
+ if (submenu_show_timer)
+ g_source_remove(submenu_show_timer);
+ submenu_show_timer =
+ g_timeout_add_full(G_PRIORITY_DEFAULT,
+ config_submenu_show_delay,
+ submenu_show_timeout,
+ self->selected, NULL);
+ }
}
/* hide the grandchildren of this menu. and move the cursor to
the current menu */
/* pass our direction on to our child */
f->direction_right = self->frame->direction_right;
- menu_frame_show_submenu(f, self->frame, self);
+ if (!menu_frame_show_submenu(f, self->frame, self))
+ menu_frame_free(f);
}
void menu_entry_frame_execute(ObMenuEntryFrame *self, guint state)
GSList *acts = self->entry->data.normal.actions;
ObClient *client = self->frame->client;
ObMenuFrame *frame = self->frame;
+ guint mods = obt_keyboard_only_modmasks(state);
/* release grabs before executing the shit */
- if (!(state & ControlMask)) {
- menu_frame_hide_all();
+ if (!(mods & ControlMask)) {
+ event_cancel_all_key_grabs();
frame = NULL;
}
}
menu_frame_select(self, it ? it->data : NULL, FALSE);
}
+
+void menu_frame_select_first(ObMenuFrame *self)
+{
+ GList *it = NULL;
+
+ if (self->entries) {
+ for (it = self->entries; it; it = g_list_next(it)) {
+ ObMenuEntryFrame *e = it->data;
+ if (e->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)
+ break;
+ if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL)
+ break;
+ }
+ }
+ menu_frame_select(self, it ? it->data : NULL, FALSE);
+}
+
+void menu_frame_select_last(ObMenuFrame *self)
+{
+ GList *it = NULL;
+
+ if (self->entries) {
+ for (it = g_list_last(self->entries); it; it = g_list_previous(it)) {
+ ObMenuEntryFrame *e = it->data;
+ if (e->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)
+ break;
+ if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL)
+ break;
+ }
+ }
+ menu_frame_select(self, it ? it->data : NULL, FALSE);
+}