]> Dogcows Code - chaz/openbox/blobdiff - openbox/menuframe.c
add keyboard shortcuts to the menus. you can specify the shortcut key with & even...
[chaz/openbox] / openbox / menuframe.c
index 03e6162104f05889dd6f550701295ad98850e516..d92f47df686cc756dc7ae635dafe23e0d522cee6 100644 (file)
@@ -1,7 +1,8 @@
 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
 
    menuframe.c for the Openbox window manager
-   Copyright (c) 2003        Ben Jansens
+   Copyright (c) 2006        Mikael Magnusson
+   Copyright (c) 2003-2007   Dana Jansens
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -22,6 +23,8 @@
 #include "screen.h"
 #include "grab.h"
 #include "openbox.h"
+#include "mainloop.h"
+#include "config.h"
 #include "render/theme.h"
 
 #define PADDING 2
@@ -29,8 +32,7 @@
 #define MAX_MENU_WIDTH 400
 
 #define FRAME_EVENTMASK (ButtonPressMask |ButtonMotionMask | EnterWindowMask |\
-                        LeaveWindowMask)
-#define TITLE_EVENTMASK (ButtonPressMask | ButtonMotionMask)
+                         LeaveWindowMask)
 #define ENTRY_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
                          ButtonPressMask | ButtonReleaseMask)
 
@@ -41,15 +43,32 @@ static ObMenuEntryFrame* menu_entry_frame_new(ObMenuEntry *entry,
 static void menu_entry_frame_free(ObMenuEntryFrame *self);
 static void menu_frame_render(ObMenuFrame *self);
 static void menu_frame_update(ObMenuFrame *self);
+static gboolean menu_entry_frame_submenu_timeout(gpointer data);
 
 static Window createWindow(Window parent, gulong mask,
-                          XSetWindowAttributes *attrib)
+                           XSetWindowAttributes *attrib)
 {
     return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
-                        RrDepth(ob_rr_inst), InputOutput,
+                         RrDepth(ob_rr_inst), InputOutput,
                          RrVisual(ob_rr_inst), mask, attrib);
 }
 
+GHashTable *menu_frame_map;
+
+void menu_frame_startup(gboolean reconfig)
+{
+    if (reconfig) return;
+
+    menu_frame_map = g_hash_table_new(g_int_hash, g_int_equal);
+}
+
+void menu_frame_shutdown(gboolean reconfig)
+{
+    if (reconfig) return;
+
+    g_hash_table_destroy(menu_frame_map);
+}
+
 ObMenuFrame* menu_frame_new(ObMenu *menu, ObClient *client)
 {
     ObMenuFrame *self;
@@ -59,17 +78,12 @@ ObMenuFrame* menu_frame_new(ObMenu *menu, ObClient *client)
     self->type = Window_Menu;
     self->menu = menu;
     self->selected = NULL;
-    self->show_title = TRUE;
     self->client = client;
+    self->direction_right = TRUE;
 
     attr.event_mask = FRAME_EVENTMASK;
     self->window = createWindow(RootWindow(ob_display, ob_screen),
-                                   CWEventMask, &attr);
-    attr.event_mask = TITLE_EVENTMASK;
-    self->title = createWindow(self->window, CWEventMask, &attr);
-    self->items = createWindow(self->window, 0, NULL);
-
-    XMapWindow(ob_display, self->items);
+                                CWEventMask, &attr);
 
     self->a_title = RrAppearanceCopy(ob_rr_theme->a_menu_title);
     self->a_items = RrAppearanceCopy(ob_rr_theme->a_menu);
@@ -89,8 +103,6 @@ void menu_frame_free(ObMenuFrame *self)
 
         stacking_remove(MENU_AS_WINDOW(self));
 
-        XDestroyWindow(ob_display, self->items);
-        XDestroyWindow(ob_display, self->title);
         XDestroyWindow(ob_display, self->window);
 
         RrAppearanceFree(self->a_items);
@@ -111,11 +123,17 @@ static ObMenuEntryFrame* menu_entry_frame_new(ObMenuEntry *entry,
     self->frame = frame;
 
     attr.event_mask = ENTRY_EVENTMASK;
-    self->window = createWindow(self->frame->items, CWEventMask, &attr);
+    self->window = createWindow(self->frame->window, CWEventMask, &attr);
     self->text = createWindow(self->window, 0, NULL);
-    if (entry->type != OB_MENU_ENTRY_TYPE_SEPARATOR) {
+    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) {
         self->icon = createWindow(self->window, 0, NULL);
+        g_hash_table_insert(menu_frame_map, &self->icon, self);
+    }
+    if (entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) {
         self->bullet = createWindow(self->window, 0, NULL);
+        g_hash_table_insert(menu_frame_map, &self->bullet, self);
     }
 
     XMapWindow(ob_display, self->window);
@@ -145,6 +163,8 @@ static ObMenuEntryFrame* menu_entry_frame_new(ObMenuEntry *entry,
         RrAppearanceCopy(ob_rr_theme->a_menu_text_disabled);
     self->a_text_selected =
         RrAppearanceCopy(ob_rr_theme->a_menu_text_selected);
+    self->a_text_title =
+        RrAppearanceCopy(ob_rr_theme->a_menu_text_title);
 
     return self;
 }
@@ -154,9 +174,15 @@ static void menu_entry_frame_free(ObMenuEntryFrame *self)
     if (self) {
         XDestroyWindow(ob_display, self->text);
         XDestroyWindow(ob_display, self->window);
-        if (self->entry->type != OB_MENU_ENTRY_TYPE_SEPARATOR) {
+        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);
+            g_hash_table_remove(menu_frame_map, &self->icon);
+        }
+        if (self->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) {
             XDestroyWindow(ob_display, self->bullet);
+            g_hash_table_remove(menu_frame_map, &self->bullet);
         }
 
         RrAppearanceFree(self->a_normal);
@@ -169,6 +195,7 @@ static void menu_entry_frame_free(ObMenuEntryFrame *self)
         RrAppearanceFree(self->a_text_normal);
         RrAppearanceFree(self->a_text_disabled);
         RrAppearanceFree(self->a_text_selected);
+        RrAppearanceFree(self->a_text_title);
         RrAppearanceFree(self->a_bullet_normal);
         RrAppearanceFree(self->a_bullet_selected);
 
@@ -182,46 +209,66 @@ void menu_frame_move(ObMenuFrame *self, gint x, gint y)
     XMoveWindow(ob_display, self->window, self->area.x, self->area.y);
 }
 
-void menu_frame_move_on_screen(ObMenuFrame *self)
+void menu_frame_place_topmenu(ObMenuFrame *self, gint x, gint y)
+{
+    if (self->client && x < 0 && y < 0) {
+        x = self->client->frame->area.x + self->client->frame->size.left;
+        y = self->client->frame->area.y + self->client->frame->size.top;
+    } else {
+        if (config_menu_middle)
+            y -= self->area.height / 2;
+    }
+    menu_frame_move(self, x, y);
+}
+
+void menu_frame_place_submenu(ObMenuFrame *self)
+{
+    gint x, y;
+    gint overlap;
+    gint bwidth;
+
+    overlap = ob_rr_theme->menu_overlap;
+    bwidth = ob_rr_theme->mbwidth;
+
+    if (self->direction_right)
+        x = self->parent->area.x + self->parent->area.width - overlap - bwidth;
+    else
+        x = self->parent->area.x - self->area.width + overlap + bwidth;
+
+    y = self->parent->area.y + self->parent_entry->area.y;
+    if (config_menu_middle)
+        y -= (self->area.height - (bwidth * 2) - self->item_h) / 2;
+    else
+        y += overlap;
+
+    menu_frame_move(self, x, y);
+}
+
+void menu_frame_move_on_screen(ObMenuFrame *self, gint *dx, gint *dy)
 {
     Rect *a = NULL;
-    guint i;
-    gint dx = 0, dy = 0;
     gint pos, half;
 
-    for (i = 0; i < screen_num_monitors; ++i) {
-        a = screen_physical_area_monitor(i);
-        if (RECT_INTERSECTS_RECT(*a, self->area))
-            break;
-    }
-    if (!a) a = screen_physical_area_monitor(0);
+    *dx = *dy = 0;
+
+    a = screen_physical_area_monitor(self->monitor);
 
     half = g_list_length(self->entries) / 2;
     pos = g_list_index(self->entries, self->selected);
 
-    /* if in the bottom half then check this shit first, will keep the bottom
+    /* if in the bottom half then check this stuff first, will keep the bottom
        edge of the menu visible */
     if (pos > half) {
-        dx = MAX(dx, a->x - self->area.x);
-        dy = MAX(dy, a->y - self->area.y);
+        *dx = MAX(*dx, a->x - self->area.x);
+        *dy = MAX(*dy, a->y - self->area.y);
     }
-    dx = MIN(dx, (a->x + a->width) - (self->area.x + self->area.width));
-    dy = MIN(dy, (a->y + a->height) - (self->area.y + self->area.height));
-    /* if in the top half then check this shit last, will keep the top
+    *dx = MIN(*dx, (a->x + a->width) - (self->area.x + self->area.width));
+    *dy = MIN(*dy, (a->y + a->height) - (self->area.y + self->area.height));
+    /* if in the top half then check this stuff last, will keep the top
        edge of the menu visible */
     if (pos <= half) {
-        dx = MAX(dx, a->x - self->area.x);
-        dy = MAX(dy, a->y - self->area.y);
-    }
-
-    if (dx || dy) {
-        ObMenuFrame *f;
-
-        for (f = self; f; f = f->parent)
-            menu_frame_move(f, f->area.x + dx, f->area.y + dy);
-        for (f = self->child; f; f = f->child)
-            menu_frame_move(f, f->area.x + dx, f->area.y + dy);
-        XWarpPointer(ob_display, None, None, 0, 0, 0, 0, dx, dy);
+        *dx = MAX(*dx, a->x - self->area.x);
+        *dy = MAX(*dy, a->y - self->area.y);
     }
 }
 
@@ -232,20 +279,28 @@ static void menu_entry_frame_render(ObMenuEntryFrame *self)
     ObMenu *sub;
     ObMenuFrame *frame = self->frame;
 
-    item_a = ((self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
-               !self->entry->data.normal.enabled) ?
-              self->a_disabled :
-              (self == self->frame->selected ?
-               self->a_selected :
-               self->a_normal));
     switch (self->entry->type) {
     case OB_MENU_ENTRY_TYPE_NORMAL:
     case OB_MENU_ENTRY_TYPE_SUBMENU:
+        item_a = ((self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
+                   !self->entry->data.normal.enabled) ?
+                  self->a_disabled :
+                  (self == self->frame->selected ?
+                   self->a_selected :
+                   self->a_normal));
         th = self->frame->item_h;
         break;
     case OB_MENU_ENTRY_TYPE_SEPARATOR:
-        th = SEPARATOR_HEIGHT + 2*PADDING;
+        if (self->entry->data.separator.label) {
+            item_a = self->frame->a_title;
+            th = ob_rr_theme->menu_title_height;
+        } else {
+            item_a = self->a_normal;
+            th = SEPARATOR_HEIGHT + 2*PADDING;
+        }
         break;
+    default:
+        g_assert_not_reached();
     }
     RECT_SET_SIZE(self->area, self->frame->inner_w, th);
     XResizeWindow(ob_display, self->window,
@@ -255,21 +310,39 @@ static void menu_entry_frame_render(ObMenuEntryFrame *self)
     item_a->surface.parenty = self->area.y;
     RrPaint(item_a, self->window, self->area.width, self->area.height);
 
-    text_a = ((self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
-               !self->entry->data.normal.enabled) ?
-              self->a_text_disabled :
-              (self == self->frame->selected ?
-               self->a_text_selected :
-               self->a_text_normal));
     switch (self->entry->type) {
     case OB_MENU_ENTRY_TYPE_NORMAL:
+        text_a = (!self->entry->data.normal.enabled ?
+                  self->a_text_disabled :
+                  (self == self->frame->selected ?
+                   self->a_text_selected :
+                   self->a_text_normal));
         text_a->texture[0].data.text.string = self->entry->data.normal.label;
+        if (self->frame->menu->show_all_shortcuts ||
+            self->entry->data.normal.shortcut_position > 0)
+        {
+            text_a->texture[0].data.text.shortcut =
+                self->entry->data.normal.shortcut;
+        } else
+            text_a->texture[0].data.text.shortcut = 0;
         break;
     case OB_MENU_ENTRY_TYPE_SUBMENU:
+        text_a = (self == self->frame->selected ?
+                  self->a_text_selected :
+                  self->a_text_normal);
         sub = self->entry->data.submenu.submenu;
         text_a->texture[0].data.text.string = sub ? sub->title : "";
+        if (self->frame->menu->show_all_shortcuts ||
+            sub->shortcut_position > 0) {
+            text_a->texture[0].data.text.shortcut = sub->shortcut;
+        } else
+            text_a->texture[0].data.text.shortcut = 0;
         break;
     case OB_MENU_ENTRY_TYPE_SEPARATOR:
+        if (self->entry->data.separator.label != NULL)
+            text_a = self->a_text_title;
+        else
+            text_a = self->a_text_normal;
         break;
     }
 
@@ -297,24 +370,41 @@ static void menu_entry_frame_render(ObMenuEntryFrame *self)
                 self->frame->item_h - 2*PADDING);
         break;
     case OB_MENU_ENTRY_TYPE_SEPARATOR:
-        XMoveResizeWindow(ob_display, self->text, PADDING, PADDING,
-                          self->area.width - 2*PADDING, SEPARATOR_HEIGHT);
-        self->a_separator->surface.parent = item_a;
-        self->a_separator->surface.parentx = PADDING;
-        self->a_separator->surface.parenty = PADDING;
-        self->a_separator->texture[0].data.lineart.color =
-            text_a->texture[0].data.text.color;
-        self->a_separator->texture[0].data.lineart.x1 = 2*PADDING;
-        self->a_separator->texture[0].data.lineart.y1 = SEPARATOR_HEIGHT / 2;
-        self->a_separator->texture[0].data.lineart.x2 =
-            self->area.width - 4*PADDING;
-        self->a_separator->texture[0].data.lineart.y2 = SEPARATOR_HEIGHT / 2;
-        RrPaint(self->a_separator, self->text,
-                self->area.width - 2*PADDING, SEPARATOR_HEIGHT);
+        if (self->entry->data.separator.label != NULL) {
+            /* labeled separator */
+            XMoveResizeWindow(ob_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 -
+                              2*ob_rr_theme->paddingy);
+            text_a->surface.parent = item_a;
+            text_a->surface.parentx = ob_rr_theme->paddingx;
+            text_a->surface.parenty = ob_rr_theme->paddingy;
+            RrPaint(text_a, self->text,
+                    self->area.width - 2*ob_rr_theme->paddingx,
+                    ob_rr_theme->menu_title_height -
+                    2*ob_rr_theme->paddingy);
+        } else {
+            /* unlabeled separaator */
+            XMoveResizeWindow(ob_display, self->text, PADDING, PADDING,
+                              self->area.width - 2*PADDING, SEPARATOR_HEIGHT);
+            self->a_separator->surface.parent = item_a;
+            self->a_separator->surface.parentx = PADDING;
+            self->a_separator->surface.parenty = PADDING;
+            self->a_separator->texture[0].data.lineart.color =
+                text_a->texture[0].data.text.color;
+            self->a_separator->texture[0].data.lineart.x1 = 2*PADDING;
+            self->a_separator->texture[0].data.lineart.y1 = SEPARATOR_HEIGHT/2;
+            self->a_separator->texture[0].data.lineart.x2 =
+                self->area.width - 4*PADDING;
+            self->a_separator->texture[0].data.lineart.y2 = SEPARATOR_HEIGHT/2;
+            RrPaint(self->a_separator, self->text,
+                    self->area.width - 2*PADDING, SEPARATOR_HEIGHT);
+        }
         break;
     }
 
-    if (self->entry->type != OB_MENU_ENTRY_TYPE_SEPARATOR &&
+    if (self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
         self->entry->data.normal.icon_data)
     {
         XMoveResizeWindow(ob_display, self->icon,
@@ -338,7 +428,7 @@ static void menu_entry_frame_render(ObMenuEntryFrame *self)
                 self->frame->item_h - frame->item_margin.top
                 - frame->item_margin.bottom);
         XMapWindow(ob_display, self->icon);
-    } else if (self->entry->type != OB_MENU_ENTRY_TYPE_SEPARATOR &&
+    } else if (self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
                self->entry->data.normal.mask)
     {
         RrColor *c;
@@ -400,34 +490,17 @@ static void menu_entry_frame_render(ObMenuEntryFrame *self)
 static void menu_frame_render(ObMenuFrame *self)
 {
     gint w = 0, h = 0;
-    gint allitems_h = 0;
     gint tw, th; /* temps */
     GList *it;
     gboolean has_icon = FALSE;
     ObMenu *sub;
+    ObMenuEntryFrame *e;
 
-    XSetWindowBorderWidth(ob_display, self->window, ob_rr_theme->bwidth);
+    XSetWindowBorderWidth(ob_display, self->window, ob_rr_theme->mbwidth);
     XSetWindowBorder(ob_display, self->window,
-                     RrColorPixel(ob_rr_theme->b_color));
+                     RrColorPixel(ob_rr_theme->menu_b_color));
 
-    if (!self->parent && self->show_title) {
-        XMoveWindow(ob_display, self->title, 
-                    -ob_rr_theme->bwidth, h - ob_rr_theme->bwidth);
-
-        self->a_title->texture[0].data.text.string = self->menu->title;
-        RrMinsize(self->a_title, &tw, &th);
-        tw = MIN(tw, MAX_MENU_WIDTH) + ob_rr_theme->padding * 2;
-        w = MAX(w, tw);
-
-        th = ob_rr_theme->menu_title_height;
-        h += (self->title_h = th + ob_rr_theme->bwidth);
-
-        XSetWindowBorderWidth(ob_display, self->title, ob_rr_theme->bwidth);
-        XSetWindowBorder(ob_display, self->title,
-                         RrColorPixel(ob_rr_theme->b_color));
-    }
-
-    XMoveWindow(ob_display, self->items, 0, h);
+    /* find text dimensions */
 
     STRUT_SET(self->item_margin, 0, 0, 0, 0);
 
@@ -462,12 +535,32 @@ static void menu_frame_render(ObMenuFrame *self)
     } else
         self->item_h = 0;
 
+    /* render the entries */
+
     for (it = self->entries; it; it = g_list_next(it)) {
         RrAppearance *text_a;
-        ObMenuEntryFrame *e = it->data;
+        e = it->data;
+
+        /* if the first entry is a labeled separator, then make its border
+           overlap with the menu's outside border */
+        if (it == self->entries &&
+            e->entry->type == OB_MENU_ENTRY_TYPE_SEPARATOR &&
+            e->entry->data.separator.label)
+        {
+            h -= ob_rr_theme->mbwidth;
+        }
 
-        RECT_SET_POINT(e->area, 0, allitems_h);
-        XMoveWindow(ob_display, e->window, 0, e->area.y);
+        if (e->entry->type == OB_MENU_ENTRY_TYPE_SEPARATOR &&
+            e->entry->data.separator.label)
+        {
+            e->border = ob_rr_theme->mbwidth;
+        }
+
+        RECT_SET_POINT(e->area, 0, h+e->border);
+        XMoveWindow(ob_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,
+                         RrColorPixel(ob_rr_theme->menu_b_color));
 
         text_a = ((e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
                    !e->entry->data.normal.enabled) ?
@@ -498,15 +591,33 @@ static void menu_frame_render(ObMenuFrame *self)
             tw += self->item_h - PADDING;
             break;
         case OB_MENU_ENTRY_TYPE_SEPARATOR:
-            tw = 0;
-            th = SEPARATOR_HEIGHT;
+            if (e->entry->data.separator.label != NULL) {
+                e->a_text_title->texture[0].data.text.string =
+                    e->entry->data.separator.label;
+                RrMinsize(e->a_text_title, &tw, &th);
+                tw = MIN(tw, MAX_MENU_WIDTH);
+                th = ob_rr_theme->menu_title_height +
+                    (ob_rr_theme->mbwidth - PADDING) *2;
+            } else {
+                tw = 0;
+                th = SEPARATOR_HEIGHT;
+            }
             break;
         }
         tw += 2*PADDING;
         th += 2*PADDING;
         w = MAX(w, tw);
         h += th;
-        allitems_h += th;
+    }
+
+    /* if the last entry is a labeled separator, then make its border
+       overlap with the menu's outside border */
+    it = g_list_last(self->entries);
+    e = it ? it->data : NULL;
+    if (e && e->entry->type == OB_MENU_ENTRY_TYPE_SEPARATOR &&
+        e->entry->data.separator.label)
+    {
+        h -= ob_rr_theme->mbwidth;
     }
 
     self->text_x = PADDING;
@@ -520,32 +631,19 @@ static void menu_frame_render(ObMenuFrame *self)
     }
 
     if (!w) w = 10;
-    if (!allitems_h) {
-        allitems_h = 3;
-        h += 3;
-    }
+    if (!h) h = 3;
 
     XResizeWindow(ob_display, self->window, w, h);
-    XResizeWindow(ob_display, self->items, w, allitems_h);
 
     self->inner_w = w;
 
-    if (!self->parent && self->show_title) {
-        XResizeWindow(ob_display, self->title,
-                      w, self->title_h - ob_rr_theme->bwidth);
-        RrPaint(self->a_title, self->title,
-                w, self->title_h - ob_rr_theme->bwidth);
-        XMapWindow(ob_display, self->title);
-    } else
-        XUnmapWindow(ob_display, self->title);
-
-    RrPaint(self->a_items, self->items, w, allitems_h);
+    RrPaint(self->a_items, self->window, w, h);
 
     for (it = self->entries; it; it = g_list_next(it))
         menu_entry_frame_render(it->data);
 
-    w += ob_rr_theme->bwidth * 2;
-    h += ob_rr_theme->bwidth * 2;
+    w += ob_rr_theme->mbwidth * 2;
+    h += ob_rr_theme->mbwidth * 2;
 
     RECT_SET_SIZE(self->area, w, h);
 
@@ -584,30 +682,25 @@ static void menu_frame_update(ObMenuFrame *self)
     menu_frame_render(self);
 }
 
-gboolean menu_frame_show(ObMenuFrame *self, ObMenuFrame *parent)
+static gboolean menu_frame_is_visible(ObMenuFrame *self)
 {
-    GList *it;
+    return !!(g_list_find(menu_frame_visible, self));
+}
 
-    if (g_list_find(menu_frame_visible, self))
-        return TRUE;
+static gboolean menu_frame_show(ObMenuFrame *self)
+{
+    GList *it;
 
     if (menu_frame_visible == NULL) {
         /* no menus shown yet */
-        if (!grab_pointer(TRUE, OB_CURSOR_NONE))
+        if (!grab_pointer(TRUE, TRUE, OB_CURSOR_POINTER))
             return FALSE;
         if (!grab_keyboard(TRUE)) {
-            grab_pointer(FALSE, OB_CURSOR_NONE);
+            grab_pointer(FALSE, TRUE, OB_CURSOR_POINTER);
             return FALSE;
         }
     }
 
-    if (parent) {
-        if (parent->child)
-            menu_frame_hide(parent->child);
-        parent->child = self;
-    }
-    self->parent = parent;
-
     /* determine if the underlying menu is already visible */
     for (it = menu_frame_visible; it; it = g_list_next(it)) {
         ObMenuFrame *f = it->data;
@@ -623,13 +716,92 @@ gboolean menu_frame_show(ObMenuFrame *self, ObMenuFrame *parent)
 
     menu_frame_visible = g_list_prepend(menu_frame_visible, self);
 
-    menu_frame_move_on_screen(self);
+    return TRUE;
+}
+
+gboolean menu_frame_show_topmenu(ObMenuFrame *self, gint x, gint y)
+{
+    gint dx, dy;
+    guint i;
+
+    if (menu_frame_is_visible(self))
+        return TRUE;
+    if (!menu_frame_show(self))
+        return FALSE;
+
+    menu_frame_place_topmenu(self, x, y);
+
+    /* find the monitor the menu is on */
+    for (i = 0; i < screen_num_monitors; ++i) {
+        Rect *a = screen_physical_area_monitor(i);
+        if (RECT_CONTAINS(*a, x, y)) {
+            self->monitor = i;
+            break;
+        }
+    }
+
+    menu_frame_move_on_screen(self, &dx, &dy);
+    menu_frame_move(self, self->area.x + dx, self->area.y + dy);
 
     XMapWindow(ob_display, self->window);
 
     return TRUE;
 }
 
+gboolean menu_frame_show_submenu(ObMenuFrame *self, ObMenuFrame *parent,
+                                 ObMenuEntryFrame *parent_entry)
+{
+    ObMenuEntryFrame *e;
+    gint dx, dy;
+
+    if (menu_frame_is_visible(self))
+        return TRUE;
+
+    self->monitor = parent->monitor;
+    self->parent = parent;
+    self->parent_entry = parent_entry;
+
+    /* set up parent's child to be us */
+    if (parent->child)
+        menu_frame_hide(parent->child);
+    parent->child = self;
+
+    if (!menu_frame_show(self))
+        return FALSE;
+
+    menu_frame_place_submenu(self);
+    menu_frame_move_on_screen(self, &dx, &dy);
+
+    if (dx == 0) {
+        menu_frame_move(self, self->area.x, self->area.y + dy);
+    } else {
+        gboolean dir;
+
+        /* flip the direction in which we're placing submenus */
+        if (dx > 0)
+            dir = TRUE;
+        else
+            dir = FALSE;
+
+        /* if it changed, then replace the menu on the opposite side,
+           and try keep it on the screen too */
+        if (dir != self->direction_right) {
+            self->direction_right = dir;
+            menu_frame_place_submenu(self);
+            menu_frame_move_on_screen(self, &dx, &dy);
+            menu_frame_move(self, self->area.x + dx, self->area.y + dy);
+        }
+    }
+
+    XMapWindow(ob_display, self->window);
+
+    if (screen_pointer_pos(&dx, &dy) && (e = menu_entry_frame_under(dx, dy)) &&
+        e->frame == self)
+        ++e->ignore_enters;
+
+    return TRUE;
+}
+
 void menu_frame_hide(ObMenuFrame *self)
 {
     GList *it = g_list_find(menu_frame_visible, self);
@@ -643,12 +815,13 @@ void menu_frame_hide(ObMenuFrame *self)
     if (self->parent)
         self->parent->child = NULL;
     self->parent = NULL;
+    self->parent_entry = NULL;
 
     menu_frame_visible = g_list_delete_link(menu_frame_visible, it);
 
     if (menu_frame_visible == NULL) {
         /* last menu shown */
-        grab_pointer(FALSE, OB_CURSOR_NONE);
+        grab_pointer(FALSE, TRUE, OB_CURSOR_NONE);
         grab_keyboard(FALSE);
     }
 
@@ -659,8 +832,14 @@ void menu_frame_hide(ObMenuFrame *self)
 
 void menu_frame_hide_all()
 {
-    GList *it = g_list_last(menu_frame_visible);
-    if (it) 
+    GList *it;
+
+    if (config_submenu_show_delay) {
+        /* remove any submenu open requests */
+        ob_main_loop_timeout_remove(ob_main_loop,
+                                    menu_entry_frame_submenu_timeout);
+    }
+    if ((it = g_list_last(menu_frame_visible)))
         menu_frame_hide(it->data);
 }
 
@@ -698,8 +877,8 @@ ObMenuEntryFrame* menu_entry_frame_under(gint x, gint y)
     GList *it;
 
     if ((frame = menu_frame_under(x, y))) {
-        x -= ob_rr_theme->bwidth + frame->area.x;
-        y -= frame->title_h + ob_rr_theme->bwidth + frame->area.y;
+        x -= ob_rr_theme->mbwidth + frame->area.x;
+        y -= ob_rr_theme->mbwidth + frame->area.y;
 
         for (it = frame->entries; it; it = g_list_next(it)) {
             ObMenuEntryFrame *e = it->data;
@@ -713,7 +892,14 @@ ObMenuEntryFrame* menu_entry_frame_under(gint x, gint y)
     return ret;
 }
 
-void menu_frame_select(ObMenuFrame *self, ObMenuEntryFrame *entry)
+static gboolean menu_entry_frame_submenu_timeout(gpointer data)
+{
+    menu_entry_frame_show_submenu((ObMenuEntryFrame*)data);
+    return FALSE;
+}
+
+void menu_frame_select(ObMenuFrame *self, ObMenuEntryFrame *entry,
+                       gboolean immediate)
 {
     ObMenuEntryFrame *old = self->selected;
     ObMenuFrame *oldchild = self->child;
@@ -722,6 +908,12 @@ void menu_frame_select(ObMenuFrame *self, ObMenuEntryFrame *entry)
         entry = old;
 
     if (old == entry) return;
+   
+    if (config_submenu_show_delay) { 
+        /* remove any submenu open requests */
+        ob_main_loop_timeout_remove(ob_main_loop,
+                                    menu_entry_frame_submenu_timeout);
+    }
 
     self->selected = entry;
 
@@ -733,8 +925,18 @@ void menu_frame_select(ObMenuFrame *self, ObMenuEntryFrame *entry)
     if (self->selected) {
         menu_entry_frame_render(self->selected);
 
-        if (self->selected->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)
-            menu_entry_frame_show_submenu(self->selected);
+        if (self->selected->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) {
+            if (config_submenu_show_delay && !immediate) {
+                /* initiate a new submenu open request */
+                ob_main_loop_timeout_add(ob_main_loop,
+                                         config_submenu_show_delay * 1000,
+                                         menu_entry_frame_submenu_timeout,
+                                         self->selected, g_direct_equal,
+                                         NULL);
+            } else {
+                menu_entry_frame_show_submenu(self->selected);
+            }
+        }
     }
 }
 
@@ -746,15 +948,13 @@ void menu_entry_frame_show_submenu(ObMenuEntryFrame *self)
 
     f = menu_frame_new(self->entry->data.submenu.submenu,
                        self->frame->client);
-    menu_frame_move(f,
-                    self->frame->area.x + self->frame->area.width
-                    - ob_rr_theme->menu_overlap - ob_rr_theme->bwidth,
-                    self->frame->area.y + self->frame->title_h +
-                    self->area.y + ob_rr_theme->menu_overlap);
-    menu_frame_show(f, self->frame);
+    /* pass our direction on to our child */
+    f->direction_right = self->frame->direction_right;
+
+    menu_frame_show_submenu(f, self->frame, self);
 }
 
-void menu_entry_frame_execute(ObMenuEntryFrame *self, guint state)
+void menu_entry_frame_execute(ObMenuEntryFrame *self, guint state, Time time)
 {
     if (self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
         self->entry->data.normal.enabled)
@@ -772,9 +972,9 @@ void menu_entry_frame_execute(ObMenuEntryFrame *self, guint state)
             menu_frame_hide_all();
 
         if (func)
-            func(entry, state, data);
+            func(entry, state, data, time);
         else
-            action_run(acts, client, state);
+            action_run(acts, client, state, time);
     }
 }
 
@@ -801,7 +1001,7 @@ void menu_frame_select_previous(ObMenuFrame *self)
             }
         }
     }
-    menu_frame_select(self, it ? it->data : NULL);
+    menu_frame_select(self, it ? it->data : NULL, TRUE);
 }
 
 void menu_frame_select_next(ObMenuFrame *self)
@@ -827,5 +1027,5 @@ void menu_frame_select_next(ObMenuFrame *self)
             }
         }
     }
-    menu_frame_select(self, it ? it->data : NULL);
+    menu_frame_select(self, it ? it->data : NULL, TRUE);
 }
This page took 0.04141 seconds and 4 git commands to generate.